authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-01 18:24:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-01 18:24:00-07:00
log6f303c01f3e06fe8203563065ea32537f6eff456
treec1e85221ff40bfd5be043a6f7429e2b3e4d94024
parent8878f085dccaf9efe89a04b458205fddc215e095

LLVM: add extra padding to structs and tuples sometimes

* Sema: resolve type fully when emitting an alloc AIR instruction to avoid tripping assertion for checking struct field alignment. * LLVM backend: keep a reference to the LLVM target data alive during lowering so that we can ask LLVM what it thinks the ABI alignment and size of LLVM types are. We need this in order to lower tuples and structs so that we can put in extra padding bytes when Zig disagrees with LLVM about the size or alignment of something. * LLVM backend: make the LLVM struct type packed that contains the most aligned union field and the padding. This prevents the struct from being too big according to LLVM. In the future, we may want to consider instead emitting unions in a "flat" manner; putting the tag, most aligned union field, and padding all in the same struct field space. * LLVM backend: make structs with 2 or fewer fields return isByRef=false. This results in more efficient codegen. This required lowering of bitcast to sometimes store the struct into an alloca, ptrcast, and then load because LLVM does not allow bitcasting structs. * enable more passing behavior tests.

15 files changed, 327 insertions(+), 133 deletions(-)

src/Sema.zig+2-2
......@@ -2439,7 +2439,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
24392439 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
24402440 });
24412441 try sema.requireRuntimeBlock(block, var_decl_src);
2442 try sema.resolveTypeLayout(block, ty_src, var_ty);
2442 try sema.resolveTypeFully(block, ty_src, var_ty);
24432443 return block.addTy(.alloc, ptr_type);
24442444}
24452445
......@@ -2461,7 +2461,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
24612461 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
24622462 });
24632463 try sema.requireRuntimeBlock(block, var_decl_src);
2464 try sema.resolveTypeLayout(block, ty_src, var_ty);
2464 try sema.resolveTypeFully(block, ty_src, var_ty);
24652465 return block.addTy(.alloc, ptr_type);
24662466}
24672467
src/codegen/llvm.zig+278-99
......@@ -160,6 +160,7 @@ pub const Object = struct {
160160 llvm_module: *const llvm.Module,
161161 context: *const llvm.Context,
162162 target_machine: *const llvm.TargetMachine,
163 target_data: *const llvm.TargetData,
163164 /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function,
164165 /// but that has some downsides:
165166 /// * we have to compute the fully qualified name every time we want to do the lookup
......@@ -258,7 +259,7 @@ pub const Object = struct {
258259 errdefer target_machine.dispose();
259260
260261 const target_data = target_machine.createTargetDataLayout();
261 defer target_data.dispose();
262 errdefer target_data.dispose();
262263
263264 llvm_module.setModuleDataLayout(target_data);
264265
......@@ -266,6 +267,7 @@ pub const Object = struct {
266267 .llvm_module = llvm_module,
267268 .context = context,
268269 .target_machine = target_machine,
270 .target_data = target_data,
269271 .decl_map = .{},
270272 .type_map = .{},
271273 .type_map_arena = std.heap.ArenaAllocator.init(gpa),
......@@ -274,6 +276,7 @@ pub const Object = struct {
274276 }
275277
276278 pub fn deinit(self: *Object, gpa: Allocator) void {
279 self.target_data.dispose();
277280 self.target_machine.dispose();
278281 self.llvm_module.dispose();
279282 self.context.dispose();
......@@ -955,20 +958,55 @@ pub const DeclGen = struct {
955958 // reference, we need to copy it here.
956959 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
957960
958 if (t.castTag(.tuple)) |tuple| {
961 if (t.isTuple()) {
962 const tuple = t.tupleFields();
959963 const llvm_struct_ty = dg.context.structCreateNamed("");
960964 gop.value_ptr.* = llvm_struct_ty; // must be done before any recursive calls
961965
962 const types = tuple.data.types;
963 const values = tuple.data.values;
964 var llvm_field_types = try std.ArrayListUnmanaged(*const llvm.Type).initCapacity(gpa, types.len);
966 var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{};
965967 defer llvm_field_types.deinit(gpa);
966968
967 for (types) |field_ty, i| {
968 const field_val = values[i];
969 try llvm_field_types.ensureUnusedCapacity(gpa, tuple.types.len);
970
971 // We need to insert extra padding if LLVM's isn't enough.
972 var zig_offset: u64 = 0;
973 var llvm_offset: u64 = 0;
974 var zig_big_align: u32 = 0;
975 var llvm_big_align: u32 = 0;
976
977 for (tuple.types) |field_ty, i| {
978 const field_val = tuple.values[i];
969979 if (field_val.tag() != .unreachable_value) continue;
970980
971 llvm_field_types.appendAssumeCapacity(try dg.llvmType(field_ty));
981 const field_align = field_ty.abiAlignment(target);
982 zig_big_align = @maximum(zig_big_align, field_align);
983 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align);
984
985 const field_llvm_ty = try dg.llvmType(field_ty);
986 const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty);
987 llvm_big_align = @maximum(llvm_big_align, field_llvm_align);
988 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align);
989
990 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
991 if (padding_len > 0) {
992 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);
993 try llvm_field_types.append(gpa, llvm_array_ty);
994 llvm_offset = zig_offset;
995 }
996 try llvm_field_types.append(gpa, field_llvm_ty);
997
998 llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty);
999 zig_offset += field_ty.abiSize(target);
1000 }
1001 {
1002 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, zig_big_align);
1003 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, llvm_big_align);
1004 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
1005 if (padding_len > 0) {
1006 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);
1007 try llvm_field_types.append(gpa, llvm_array_ty);
1008 llvm_offset = zig_offset;
1009 }
9721010 }
9731011
9741012 llvm_struct_ty.structSetBody(
......@@ -998,12 +1036,49 @@ pub const DeclGen = struct {
9981036
9991037 assert(struct_obj.haveFieldTypes());
10001038
1001 var llvm_field_types = try std.ArrayListUnmanaged(*const llvm.Type).initCapacity(gpa, struct_obj.fields.count());
1039 var llvm_field_types: std.ArrayListUnmanaged(*const llvm.Type) = .{};
10021040 defer llvm_field_types.deinit(gpa);
10031041
1042 try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count());
1043
1044 // We need to insert extra padding if LLVM's isn't enough.
1045 var zig_offset: u64 = 0;
1046 var llvm_offset: u64 = 0;
1047 var zig_big_align: u32 = 0;
1048 var llvm_big_align: u32 = 0;
1049
10041050 for (struct_obj.fields.values()) |field| {
1005 if (!field.ty.hasRuntimeBits()) continue;
1006 llvm_field_types.appendAssumeCapacity(try dg.llvmType(field.ty));
1051 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
1052
1053 const field_align = field.normalAlignment(target);
1054 zig_big_align = @maximum(zig_big_align, field_align);
1055 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align);
1056
1057 const field_llvm_ty = try dg.llvmType(field.ty);
1058 const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty);
1059 llvm_big_align = @maximum(llvm_big_align, field_llvm_align);
1060 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align);
1061
1062 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
1063 if (padding_len > 0) {
1064 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);
1065 try llvm_field_types.append(gpa, llvm_array_ty);
1066 llvm_offset = zig_offset;
1067 }
1068 try llvm_field_types.append(gpa, field_llvm_ty);
1069
1070 llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty);
1071 zig_offset += field.ty.abiSize(target);
1072 }
1073 {
1074 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, zig_big_align);
1075 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, llvm_big_align);
1076 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
1077 if (padding_len > 0) {
1078 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);
1079 try llvm_field_types.append(gpa, llvm_array_ty);
1080 llvm_offset = zig_offset;
1081 }
10071082 }
10081083
10091084 llvm_struct_ty.structSetBody(
......@@ -1050,7 +1125,7 @@ pub const DeclGen = struct {
10501125 llvm_aligned_field_ty,
10511126 dg.context.intType(8).arrayType(padding_len),
10521127 };
1053 break :t dg.context.structType(&fields, fields.len, .False);
1128 break :t dg.context.structType(&fields, fields.len, .True);
10541129 };
10551130
10561131 if (layout.tag_size == 0) {
......@@ -1461,9 +1536,9 @@ pub const DeclGen = struct {
14611536 const field_vals = tv.val.castTag(.@"struct").?.data;
14621537 const gpa = dg.gpa;
14631538 const struct_obj = tv.ty.castTag(.@"struct").?.data;
1539 const target = dg.module.getTarget();
14641540
14651541 if (struct_obj.layout == .Packed) {
1466 const target = dg.module.getTarget();
14671542 const big_bits = struct_obj.packedIntegerBits(target);
14681543 const int_llvm_ty = dg.context.intType(big_bits);
14691544 const fields = struct_obj.fields.values();
......@@ -1497,19 +1572,56 @@ pub const DeclGen = struct {
14971572 var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count);
14981573 defer llvm_fields.deinit(gpa);
14991574
1575 // These are used to detect where the extra padding fields are so that we
1576 // can initialize them with undefined.
1577 var zig_offset: u64 = 0;
1578 var llvm_offset: u64 = 0;
1579 var zig_big_align: u32 = 0;
1580 var llvm_big_align: u32 = 0;
1581
15001582 var need_unnamed = false;
1501 for (field_vals) |field_val, i| {
1502 const field_ty = tv.ty.structFieldType(i);
1503 if (!field_ty.hasRuntimeBits()) continue;
1583 for (struct_obj.fields.values()) |field, i| {
1584 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
1585
1586 const field_align = field.normalAlignment(target);
1587 zig_big_align = @maximum(zig_big_align, field_align);
1588 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align);
1589
1590 const field_llvm_ty = try dg.llvmType(field.ty);
1591 const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty);
1592 llvm_big_align = @maximum(llvm_big_align, field_llvm_align);
1593 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align);
1594
1595 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
1596 if (padding_len > 0) {
1597 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);
1598 // TODO make this and all other padding elsewhere in debug
1599 // builds be 0xaa not undef.
1600 llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef());
1601 llvm_offset = zig_offset;
1602 }
15041603
15051604 const field_llvm_val = try dg.genTypedValue(.{
1506 .ty = field_ty,
1507 .val = field_val,
1605 .ty = field.ty,
1606 .val = field_vals[i],
15081607 });
15091608
1510 need_unnamed = need_unnamed or dg.isUnnamedType(field_ty, field_llvm_val);
1609 need_unnamed = need_unnamed or dg.isUnnamedType(field.ty, field_llvm_val);
15111610
15121611 llvm_fields.appendAssumeCapacity(field_llvm_val);
1612
1613 llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty);
1614 zig_offset += field.ty.abiSize(target);
1615 }
1616 {
1617 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, zig_big_align);
1618 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, llvm_big_align);
1619 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
1620 if (padding_len > 0) {
1621 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);
1622 llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef());
1623 llvm_offset = zig_offset;
1624 }
15131625 }
15141626
15151627 if (need_unnamed) {
......@@ -1556,7 +1668,7 @@ pub const DeclGen = struct {
15561668 const fields: [2]*const llvm.Value = .{
15571669 field, dg.context.intType(8).arrayType(padding_len).getUndef(),
15581670 };
1559 break :p dg.context.constStruct(&fields, fields.len, .False);
1671 break :p dg.context.constStruct(&fields, fields.len, .True);
15601672 };
15611673
15621674 // In this case we must make an unnamed struct because LLVM does
......@@ -1741,7 +1853,7 @@ pub const DeclGen = struct {
17411853 },
17421854 .Struct => {
17431855 var ty_buf: Type.Payload.Pointer = undefined;
1744 const llvm_field_index = llvmFieldIndex(parent.ty, field_index, target, &ty_buf).?;
1856 const llvm_field_index = dg.llvmFieldIndex(parent.ty, field_index, &ty_buf).?;
17451857 const indices: [2]*const llvm.Value = .{
17461858 llvm_u32.constInt(0, .False),
17471859 llvm_u32.constInt(llvm_field_index, .False),
......@@ -1972,6 +2084,107 @@ pub const DeclGen = struct {
19722084 return null;
19732085 }
19742086 }
2087
2088 /// Take into account 0 bit fields and padding. Returns null if an llvm
2089 /// field could not be found.
2090 /// This only happens if you want the field index of a zero sized field at
2091 /// the end of the struct.
2092 fn llvmFieldIndex(
2093 dg: *DeclGen,
2094 ty: Type,
2095 field_index: u32,
2096 ptr_pl_buf: *Type.Payload.Pointer,
2097 ) ?c_uint {
2098 const target = dg.module.getTarget();
2099
2100 // Detects where we inserted extra padding fields so that we can skip
2101 // over them in this function.
2102 var zig_offset: u64 = 0;
2103 var llvm_offset: u64 = 0;
2104 var zig_big_align: u32 = 0;
2105 var llvm_big_align: u32 = 0;
2106
2107 if (ty.isTuple()) {
2108 const tuple = ty.tupleFields();
2109 var llvm_field_index: c_uint = 0;
2110 for (tuple.types) |field_ty, i| {
2111 if (tuple.values[i].tag() != .unreachable_value) continue;
2112
2113 const field_align = field_ty.abiAlignment(target);
2114 zig_big_align = @maximum(zig_big_align, field_align);
2115 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align);
2116
2117 // assert no error because we have already seen a successful
2118 // llvmType on this field.
2119 const field_llvm_ty = dg.llvmType(field_ty) catch unreachable;
2120 const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty);
2121 llvm_big_align = @maximum(llvm_big_align, field_llvm_align);
2122 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align);
2123
2124 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
2125 if (padding_len > 0) {
2126 llvm_field_index += 1;
2127 llvm_offset = zig_offset;
2128 }
2129
2130 if (field_index == i) {
2131 ptr_pl_buf.* = .{
2132 .data = .{
2133 .pointee_type = field_ty,
2134 .@"align" = field_align,
2135 .@"addrspace" = .generic,
2136 },
2137 };
2138 return llvm_field_index;
2139 }
2140
2141 llvm_field_index += 1;
2142 llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty);
2143 zig_offset += field_ty.abiSize(target);
2144 }
2145 return null;
2146 }
2147 assert(ty.containerLayout() != .Packed);
2148
2149 var llvm_field_index: c_uint = 0;
2150 for (ty.structFields().values()) |field, i| {
2151 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
2152
2153 const field_align = field.normalAlignment(target);
2154 zig_big_align = @maximum(zig_big_align, field_align);
2155 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align);
2156
2157 // assert no error because we have already seen a successful llvmType on this field.
2158 const field_llvm_ty = dg.llvmType(field.ty) catch unreachable;
2159 const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty);
2160 llvm_big_align = @maximum(llvm_big_align, field_llvm_align);
2161 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align);
2162
2163 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
2164 if (padding_len > 0) {
2165 llvm_field_index += 1;
2166 llvm_offset = zig_offset;
2167 }
2168
2169 if (field_index == i) {
2170 ptr_pl_buf.* = .{
2171 .data = .{
2172 .pointee_type = field.ty,
2173 .@"align" = field_align,
2174 .@"addrspace" = .generic,
2175 },
2176 };
2177 return llvm_field_index;
2178 }
2179
2180 llvm_field_index += 1;
2181 llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty);
2182 zig_offset += field.ty.abiSize(target);
2183 } else {
2184 // We did not find an llvm field that corresponds to this zig field.
2185 return null;
2186 }
2187 }
19752188};
19762189
19772190pub const FuncGen = struct {
......@@ -2850,7 +3063,7 @@ pub const FuncGen = struct {
28503063 },
28513064 else => {
28523065 var ptr_ty_buf: Type.Payload.Pointer = undefined;
2853 const llvm_field_index = llvmFieldIndex(struct_ty, field_index, target, &ptr_ty_buf).?;
3066 const llvm_field_index = self.dg.llvmFieldIndex(struct_ty, field_index, &ptr_ty_buf).?;
28543067 return self.builder.buildExtractValue(struct_llvm_val, llvm_field_index, "");
28553068 },
28563069 },
......@@ -2865,7 +3078,7 @@ pub const FuncGen = struct {
28653078 .Struct => {
28663079 assert(struct_ty.containerLayout() != .Packed);
28673080 var ptr_ty_buf: Type.Payload.Pointer = undefined;
2868 const llvm_field_index = llvmFieldIndex(struct_ty, field_index, target, &ptr_ty_buf).?;
3081 const llvm_field_index = self.dg.llvmFieldIndex(struct_ty, field_index, &ptr_ty_buf).?;
28693082 const field_ptr = self.builder.buildStructGEP(struct_llvm_val, llvm_field_index, "");
28703083 const field_ptr_ty = Type.initPayload(&ptr_ty_buf.base);
28713084 return self.load(field_ptr, field_ptr_ty);
......@@ -3916,6 +4129,7 @@ pub const FuncGen = struct {
39164129 const operand_is_ref = isByRef(operand_ty);
39174130 const result_is_ref = isByRef(inst_ty);
39184131 const llvm_dest_ty = try self.dg.llvmType(inst_ty);
4132 const target = self.dg.module.getTarget();
39194133
39204134 if (operand_is_ref and result_is_ref) {
39214135 // They are both pointers; just do a bitcast on the pointers :)
......@@ -3927,7 +4141,6 @@ pub const FuncGen = struct {
39274141 }
39284142
39294143 if (operand_ty.zigTypeTag() == .Vector and inst_ty.zigTypeTag() == .Array) {
3930 const target = self.dg.module.getTarget();
39314144 const elem_ty = operand_ty.childType();
39324145 if (!result_is_ref) {
39334146 return self.dg.todo("implement bitcast vector to non-ref array", .{});
......@@ -3957,7 +4170,6 @@ pub const FuncGen = struct {
39574170 }
39584171 return array_ptr;
39594172 } else if (operand_ty.zigTypeTag() == .Array and inst_ty.zigTypeTag() == .Vector) {
3960 const target = self.dg.module.getTarget();
39614173 const elem_ty = operand_ty.childType();
39624174 const llvm_vector_ty = try self.dg.llvmType(inst_ty);
39634175 if (!operand_is_ref) {
......@@ -3998,18 +4210,39 @@ pub const FuncGen = struct {
39984210 if (operand_is_ref) {
39994211 // Bitcast the operand pointer, then load.
40004212 const casted_ptr = self.builder.buildBitCast(operand, llvm_dest_ty.pointerType(0), "");
4001 return self.builder.buildLoad(casted_ptr, "");
4213 const load_inst = self.builder.buildLoad(casted_ptr, "");
4214 load_inst.setAlignment(operand_ty.abiAlignment(target));
4215 return load_inst;
40024216 }
40034217
40044218 if (result_is_ref) {
40054219 // Bitcast the result pointer, then store.
4220 const alignment = @maximum(operand_ty.abiAlignment(target), inst_ty.abiAlignment(target));
40064221 const result_ptr = self.buildAlloca(llvm_dest_ty);
4222 result_ptr.setAlignment(alignment);
40074223 const operand_llvm_ty = try self.dg.llvmType(operand_ty);
40084224 const casted_ptr = self.builder.buildBitCast(result_ptr, operand_llvm_ty.pointerType(0), "");
4009 _ = self.builder.buildStore(operand, casted_ptr);
4225 const store_inst = self.builder.buildStore(operand, casted_ptr);
4226 store_inst.setAlignment(alignment);
40104227 return result_ptr;
40114228 }
40124229
4230 if (llvm_dest_ty.getTypeKind() == .Struct) {
4231 // Both our operand and our result are values, not pointers,
4232 // but LLVM won't let us bitcast struct values.
4233 // Therefore, we store operand to bitcasted alloca, then load for result.
4234 const alignment = @maximum(operand_ty.abiAlignment(target), inst_ty.abiAlignment(target));
4235 const result_ptr = self.buildAlloca(llvm_dest_ty);
4236 result_ptr.setAlignment(alignment);
4237 const operand_llvm_ty = try self.dg.llvmType(operand_ty);
4238 const casted_ptr = self.builder.buildBitCast(result_ptr, operand_llvm_ty.pointerType(0), "");
4239 const store_inst = self.builder.buildStore(operand, casted_ptr);
4240 store_inst.setAlignment(alignment);
4241 const load_inst = self.builder.buildLoad(result_ptr, "");
4242 load_inst.setAlignment(alignment);
4243 return load_inst;
4244 }
4245
40134246 return self.builder.buildBitCast(operand, llvm_dest_ty, "");
40144247 }
40154248
......@@ -5009,9 +5242,8 @@ pub const FuncGen = struct {
50095242 return self.builder.buildBitCast(struct_ptr, result_llvm_ty, "");
50105243 },
50115244 else => {
5012 const target = self.dg.module.getTarget();
50135245 var ty_buf: Type.Payload.Pointer = undefined;
5014 if (llvmFieldIndex(struct_ty, field_index, target, &ty_buf)) |llvm_field_index| {
5246 if (self.dg.llvmFieldIndex(struct_ty, field_index, &ty_buf)) |llvm_field_index| {
50155247 return self.builder.buildStructGEP(struct_ptr, llvm_field_index, "");
50165248 } else {
50175249 // If we found no index then this means this is a zero sized field at the
......@@ -5422,63 +5654,6 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca
54225654 };
54235655}
54245656
5425/// Take into account 0 bit fields. Returns null if an llvm field could not be found. This only
5426/// happens if you want the field index of a zero sized field at the end of the struct.
5427fn llvmFieldIndex(
5428 ty: Type,
5429 field_index: u32,
5430 target: std.Target,
5431 ptr_pl_buf: *Type.Payload.Pointer,
5432) ?c_uint {
5433 if (ty.castTag(.tuple)) |payload| {
5434 const values = payload.data.values;
5435 var llvm_field_index: c_uint = 0;
5436 for (values) |val, i| {
5437 if (val.tag() != .unreachable_value) {
5438 continue;
5439 }
5440 if (field_index > i) {
5441 llvm_field_index += 1;
5442 continue;
5443 }
5444 const field_ty = payload.data.types[i];
5445 ptr_pl_buf.* = .{
5446 .data = .{
5447 .pointee_type = field_ty,
5448 .@"align" = field_ty.abiAlignment(target),
5449 .@"addrspace" = .generic,
5450 },
5451 };
5452 return llvm_field_index;
5453 }
5454 return null;
5455 }
5456 const struct_obj = ty.castTag(.@"struct").?.data;
5457 assert(struct_obj.layout != .Packed);
5458
5459 var llvm_field_index: c_uint = 0;
5460 for (struct_obj.fields.values()) |field, i| {
5461 if (!field.ty.hasRuntimeBits())
5462 continue;
5463 if (field_index > i) {
5464 llvm_field_index += 1;
5465 continue;
5466 }
5467
5468 ptr_pl_buf.* = .{
5469 .data = .{
5470 .pointee_type = field.ty,
5471 .@"align" = field.normalAlignment(target),
5472 .@"addrspace" = .generic,
5473 },
5474 };
5475 return llvm_field_index;
5476 } else {
5477 // We did not find an llvm field that corresponds to this zig field.
5478 return null;
5479 }
5480}
5481
54825657fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {
54835658 switch (fn_info.cc) {
54845659 .Unspecified, .Inline => return isByRef(fn_info.return_type),
......@@ -5497,7 +5672,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
54975672}
54985673
54995674fn isByRef(ty: Type) bool {
5500 // For tuples (and TODO structs), if there are more than this many non-void
5675 // For tuples and structs, if there are more than this many non-void
55015676 // fields, then we make it byref, otherwise byval.
55025677 const max_fields_byval = 2;
55035678
......@@ -5529,24 +5704,28 @@ fn isByRef(ty: Type) bool {
55295704 .Struct => {
55305705 // Packed structs are represented to LLVM as integers.
55315706 if (ty.containerLayout() == .Packed) return false;
5532
5533 if (!ty.hasRuntimeBits()) return false;
5534 if (ty.castTag(.tuple)) |tuple| {
5707 if (ty.isTuple()) {
5708 const tuple = ty.tupleFields();
55355709 var count: usize = 0;
5536 for (tuple.data.values) |field_val, i| {
5710 for (tuple.values) |field_val, i| {
55375711 if (field_val.tag() != .unreachable_value) continue;
5712
55385713 count += 1;
5539 if (count > max_fields_byval) {
5540 return true;
5541 }
5542 const field_ty = tuple.data.types[i];
5543 if (isByRef(field_ty)) {
5544 return true;
5545 }
5714 if (count > max_fields_byval) return true;
5715 if (isByRef(tuple.types[i])) return true;
55465716 }
55475717 return false;
55485718 }
5549 return true;
5719 var count: usize = 0;
5720 const fields = ty.structFields();
5721 for (fields.values()) |field| {
5722 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
5723
5724 count += 1;
5725 if (count > max_fields_byval) return true;
5726 if (isByRef(field.ty)) return true;
5727 }
5728 return false;
55505729 },
55515730 .Union => return ty.hasRuntimeBits(),
55525731 .ErrorUnion => return isByRef(ty.errorUnionPayload()),
src/codegen/llvm/bindings.zig+6
......@@ -916,6 +916,12 @@ pub const TargetMachine = opaque {
916916pub const TargetData = opaque {
917917 pub const dispose = LLVMDisposeTargetData;
918918 extern fn LLVMDisposeTargetData(*const TargetData) void;
919
920 pub const ABISizeOfType = LLVMABISizeOfType;
921 extern fn LLVMABISizeOfType(TD: *const TargetData, Ty: *const Type) c_ulonglong;
922
923 pub const ABIAlignmentOfType = LLVMABIAlignmentOfType;
924 extern fn LLVMABIAlignmentOfType(TD: *const TargetData, Ty: *const Type) c_uint;
919925};
920926
921927pub const CodeModel = enum(c_int) {
src/type.zig+1-1
......@@ -4400,7 +4400,7 @@ pub const Type = extern union {
44004400 };
44014401
44024402 /// Get an iterator that iterates over all the struct field, returning the field and
4403 /// offset of that field. Asserts that the type is a none packed struct.
4403 /// offset of that field. Asserts that the type is a non-packed struct.
44044404 pub fn iterateStructOffsets(ty: Type, target: Target) StructOffsetIterator {
44054405 const struct_obj = ty.castTag(.@"struct").?.data;
44064406 assert(struct_obj.haveLayout());
test/behavior/basic.zig-2
......@@ -778,8 +778,6 @@ extern var opaque_extern_var: opaque {};
778778var var_to_export: u32 = 42;
779779
780780test "lazy typeInfo value as generic parameter" {
781 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
782
783781 const S = struct {
784782 fn foo(args: anytype) void {
785783 _ = args;
test/behavior/bugs/394.zig+1-1
......@@ -10,7 +10,7 @@ const S = struct {
1010const expect = @import("std").testing.expect;
1111const builtin = @import("builtin");
1212
13test "bug 394 fixed" {
13test "fixed" {
1414 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1515 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1616 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
test/behavior/for.zig+7-2
......@@ -157,7 +157,9 @@ test "2 break statements and an else" {
157157}
158158
159159test "for loop with pointer elem var" {
160 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
160 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
161 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
162 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
161163
162164 const source = "abcdefg";
163165 var target: [source.len]u8 = undefined;
......@@ -199,7 +201,10 @@ test "for copies its payload" {
199201}
200202
201203test "for on slice with allowzero ptr" {
202 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
204 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
205 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
206 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
207 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
203208
204209 const S = struct {
205210 fn doTheTest(slice: []const u8) !void {
test/behavior/if.zig-2
......@@ -106,8 +106,6 @@ test "if copies its payload" {
106106}
107107
108108test "if prongs cast to expected type instead of peer type resolution" {
109 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
110
111109 const S = struct {
112110 fn doTheTest(f: bool) !void {
113111 var x: i32 = 0;
test/behavior/optional.zig+12-8
......@@ -251,7 +251,9 @@ test "coerce an anon struct literal to optional struct" {
251251}
252252
253253test "0-bit child type coerced to optional return ptr result location" {
254 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
254 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
255 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
256 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
255257
256258 const S = struct {
257259 fn doTheTest() !void {
......@@ -300,7 +302,9 @@ test "0-bit child type coerced to optional" {
300302}
301303
302304test "array of optional unaligned types" {
303 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
305 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
306 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
307 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
304308
305309 const Enum = enum { one, two, three };
306310
......@@ -320,15 +324,15 @@ test "array of optional unaligned types" {
320324
321325 // The index must be a runtime value
322326 var i: usize = 0;
323 try expectEqual(Enum.one, values[i].?.Num);
327 try expect(Enum.one == values[i].?.Num);
324328 i += 1;
325 try expectEqual(Enum.two, values[i].?.Num);
329 try expect(Enum.two == values[i].?.Num);
326330 i += 1;
327 try expectEqual(Enum.three, values[i].?.Num);
331 try expect(Enum.three == values[i].?.Num);
328332 i += 1;
329 try expectEqual(Enum.one, values[i].?.Num);
333 try expect(Enum.one == values[i].?.Num);
330334 i += 1;
331 try expectEqual(Enum.two, values[i].?.Num);
335 try expect(Enum.two == values[i].?.Num);
332336 i += 1;
333 try expectEqual(Enum.three, values[i].?.Num);
337 try expect(Enum.three == values[i].?.Num);
334338}
test/behavior/ptrcast.zig+5-1
......@@ -41,7 +41,11 @@ fn testReinterpretBytesAsExternStruct() !void {
4141}
4242
4343test "reinterpret struct field at comptime" {
44 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
44 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
45 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
46 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
47 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
48 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4549
4650 const numNative = comptime Bytes.init(0x12345678);
4751 if (native_endian != .Little) {
test/behavior/reflection.zig-2
......@@ -5,8 +5,6 @@ const mem = std.mem;
55const reflection = @This();
66
77test "reflection: function return type, var args, and param types" {
8 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
9
108 comptime {
119 const info = @typeInfo(@TypeOf(dummy)).Fn;
1210 try expect(info.return_type.? == i32);
test/behavior/sizeof_and_typeof.zig-4
......@@ -180,8 +180,6 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" {
180180}
181181
182182test "@TypeOf() has no runtime side effects" {
183 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
184
185183 const S = struct {
186184 fn foo(comptime T: type, ptr: *T) T {
187185 ptr.* += 1;
......@@ -195,8 +193,6 @@ test "@TypeOf() has no runtime side effects" {
195193}
196194
197195test "branching logic inside @TypeOf" {
198 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
199
200196 const S = struct {
201197 var data: i32 = 0;
202198 fn foo() anyerror!i32 {
test/behavior/struct.zig+6-2
......@@ -857,7 +857,9 @@ test "fn with C calling convention returns struct by value" {
857857}
858858
859859test "non-packed struct with u128 entry in union" {
860 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
860 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
861 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
862 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
861863
862864 const U = union(enum) {
863865 Num: u128,
......@@ -952,7 +954,9 @@ test "fully anonymous struct" {
952954}
953955
954956test "fully anonymous list literal" {
955 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
957 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
958 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
959 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
956960
957961 const S = struct {
958962 fn doTheTest() !void {
test/behavior/type.zig-4
......@@ -138,8 +138,6 @@ test "Type.Array" {
138138}
139139
140140test "@Type create slice with null sentinel" {
141 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
142
143141 const Slice = @Type(TypeInfo{
144142 .Pointer = .{
145143 .size = .Slice,
......@@ -156,8 +154,6 @@ test "@Type create slice with null sentinel" {
156154}
157155
158156test "@Type picks up the sentinel value from TypeInfo" {
159 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
160
161157 try testTypes(&[_]type{
162158 [11:0]u8, [4:10]u8,
163159 [*:0]u8, [*:0]const u8,
test/behavior/var_args.zig+9-3
......@@ -13,7 +13,9 @@ fn add(args: anytype) i32 {
1313}
1414
1515test "add arbitrary args" {
16 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
16 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
17 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
18 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1719
1820 try expect(add(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10);
1921 try expect(add(.{@as(i32, 1234)}) == 1234);
......@@ -34,7 +36,9 @@ test "send void arg to var args" {
3436}
3537
3638test "pass args directly" {
37 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
39 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
40 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
41 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
3842
3943 try expect(addSomeStuff(.{ @as(i32, 1), @as(i32, 2), @as(i32, 3), @as(i32, 4) }) == 10);
4044 try expect(addSomeStuff(.{@as(i32, 1234)}) == 1234);
......@@ -46,7 +50,9 @@ fn addSomeStuff(args: anytype) i32 {
4650}
4751
4852test "runtime parameter before var args" {
49 if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
53 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
54 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
55 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5056
5157 try expect((try extraFn(10, .{})) == 0);
5258 try expect((try extraFn(10, .{false})) == 1);