diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 7a46472c62..2e61356b3a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -825,6 +825,18 @@ pub const Object = struct { /// Memoizes a null `?usize` value. null_opt_usize: Builder.Constant, + /// When an LLVM struct type is created, an entry is inserted into this + /// table for every zig source field of the struct that has a corresponding + /// LLVM struct field. comptime fields and 0 bit fields are not included. + /// The value is the LLVM struct field index. + /// This is denormalized data. + struct_field_map: std.AutoHashMapUnmanaged(ZigStructField, c_uint), + + const ZigStructField = struct { + struct_ty: InternPool.Index, + field_index: u32, + }; + pub const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type); /// This is an ArrayHashMap as opposed to a HashMap because in `flushModule` we @@ -979,6 +991,7 @@ pub const Object = struct { .error_name_table = .none, .extern_collisions = .{}, .null_opt_usize = .no_init, + .struct_field_map = .{}, }; } @@ -994,6 +1007,7 @@ pub const Object = struct { self.type_map.deinit(gpa); self.extern_collisions.deinit(gpa); self.builder.deinit(); + self.struct_field_map.deinit(gpa); self.* = undefined; } @@ -3274,7 +3288,10 @@ pub const Object = struct { var llvm_field_types = std.ArrayListUnmanaged(Builder.Type){}; defer llvm_field_types.deinit(o.gpa); + // Although we can estimate how much capacity to add, these cannot be + // relied upon because of the recursive calls to lowerType below. try llvm_field_types.ensureUnusedCapacity(o.gpa, struct_obj.fields.count()); + try o.struct_field_map.ensureUnusedCapacity(o.gpa, @intCast(struct_obj.fields.count())); comptime assert(struct_layout_version == 2); var offset: u64 = 0; @@ -3296,6 +3313,10 @@ pub const Object = struct { o.gpa, try o.builder.arrayType(padding_len, .i8), ); + try o.struct_field_map.put(o.gpa, .{ + .struct_ty = t.toIntern(), + .field_index = field_and_index.index, + }, @intCast(llvm_field_types.items.len)); try llvm_field_types.append(o.gpa, try o.lowerType(field.ty)); offset += field.ty.abiSize(mod); @@ -3319,7 +3340,10 @@ pub const Object = struct { .anon_struct_type => |anon_struct_type| { var llvm_field_types: std.ArrayListUnmanaged(Builder.Type) = .{}; defer llvm_field_types.deinit(o.gpa); + // Although we can estimate how much capacity to add, these cannot be + // relied upon because of the recursive calls to lowerType below. try llvm_field_types.ensureUnusedCapacity(o.gpa, anon_struct_type.types.len); + try o.struct_field_map.ensureUnusedCapacity(o.gpa, anon_struct_type.types.len); comptime assert(struct_layout_version == 2); var offset: u64 = 0; @@ -3328,7 +3352,8 @@ pub const Object = struct { for ( anon_struct_type.types.get(ip), anon_struct_type.values.get(ip), - ) |field_ty, field_val| { + 0.., + ) |field_ty, field_val, field_index| { if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue; const field_align = field_ty.toType().abiAlignment(mod); @@ -3341,6 +3366,10 @@ pub const Object = struct { o.gpa, try o.builder.arrayType(padding_len, .i8), ); + try o.struct_field_map.put(o.gpa, .{ + .struct_ty = t.toIntern(), + .field_index = @intCast(field_index), + }, @intCast(llvm_field_types.items.len)); try llvm_field_types.append(o.gpa, try o.lowerType(field_ty.toType())); offset += field_ty.toType().abiSize(mod); @@ -4237,9 +4266,9 @@ pub const Object = struct { try o.lowerType(parent_ty), parent_ptr, null, - if (llvmField(parent_ty, field_index, mod)) |llvm_field| &.{ + if (o.llvmFieldIndex(parent_ty, field_index)) |llvm_field_index| &.{ try o.builder.intConst(.i32, 0), - try o.builder.intConst(.i32, llvm_field.index), + try o.builder.intConst(.i32, llvm_field_index), } else &.{ try o.builder.intConst(.i32, @intFromBool( parent_ty.hasRuntimeBitsIgnoreComptime(mod), @@ -4396,6 +4425,13 @@ pub const Object = struct { try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = alignment }, &o.builder); if (byval) try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder); } + + fn llvmFieldIndex(o: *Object, struct_ty: Type, field_index: usize) ?c_uint { + return o.struct_field_map.get(.{ + .struct_ty = struct_ty.toIntern(), + .field_index = @intCast(field_index), + }); + } }; pub const DeclGen = struct { @@ -6142,7 +6178,7 @@ pub const FuncGen = struct { return self.wip.cast(.trunc, shifted_value, elem_llvm_ty, ""); }, else => { - const llvm_field_index = llvmField(struct_ty, field_index, mod).?.index; + const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?; return self.wip.extractValue(struct_llvm_val, &.{llvm_field_index}, ""); }, }, @@ -6169,23 +6205,25 @@ pub const FuncGen = struct { switch (struct_ty.zigTypeTag(mod)) { .Struct => { - assert(struct_ty.containerLayout(mod) != .Packed); - const llvm_field = llvmField(struct_ty, field_index, mod).?; + const layout = struct_ty.containerLayout(mod); + assert(layout != .Packed); const struct_llvm_ty = try o.lowerType(struct_ty); + const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?; const field_ptr = - try self.wip.gepStruct(struct_llvm_ty, struct_llvm_val, llvm_field.index, ""); + try self.wip.gepStruct(struct_llvm_ty, struct_llvm_val, llvm_field_index, ""); + const alignment = struct_ty.structFieldAlign(field_index, mod); const field_ptr_ty = try mod.ptrType(.{ - .child = llvm_field.ty.toIntern(), + .child = field_ty.toIntern(), .flags = .{ - .alignment = InternPool.Alignment.fromNonzeroByteUnits(llvm_field.alignment), + .alignment = InternPool.Alignment.fromNonzeroByteUnits(alignment), }, }); if (isByRef(field_ty, mod)) { if (canElideLoad(self, body_tail)) return field_ptr; - assert(llvm_field.alignment != 0); - const field_alignment = Builder.Alignment.fromByteUnits(llvm_field.alignment); + assert(alignment != 0); + const field_alignment = Builder.Alignment.fromByteUnits(alignment); return self.loadByRef(field_ptr, field_ty, field_alignment, .normal); } else { return self.load(field_ptr, field_ptr_ty); @@ -7080,15 +7118,17 @@ pub const FuncGen = struct { const field_index = ty_pl.payload; const mod = o.module; - const llvm_field = llvmField(struct_ty, field_index, mod).?; const struct_llvm_ty = try o.lowerType(struct_ty); + const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?; assert(self.err_ret_trace != .none); const field_ptr = - try self.wip.gepStruct(struct_llvm_ty, self.err_ret_trace, llvm_field.index, ""); + try self.wip.gepStruct(struct_llvm_ty, self.err_ret_trace, llvm_field_index, ""); + const field_alignment = struct_ty.structFieldAlign(field_index, mod); + const field_ty = struct_ty.structFieldType(field_index, mod); const field_ptr_ty = try mod.ptrType(.{ - .child = llvm_field.ty.toIntern(), + .child = field_ty.toIntern(), .flags = .{ - .alignment = InternPool.Alignment.fromNonzeroByteUnits(llvm_field.alignment), + .alignment = InternPool.Alignment.fromNonzeroByteUnits(field_alignment), }, }); return self.load(field_ptr, field_ptr_ty); @@ -7640,8 +7680,8 @@ pub const FuncGen = struct { const result_val = try self.wip.extractValue(results, &.{0}, ""); const overflow_bit = try self.wip.extractValue(results, &.{1}, ""); - const result_index = llvmField(inst_ty, 0, mod).?.index; - const overflow_index = llvmField(inst_ty, 1, mod).?.index; + const result_index = o.llvmFieldIndex(inst_ty, 0).?; + const overflow_index = o.llvmFieldIndex(inst_ty, 1).?; if (isByRef(inst_ty, mod)) { const result_alignment = Builder.Alignment.fromByteUnits(inst_ty.abiAlignment(mod)); @@ -7998,8 +8038,8 @@ pub const FuncGen = struct { const overflow_bit = try self.wip.icmp(.ne, lhs, reconstructed, ""); - const result_index = llvmField(dest_ty, 0, mod).?.index; - const overflow_index = llvmField(dest_ty, 1, mod).?.index; + const result_index = o.llvmFieldIndex(dest_ty, 0).?; + const overflow_index = o.llvmFieldIndex(dest_ty, 1).?; if (isByRef(dest_ty, mod)) { const result_alignment = Builder.Alignment.fromByteUnits(dest_ty.abiAlignment(mod)); @@ -9616,7 +9656,7 @@ pub const FuncGen = struct { if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue; const llvm_elem = try self.resolveInst(elem); - const llvm_i = llvmField(result_ty, i, mod).?.index; + const llvm_i = o.llvmFieldIndex(result_ty, i).?; const field_ptr = try self.wip.gepStruct(llvm_result_ty, alloca_inst, llvm_i, ""); const field_ptr_ty = try mod.ptrType(.{ @@ -9637,7 +9677,7 @@ pub const FuncGen = struct { if ((try result_ty.structFieldValueComptime(mod, i)) != null) continue; const llvm_elem = try self.resolveInst(elem); - const llvm_i = llvmField(result_ty, i, mod).?.index; + const llvm_i = o.llvmFieldIndex(result_ty, i).?; result = try self.wip.insertValue(result, llvm_elem, &.{llvm_i}, ""); } return result; @@ -10059,8 +10099,8 @@ pub const FuncGen = struct { else => { const struct_llvm_ty = try o.lowerPtrElemTy(struct_ty); - if (llvmField(struct_ty, field_index, mod)) |llvm_field| { - return self.wip.gepStruct(struct_llvm_ty, struct_ptr, llvm_field.index, ""); + if (o.llvmFieldIndex(struct_ty, field_index)) |llvm_field_index| { + return self.wip.gepStruct(struct_llvm_ty, struct_ptr, llvm_field_index, ""); } else { // If we found no index then this means this is a zero sized field at the // end of the struct. Treat our struct pointer as an array of two and get @@ -10527,90 +10567,6 @@ fn toLlvmGlobalAddressSpace(wanted_address_space: std.builtin.AddressSpace, targ }; } -const LlvmField = struct { - index: c_uint, - ty: Type, - alignment: u32, -}; - -/// Take into account 0 bit fields and padding. Returns null if an llvm -/// field could not be found. -/// This only happens if you want the field index of a zero sized field at -/// the end of the struct. -fn llvmField(ty: Type, field_index: usize, mod: *Module) ?LlvmField { - // Detects where we inserted extra padding fields so that we can skip - // over them in this function. - comptime assert(struct_layout_version == 2); - var offset: u64 = 0; - var big_align: u32 = 0; - - const ip = &mod.intern_pool; - const struct_type = switch (ip.indexToKey(ty.toIntern())) { - .anon_struct_type => |tuple| { - var llvm_field_index: c_uint = 0; - for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| { - if (field_val != .none or !field_ty.toType().hasRuntimeBits(mod)) continue; - - const field_align = field_ty.toType().abiAlignment(mod); - big_align = @max(big_align, field_align); - const prev_offset = offset; - offset = std.mem.alignForward(u64, offset, field_align); - - const padding_len = offset - prev_offset; - if (padding_len > 0) { - llvm_field_index += 1; - } - - if (field_index <= i) { - return .{ - .index = llvm_field_index, - .ty = field_ty.toType(), - .alignment = field_align, - }; - } - - llvm_field_index += 1; - offset += field_ty.toType().abiSize(mod); - } - return null; - }, - .struct_type => |s| s, - else => unreachable, - }; - const struct_obj = mod.structPtrUnwrap(struct_type.index).?; - const layout = struct_obj.layout; - assert(layout != .Packed); - - var llvm_field_index: c_uint = 0; - var it = struct_obj.runtimeFieldIterator(mod); - while (it.next()) |field_and_index| { - const field = field_and_index.field; - const field_align = field.alignment(mod, layout); - big_align = @max(big_align, field_align); - const prev_offset = offset; - offset = std.mem.alignForward(u64, offset, field_align); - - const padding_len = offset - prev_offset; - if (padding_len > 0) { - llvm_field_index += 1; - } - - if (field_index == field_and_index.index) { - return .{ - .index = llvm_field_index, - .ty = field.ty, - .alignment = field_align, - }; - } - - llvm_field_index += 1; - offset += field.ty.abiSize(mod); - } else { - // We did not find an llvm field that corresponds to this zig field. - return null; - } -} - fn firstParamSRet(fn_info: InternPool.Key.FuncType, mod: *Module) bool { const return_type = fn_info.return_type.toType(); if (!return_type.hasRuntimeBitsIgnoreComptime(mod)) return false;