authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-03 01:14:56-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-03 01:19:49-07:00
logb33f3b23c98d7bb03b116fb084b35ce6b9fc1257
tree2ae27a2c1268e4112f3f5b38feeef7e4643af2b4
parent06b1a88a157dde8fdb9653aabda600cdeee2dd13

LLVM: fix lowering of unions and switches

`Module.Union.getLayout` now additionally returns a `padding` field which tells how many bytes are between the final field end offset and the ending offset of the union. This is used by the LLVM backend to explicitly insert padding. LLVM backend: lowering of unions now inserts additional padding so that LLVM's internals will agree on the ABI size to match what ABI size zig wants unions to be. This is an alternative to calling LLVMABISizeOfType and LLVMABIAlignmentOfType which end up crashing when recursive struct definitions come into play. We no longer ever call these two functions and the bindings are deleted to avoid future footgun firings. LLVM backend: lowering of unions now represents untagged unions consistently. Before it was tripping an assertion. LLVM backend: switch cases call inttoptr on the case items and condition if necessary. Prevents tripping an LLVM assertion. After this commit, we are no longer tripping over any LLVM assertions.

3 files changed, 219 insertions(+), 243 deletions(-)

src/Module.zig+8
...@@ -1254,6 +1254,7 @@ pub const Union = struct {...@@ -1254,6 +1254,7 @@ pub const Union = struct {
1254 payload_align: u32,1254 payload_align: u32,
1255 tag_align: u32,1255 tag_align: u32,
1256 tag_size: u64,1256 tag_size: u64,
1257 padding: u32,
1257 };1258 };
12581259
1259 pub fn haveLayout(u: Union) bool {1260 pub fn haveLayout(u: Union) bool {
...@@ -1309,24 +1310,30 @@ pub const Union = struct {...@@ -1309,24 +1310,30 @@ pub const Union = struct {
1309 .payload_align = payload_align,1310 .payload_align = payload_align,
1310 .tag_align = 0,1311 .tag_align = 0,
1311 .tag_size = 0,1312 .tag_size = 0,
1313 .padding = 0,
1312 };1314 };
1313 // Put the tag before or after the payload depending on which one's1315 // Put the tag before or after the payload depending on which one's
1314 // alignment is greater.1316 // alignment is greater.
1315 const tag_size = u.tag_ty.abiSize(target);1317 const tag_size = u.tag_ty.abiSize(target);
1316 const tag_align = @maximum(1, u.tag_ty.abiAlignment(target));1318 const tag_align = @maximum(1, u.tag_ty.abiAlignment(target));
1317 var size: u64 = 0;1319 var size: u64 = 0;
1320 var padding: u32 = undefined;
1318 if (tag_align >= payload_align) {1321 if (tag_align >= payload_align) {
1319 // {Tag, Payload}1322 // {Tag, Payload}
1320 size += tag_size;1323 size += tag_size;
1321 size = std.mem.alignForwardGeneric(u64, size, payload_align);1324 size = std.mem.alignForwardGeneric(u64, size, payload_align);
1322 size += payload_size;1325 size += payload_size;
1326 const prev_size = size;
1323 size = std.mem.alignForwardGeneric(u64, size, tag_align);1327 size = std.mem.alignForwardGeneric(u64, size, tag_align);
1328 padding = @intCast(u32, size - prev_size);
1324 } else {1329 } else {
1325 // {Payload, Tag}1330 // {Payload, Tag}
1326 size += payload_size;1331 size += payload_size;
1327 size = std.mem.alignForwardGeneric(u64, size, tag_align);1332 size = std.mem.alignForwardGeneric(u64, size, tag_align);
1328 size += tag_size;1333 size += tag_size;
1334 const prev_size = size;
1329 size = std.mem.alignForwardGeneric(u64, size, payload_align);1335 size = std.mem.alignForwardGeneric(u64, size, payload_align);
1336 padding = @intCast(u32, size - prev_size);
1330 }1337 }
1331 return .{1338 return .{
1332 .abi_size = size,1339 .abi_size = size,
...@@ -1338,6 +1345,7 @@ pub const Union = struct {...@@ -1338,6 +1345,7 @@ pub const Union = struct {
1338 .payload_align = payload_align,1345 .payload_align = payload_align,
1339 .tag_align = tag_align,1346 .tag_align = tag_align,
1340 .tag_size = tag_size,1347 .tag_size = tag_size,
1348 .padding = padding,
1341 };1349 };
1342 }1350 }
1343};1351};
src/codegen/llvm.zig+211-237
...@@ -982,44 +982,36 @@ pub const DeclGen = struct {...@@ -982,44 +982,36 @@ pub const DeclGen = struct {
982982
983 try llvm_field_types.ensureUnusedCapacity(gpa, tuple.types.len);983 try llvm_field_types.ensureUnusedCapacity(gpa, tuple.types.len);
984984
985 // We need to insert extra padding if LLVM's isn't enough.985 comptime assert(struct_layout_version == 2);
986 var zig_offset: u64 = 0;986 var offset: u64 = 0;
987 var llvm_offset: u64 = 0;987 var big_align: u32 = 0;
988 var zig_big_align: u32 = 0;
989 var llvm_big_align: u32 = 0;
990988
991 for (tuple.types) |field_ty, i| {989 for (tuple.types) |field_ty, i| {
992 const field_val = tuple.values[i];990 const field_val = tuple.values[i];
993 if (field_val.tag() != .unreachable_value) continue;991 if (field_val.tag() != .unreachable_value) continue;
994992
995 const field_align = field_ty.abiAlignment(target);993 const field_align = field_ty.abiAlignment(target);
996 zig_big_align = @maximum(zig_big_align, field_align);994 big_align = @maximum(big_align, field_align);
997 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align);995 const prev_offset = offset;
996 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
998997
999 const field_llvm_ty = try dg.llvmType(field_ty);998 const padding_len = offset - prev_offset;
1000 const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty);
1001 llvm_big_align = @maximum(llvm_big_align, field_llvm_align);
1002 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align);
1003
1004 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
1005 if (padding_len > 0) {999 if (padding_len > 0) {
1006 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);1000 const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len));
1007 try llvm_field_types.append(gpa, llvm_array_ty);1001 try llvm_field_types.append(gpa, llvm_array_ty);
1008 llvm_offset = zig_offset;
1009 }1002 }
1003 const field_llvm_ty = try dg.llvmType(field_ty);
1010 try llvm_field_types.append(gpa, field_llvm_ty);1004 try llvm_field_types.append(gpa, field_llvm_ty);
10111005
1012 llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty);1006 offset += field_ty.abiSize(target);
1013 zig_offset += field_ty.abiSize(target);
1014 }1007 }
1015 {1008 {
1016 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, zig_big_align);1009 const prev_offset = offset;
1017 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, llvm_big_align);1010 offset = std.mem.alignForwardGeneric(u64, offset, big_align);
1018 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);1011 const padding_len = offset - prev_offset;
1019 if (padding_len > 0) {1012 if (padding_len > 0) {
1020 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);1013 const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len));
1021 try llvm_field_types.append(gpa, llvm_array_ty);1014 try llvm_field_types.append(gpa, llvm_array_ty);
1022 llvm_offset = zig_offset;
1023 }1015 }
1024 }1016 }
10251017
...@@ -1055,43 +1047,35 @@ pub const DeclGen = struct {...@@ -1055,43 +1047,35 @@ pub const DeclGen = struct {
10551047
1056 try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count());1048 try llvm_field_types.ensureUnusedCapacity(gpa, struct_obj.fields.count());
10571049
1058 // We need to insert extra padding if LLVM's isn't enough.1050 comptime assert(struct_layout_version == 2);
1059 var zig_offset: u64 = 0;1051 var offset: u64 = 0;
1060 var llvm_offset: u64 = 0;1052 var big_align: u32 = 0;
1061 var zig_big_align: u32 = 0;
1062 var llvm_big_align: u32 = 0;
10631053
1064 for (struct_obj.fields.values()) |field| {1054 for (struct_obj.fields.values()) |field| {
1065 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;1055 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
10661056
1067 const field_align = field.normalAlignment(target);1057 const field_align = field.normalAlignment(target);
1068 zig_big_align = @maximum(zig_big_align, field_align);1058 big_align = @maximum(big_align, field_align);
1069 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align);1059 const prev_offset = offset;
10701060 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
1071 const field_llvm_ty = try dg.llvmType(field.ty);
1072 const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty);
1073 llvm_big_align = @maximum(llvm_big_align, field_llvm_align);
1074 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align);
10751061
1076 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);1062 const padding_len = offset - prev_offset;
1077 if (padding_len > 0) {1063 if (padding_len > 0) {
1078 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);1064 const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len));
1079 try llvm_field_types.append(gpa, llvm_array_ty);1065 try llvm_field_types.append(gpa, llvm_array_ty);
1080 llvm_offset = zig_offset;
1081 }1066 }
1067 const field_llvm_ty = try dg.llvmType(field.ty);
1082 try llvm_field_types.append(gpa, field_llvm_ty);1068 try llvm_field_types.append(gpa, field_llvm_ty);
10831069
1084 llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty);1070 offset += field.ty.abiSize(target);
1085 zig_offset += field.ty.abiSize(target);
1086 }1071 }
1087 {1072 {
1088 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, zig_big_align);1073 const prev_offset = offset;
1089 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, llvm_big_align);1074 offset = std.mem.alignForwardGeneric(u64, offset, big_align);
1090 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);1075 const padding_len = offset - prev_offset;
1091 if (padding_len > 0) {1076 if (padding_len > 0) {
1092 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);1077 const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len));
1093 try llvm_field_types.append(gpa, llvm_array_ty);1078 try llvm_field_types.append(gpa, llvm_array_ty);
1094 llvm_offset = zig_offset;
1095 }1079 }
1096 }1080 }
10971081
...@@ -1111,71 +1095,61 @@ pub const DeclGen = struct {...@@ -1111,71 +1095,61 @@ pub const DeclGen = struct {
1111 // reference, we need to copy it here.1095 // reference, we need to copy it here.
1112 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());1096 gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
11131097
1098 const layout = t.unionGetLayout(target);
1114 const union_obj = t.cast(Type.Payload.Union).?.data;1099 const union_obj = t.cast(Type.Payload.Union).?.data;
1115 if (t.unionTagType()) |enum_tag_ty| {
1116 const layout = union_obj.getLayout(target, true);
11171100
1118 if (layout.payload_size == 0) {1101 if (layout.payload_size == 0) {
1119 const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);1102 const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty);
1120 gop.value_ptr.* = enum_tag_llvm_ty;1103 gop.value_ptr.* = enum_tag_llvm_ty;
1121 return enum_tag_llvm_ty;1104 return enum_tag_llvm_ty;
1122 }1105 }
11231106
1124 const name = try union_obj.getFullyQualifiedName(gpa);1107 const name = try union_obj.getFullyQualifiedName(gpa);
1125 defer gpa.free(name);1108 defer gpa.free(name);
11261109
1127 const llvm_union_ty = dg.context.structCreateNamed(name);1110 const llvm_union_ty = dg.context.structCreateNamed(name);
1128 gop.value_ptr.* = llvm_union_ty; // must be done before any recursive calls1111 gop.value_ptr.* = llvm_union_ty; // must be done before any recursive calls
11291112
1130 const aligned_field = union_obj.fields.values()[layout.most_aligned_field];1113 const aligned_field = union_obj.fields.values()[layout.most_aligned_field];
1131 const llvm_aligned_field_ty = try dg.llvmType(aligned_field.ty);1114 const llvm_aligned_field_ty = try dg.llvmType(aligned_field.ty);
11321115
1133 const llvm_payload_ty = t: {1116 const llvm_payload_ty = t: {
1134 if (layout.most_aligned_field_size == layout.payload_size) {1117 if (layout.most_aligned_field_size == layout.payload_size) {
1135 break :t llvm_aligned_field_ty;1118 break :t llvm_aligned_field_ty;
1136 }1119 }
1137 const padding_len = @intCast(c_uint, layout.payload_size - layout.most_aligned_field_size);1120 const padding_len = @intCast(c_uint, layout.payload_size - layout.most_aligned_field_size);
1138 const fields: [2]*const llvm.Type = .{1121 const fields: [2]*const llvm.Type = .{
1139 llvm_aligned_field_ty,1122 llvm_aligned_field_ty,
1140 dg.context.intType(8).arrayType(padding_len),1123 dg.context.intType(8).arrayType(padding_len),
1141 };
1142 break :t dg.context.structType(&fields, fields.len, .True);
1143 };1124 };
1125 break :t dg.context.structType(&fields, fields.len, .True);
1126 };
11441127
1145 if (layout.tag_size == 0) {1128 if (layout.tag_size == 0) {
1146 var llvm_fields: [1]*const llvm.Type = .{llvm_payload_ty};1129 var llvm_fields: [1]*const llvm.Type = .{llvm_payload_ty};
1147 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);
1148 return llvm_union_ty;
1149 }
1150 const enum_tag_llvm_ty = try dg.llvmType(enum_tag_ty);
1151
1152 // Put the tag before or after the payload depending on which one's
1153 // alignment is greater.
1154 var llvm_fields: [2]*const llvm.Type = undefined;
1155 if (layout.tag_align >= layout.payload_align) {
1156 llvm_fields[0] = enum_tag_llvm_ty;
1157 llvm_fields[1] = llvm_payload_ty;
1158 } else {
1159 llvm_fields[0] = llvm_payload_ty;
1160 llvm_fields[1] = enum_tag_llvm_ty;
1161 }
1162 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);1130 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);
1163 return llvm_union_ty;1131 return llvm_union_ty;
1164 }1132 }
1165 // Untagged union1133 const enum_tag_llvm_ty = try dg.llvmType(union_obj.tag_ty);
1166 const layout = union_obj.getLayout(target, false);
11671134
1168 const name = try union_obj.getFullyQualifiedName(gpa);1135 // Put the tag before or after the payload depending on which one's
1169 defer gpa.free(name);1136 // alignment is greater.
1137 var llvm_fields: [3]*const llvm.Type = undefined;
1138 var llvm_fields_len: c_uint = 2;
11701139
1171 const llvm_union_ty = dg.context.structCreateNamed(name);1140 if (layout.tag_align >= layout.payload_align) {
1172 gop.value_ptr.* = llvm_union_ty; // must be done before any recursive calls1141 llvm_fields = .{ enum_tag_llvm_ty, llvm_payload_ty, undefined };
1142 } else {
1143 llvm_fields = .{ llvm_payload_ty, enum_tag_llvm_ty, undefined };
1144 }
11731145
1174 const big_field = union_obj.fields.values()[layout.biggest_field];1146 // Insert padding to make the LLVM struct ABI size match the Zig union ABI size.
1175 const llvm_big_field_ty = try dg.llvmType(big_field.ty);1147 if (layout.padding != 0) {
1148 llvm_fields[2] = dg.context.intType(8).arrayType(layout.padding);
1149 llvm_fields_len = 3;
1150 }
11761151
1177 var llvm_fields: [1]*const llvm.Type = .{llvm_big_field_ty};1152 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields_len, .False);
1178 llvm_union_ty.structSetBody(&llvm_fields, llvm_fields.len, .False);
1179 return llvm_union_ty;1153 return llvm_union_ty;
1180 },1154 },
1181 .Fn => {1155 .Fn => {
...@@ -1586,33 +1560,25 @@ pub const DeclGen = struct {...@@ -1586,33 +1560,25 @@ pub const DeclGen = struct {
1586 var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count);1560 var llvm_fields = try std.ArrayListUnmanaged(*const llvm.Value).initCapacity(gpa, llvm_field_count);
1587 defer llvm_fields.deinit(gpa);1561 defer llvm_fields.deinit(gpa);
15881562
1589 // These are used to detect where the extra padding fields are so that we1563 comptime assert(struct_layout_version == 2);
1590 // can initialize them with undefined.1564 var offset: u64 = 0;
1591 var zig_offset: u64 = 0;1565 var big_align: u32 = 0;
1592 var llvm_offset: u64 = 0;
1593 var zig_big_align: u32 = 0;
1594 var llvm_big_align: u32 = 0;
15951566
1596 var need_unnamed = false;1567 var need_unnamed = false;
1597 for (struct_obj.fields.values()) |field, i| {1568 for (struct_obj.fields.values()) |field, i| {
1598 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;1569 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
15991570
1600 const field_align = field.normalAlignment(target);1571 const field_align = field.normalAlignment(target);
1601 zig_big_align = @maximum(zig_big_align, field_align);1572 big_align = @maximum(big_align, field_align);
1602 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align);1573 const prev_offset = offset;
16031574 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
1604 const field_llvm_ty = try dg.llvmType(field.ty);
1605 const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty);
1606 llvm_big_align = @maximum(llvm_big_align, field_llvm_align);
1607 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align);
16081575
1609 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);1576 const padding_len = offset - prev_offset;
1610 if (padding_len > 0) {1577 if (padding_len > 0) {
1611 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);1578 const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len));
1612 // TODO make this and all other padding elsewhere in debug1579 // TODO make this and all other padding elsewhere in debug
1613 // builds be 0xaa not undef.1580 // builds be 0xaa not undef.
1614 llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef());1581 llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef());
1615 llvm_offset = zig_offset;
1616 }1582 }
16171583
1618 const field_llvm_val = try dg.genTypedValue(.{1584 const field_llvm_val = try dg.genTypedValue(.{
...@@ -1624,17 +1590,15 @@ pub const DeclGen = struct {...@@ -1624,17 +1590,15 @@ pub const DeclGen = struct {
16241590
1625 llvm_fields.appendAssumeCapacity(field_llvm_val);1591 llvm_fields.appendAssumeCapacity(field_llvm_val);
16261592
1627 llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty);1593 offset += field.ty.abiSize(target);
1628 zig_offset += field.ty.abiSize(target);
1629 }1594 }
1630 {1595 {
1631 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, zig_big_align);1596 const prev_offset = offset;
1632 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, llvm_big_align);1597 offset = std.mem.alignForwardGeneric(u64, offset, big_align);
1633 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);1598 const padding_len = offset - prev_offset;
1634 if (padding_len > 0) {1599 if (padding_len > 0) {
1635 const llvm_array_ty = dg.context.intType(8).arrayType(padding_len);1600 const llvm_array_ty = dg.context.intType(8).arrayType(@intCast(c_uint, padding_len));
1636 llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef());1601 llvm_fields.appendAssumeCapacity(llvm_array_ty.getUndef());
1637 llvm_offset = zig_offset;
1638 }1602 }
1639 }1603 }
16401604
...@@ -1703,16 +1667,21 @@ pub const DeclGen = struct {...@@ -1703,16 +1667,21 @@ pub const DeclGen = struct {
1703 .ty = tv.ty.unionTagType().?,1667 .ty = tv.ty.unionTagType().?,
1704 .val = tag_and_val.tag,1668 .val = tag_and_val.tag,
1705 });1669 });
1706 var fields: [2]*const llvm.Value = undefined;1670 var fields: [3]*const llvm.Value = undefined;
1671 var fields_len: c_uint = 2;
1707 if (layout.tag_align >= layout.payload_align) {1672 if (layout.tag_align >= layout.payload_align) {
1708 fields = .{ llvm_tag_value, payload };1673 fields = .{ llvm_tag_value, payload, undefined };
1709 } else {1674 } else {
1710 fields = .{ payload, llvm_tag_value };1675 fields = .{ payload, llvm_tag_value, undefined };
1676 }
1677 if (layout.padding != 0) {
1678 fields[2] = dg.context.intType(8).arrayType(layout.padding).getUndef();
1679 fields_len = 3;
1711 }1680 }
1712 if (need_unnamed) {1681 if (need_unnamed) {
1713 return dg.context.constStruct(&fields, fields.len, .False);1682 return dg.context.constStruct(&fields, fields_len, .False);
1714 } else {1683 } else {
1715 return llvm_union_ty.constNamedStruct(&fields, fields.len);1684 return llvm_union_ty.constNamedStruct(&fields, fields_len);
1716 }1685 }
1717 },1686 },
1718 .Vector => switch (tv.val.tag()) {1687 .Vector => switch (tv.val.tag()) {
...@@ -1867,7 +1836,7 @@ pub const DeclGen = struct {...@@ -1867,7 +1836,7 @@ pub const DeclGen = struct {
1867 },1836 },
1868 .Struct => {1837 .Struct => {
1869 var ty_buf: Type.Payload.Pointer = undefined;1838 var ty_buf: Type.Payload.Pointer = undefined;
1870 const llvm_field_index = dg.llvmFieldIndex(parent.ty, field_index, &ty_buf).?;1839 const llvm_field_index = llvmFieldIndex(parent.ty, field_index, target, &ty_buf).?;
1871 const indices: [2]*const llvm.Value = .{1840 const indices: [2]*const llvm.Value = .{
1872 llvm_u32.constInt(0, .False),1841 llvm_u32.constInt(0, .False),
1873 llvm_u32.constInt(llvm_field_index, .False),1842 llvm_u32.constInt(llvm_field_index, .False),
...@@ -2098,107 +2067,6 @@ pub const DeclGen = struct {...@@ -2098,107 +2067,6 @@ pub const DeclGen = struct {
2098 return null;2067 return null;
2099 }2068 }
2100 }2069 }
2101
2102 /// Take into account 0 bit fields and padding. Returns null if an llvm
2103 /// field could not be found.
2104 /// This only happens if you want the field index of a zero sized field at
2105 /// the end of the struct.
2106 fn llvmFieldIndex(
2107 dg: *DeclGen,
2108 ty: Type,
2109 field_index: usize,
2110 ptr_pl_buf: *Type.Payload.Pointer,
2111 ) ?c_uint {
2112 const target = dg.module.getTarget();
2113
2114 // Detects where we inserted extra padding fields so that we can skip
2115 // over them in this function.
2116 var zig_offset: u64 = 0;
2117 var llvm_offset: u64 = 0;
2118 var zig_big_align: u32 = 0;
2119 var llvm_big_align: u32 = 0;
2120
2121 if (ty.isTupleOrAnonStruct()) {
2122 const tuple = ty.tupleFields();
2123 var llvm_field_index: c_uint = 0;
2124 for (tuple.types) |field_ty, i| {
2125 if (tuple.values[i].tag() != .unreachable_value) continue;
2126
2127 const field_align = field_ty.abiAlignment(target);
2128 zig_big_align = @maximum(zig_big_align, field_align);
2129 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align);
2130
2131 // assert no error because we have already seen a successful
2132 // llvmType on this field.
2133 const field_llvm_ty = dg.llvmType(field_ty) catch unreachable;
2134 const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty);
2135 llvm_big_align = @maximum(llvm_big_align, field_llvm_align);
2136 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align);
2137
2138 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
2139 if (padding_len > 0) {
2140 llvm_field_index += 1;
2141 llvm_offset = zig_offset;
2142 }
2143
2144 if (field_index == i) {
2145 ptr_pl_buf.* = .{
2146 .data = .{
2147 .pointee_type = field_ty,
2148 .@"align" = field_align,
2149 .@"addrspace" = .generic,
2150 },
2151 };
2152 return llvm_field_index;
2153 }
2154
2155 llvm_field_index += 1;
2156 llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty);
2157 zig_offset += field_ty.abiSize(target);
2158 }
2159 return null;
2160 }
2161 assert(ty.containerLayout() != .Packed);
2162
2163 var llvm_field_index: c_uint = 0;
2164 for (ty.structFields().values()) |field, i| {
2165 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
2166
2167 const field_align = field.normalAlignment(target);
2168 zig_big_align = @maximum(zig_big_align, field_align);
2169 zig_offset = std.mem.alignForwardGeneric(u64, zig_offset, field_align);
2170
2171 // assert no error because we have already seen a successful llvmType on this field.
2172 const field_llvm_ty = dg.llvmType(field.ty) catch unreachable;
2173 const field_llvm_align = dg.object.target_data.ABIAlignmentOfType(field_llvm_ty);
2174 llvm_big_align = @maximum(llvm_big_align, field_llvm_align);
2175 llvm_offset = std.mem.alignForwardGeneric(u64, llvm_offset, field_llvm_align);
2176
2177 const padding_len = @intCast(c_uint, zig_offset - llvm_offset);
2178 if (padding_len > 0) {
2179 llvm_field_index += 1;
2180 llvm_offset = zig_offset;
2181 }
2182
2183 if (field_index == i) {
2184 ptr_pl_buf.* = .{
2185 .data = .{
2186 .pointee_type = field.ty,
2187 .@"align" = field_align,
2188 .@"addrspace" = .generic,
2189 },
2190 };
2191 return llvm_field_index;
2192 }
2193
2194 llvm_field_index += 1;
2195 llvm_offset += dg.object.target_data.ABISizeOfType(field_llvm_ty);
2196 zig_offset += field.ty.abiSize(target);
2197 } else {
2198 // We did not find an llvm field that corresponds to this zig field.
2199 return null;
2200 }
2201 }
2202};2070};
22032071
2204pub const FuncGen = struct {2072pub const FuncGen = struct {
...@@ -2807,7 +2675,13 @@ pub const FuncGen = struct {...@@ -2807,7 +2675,13 @@ pub const FuncGen = struct {
2807 const cond = try self.resolveInst(pl_op.operand);2675 const cond = try self.resolveInst(pl_op.operand);
2808 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);2676 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload);
2809 const else_block = self.context.appendBasicBlock(self.llvm_func, "Else");2677 const else_block = self.context.appendBasicBlock(self.llvm_func, "Else");
2810 const llvm_switch = self.builder.buildSwitch(cond, else_block, switch_br.data.cases_len);2678 const target = self.dg.module.getTarget();
2679 const llvm_usize = self.context.intType(target.cpu.arch.ptrBitWidth());
2680 const cond_int = if (cond.typeOf().getTypeKind() == .Pointer)
2681 self.builder.buildPtrToInt(cond, llvm_usize, "")
2682 else
2683 cond;
2684 const llvm_switch = self.builder.buildSwitch(cond_int, else_block, switch_br.data.cases_len);
28112685
2812 var extra_index: usize = switch_br.end;2686 var extra_index: usize = switch_br.end;
2813 var case_i: u32 = 0;2687 var case_i: u32 = 0;
...@@ -2822,7 +2696,11 @@ pub const FuncGen = struct {...@@ -2822,7 +2696,11 @@ pub const FuncGen = struct {
28222696
2823 for (items) |item| {2697 for (items) |item| {
2824 const llvm_item = try self.resolveInst(item);2698 const llvm_item = try self.resolveInst(item);
2825 llvm_switch.addCase(llvm_item, case_block);2699 const llvm_int_item = if (llvm_item.typeOf().getTypeKind() == .Pointer)
2700 llvm_item.constPtrToInt(llvm_usize)
2701 else
2702 llvm_item;
2703 llvm_switch.addCase(llvm_int_item, case_block);
2826 }2704 }
28272705
2828 self.builder.positionBuilderAtEnd(case_block);2706 self.builder.positionBuilderAtEnd(case_block);
...@@ -3077,7 +2955,7 @@ pub const FuncGen = struct {...@@ -3077,7 +2955,7 @@ pub const FuncGen = struct {
3077 },2955 },
3078 else => {2956 else => {
3079 var ptr_ty_buf: Type.Payload.Pointer = undefined;2957 var ptr_ty_buf: Type.Payload.Pointer = undefined;
3080 const llvm_field_index = self.dg.llvmFieldIndex(struct_ty, field_index, &ptr_ty_buf).?;2958 const llvm_field_index = llvmFieldIndex(struct_ty, field_index, target, &ptr_ty_buf).?;
3081 return self.builder.buildExtractValue(struct_llvm_val, llvm_field_index, "");2959 return self.builder.buildExtractValue(struct_llvm_val, llvm_field_index, "");
3082 },2960 },
3083 },2961 },
...@@ -3092,7 +2970,7 @@ pub const FuncGen = struct {...@@ -3092,7 +2970,7 @@ pub const FuncGen = struct {
3092 .Struct => {2970 .Struct => {
3093 assert(struct_ty.containerLayout() != .Packed);2971 assert(struct_ty.containerLayout() != .Packed);
3094 var ptr_ty_buf: Type.Payload.Pointer = undefined;2972 var ptr_ty_buf: Type.Payload.Pointer = undefined;
3095 const llvm_field_index = self.dg.llvmFieldIndex(struct_ty, field_index, &ptr_ty_buf).?;2973 const llvm_field_index = llvmFieldIndex(struct_ty, field_index, target, &ptr_ty_buf).?;
3096 const field_ptr = self.builder.buildStructGEP(struct_llvm_val, llvm_field_index, "");2974 const field_ptr = self.builder.buildStructGEP(struct_llvm_val, llvm_field_index, "");
3097 const field_ptr_ty = Type.initPayload(&ptr_ty_buf.base);2975 const field_ptr_ty = Type.initPayload(&ptr_ty_buf.base);
3098 return self.load(field_ptr, field_ptr_ty);2976 return self.load(field_ptr, field_ptr_ty);
...@@ -4907,6 +4785,7 @@ pub const FuncGen = struct {...@@ -4907,6 +4785,7 @@ pub const FuncGen = struct {
4907 const len = @intCast(usize, result_ty.arrayLen());4785 const len = @intCast(usize, result_ty.arrayLen());
4908 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);4786 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
4909 const llvm_result_ty = try self.dg.llvmType(result_ty);4787 const llvm_result_ty = try self.dg.llvmType(result_ty);
4788 const target = self.dg.module.getTarget();
49104789
4911 switch (result_ty.zigTypeTag()) {4790 switch (result_ty.zigTypeTag()) {
4912 .Vector => {4791 .Vector => {
...@@ -4928,7 +4807,6 @@ pub const FuncGen = struct {...@@ -4928,7 +4807,6 @@ pub const FuncGen = struct {
4928 const alloca_inst = self.buildAlloca(llvm_result_ty);4807 const alloca_inst = self.buildAlloca(llvm_result_ty);
4929 // TODO in debug builds init to undef so that the padding will be 0xaa4808 // TODO in debug builds init to undef so that the padding will be 0xaa
4930 // even if we fully populate the fields.4809 // even if we fully populate the fields.
4931 const target = self.dg.module.getTarget();
4932 alloca_inst.setAlignment(result_ty.abiAlignment(target));4810 alloca_inst.setAlignment(result_ty.abiAlignment(target));
49334811
4934 var indices: [2]*const llvm.Value = .{ llvm_u32.constNull(), undefined };4812 var indices: [2]*const llvm.Value = .{ llvm_u32.constNull(), undefined };
...@@ -4936,7 +4814,7 @@ pub const FuncGen = struct {...@@ -4936,7 +4814,7 @@ pub const FuncGen = struct {
4936 if (result_ty.structFieldValueComptime(i) != null) continue;4814 if (result_ty.structFieldValueComptime(i) != null) continue;
49374815
4938 const llvm_elem = try self.resolveInst(elem);4816 const llvm_elem = try self.resolveInst(elem);
4939 const llvm_i = self.dg.llvmFieldIndex(result_ty, i, &ptr_ty_buf).?;4817 const llvm_i = llvmFieldIndex(result_ty, i, target, &ptr_ty_buf).?;
4940 indices[1] = llvm_u32.constInt(llvm_i, .False);4818 indices[1] = llvm_u32.constInt(llvm_i, .False);
4941 const field_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");4819 const field_ptr = self.builder.buildInBoundsGEP(alloca_inst, &indices, indices.len, "");
4942 const store_inst = self.builder.buildStore(llvm_elem, field_ptr);4820 const store_inst = self.builder.buildStore(llvm_elem, field_ptr);
...@@ -4950,7 +4828,7 @@ pub const FuncGen = struct {...@@ -4950,7 +4828,7 @@ pub const FuncGen = struct {
4950 if (result_ty.structFieldValueComptime(i) != null) continue;4828 if (result_ty.structFieldValueComptime(i) != null) continue;
49514829
4952 const llvm_elem = try self.resolveInst(elem);4830 const llvm_elem = try self.resolveInst(elem);
4953 const llvm_i = self.dg.llvmFieldIndex(result_ty, i, &ptr_ty_buf).?;4831 const llvm_i = llvmFieldIndex(result_ty, i, target, &ptr_ty_buf).?;
4954 result = self.builder.buildInsertValue(result, llvm_elem, llvm_i, "");4832 result = self.builder.buildInsertValue(result, llvm_elem, llvm_i, "");
4955 }4833 }
4956 return result;4834 return result;
...@@ -4960,7 +4838,6 @@ pub const FuncGen = struct {...@@ -4960,7 +4838,6 @@ pub const FuncGen = struct {
4960 assert(isByRef(result_ty));4838 assert(isByRef(result_ty));
49614839
4962 const llvm_usize = try self.dg.llvmType(Type.usize);4840 const llvm_usize = try self.dg.llvmType(Type.usize);
4963 const target = self.dg.module.getTarget();
4964 const alloca_inst = self.buildAlloca(llvm_result_ty);4841 const alloca_inst = self.buildAlloca(llvm_result_ty);
4965 alloca_inst.setAlignment(result_ty.abiAlignment(target));4842 alloca_inst.setAlignment(result_ty.abiAlignment(target));
49664843
...@@ -5033,13 +4910,18 @@ pub const FuncGen = struct {...@@ -5033,13 +4910,18 @@ pub const FuncGen = struct {
5033 const fields: [1]*const llvm.Type = .{payload};4910 const fields: [1]*const llvm.Type = .{payload};
5034 break :t self.context.structType(&fields, fields.len, .False);4911 break :t self.context.structType(&fields, fields.len, .False);
5035 }4912 }
5036 var fields: [2]*const llvm.Type = undefined;4913 var fields: [3]*const llvm.Type = undefined;
4914 var fields_len: c_uint = 2;
5037 if (layout.tag_align >= layout.payload_align) {4915 if (layout.tag_align >= layout.payload_align) {
5038 fields = .{ tag_llvm_ty, payload };4916 fields = .{ tag_llvm_ty, payload, undefined };
5039 } else {4917 } else {
5040 fields = .{ payload, tag_llvm_ty };4918 fields = .{ payload, tag_llvm_ty, undefined };
4919 }
4920 if (layout.padding != 0) {
4921 fields[2] = self.context.intType(8).arrayType(layout.padding);
4922 fields_len = 3;
5041 }4923 }
5042 break :t self.context.structType(&fields, fields.len, .False);4924 break :t self.context.structType(&fields, fields_len, .False);
5043 };4925 };
50444926
5045 const casted_ptr = self.builder.buildBitCast(result_ptr, llvm_union_ty.pointerType(0), "");4927 const casted_ptr = self.builder.buildBitCast(result_ptr, llvm_union_ty.pointerType(0), "");
...@@ -5255,8 +5137,9 @@ pub const FuncGen = struct {...@@ -5255,8 +5137,9 @@ pub const FuncGen = struct {
5255 return self.builder.buildBitCast(struct_ptr, result_llvm_ty, "");5137 return self.builder.buildBitCast(struct_ptr, result_llvm_ty, "");
5256 },5138 },
5257 else => {5139 else => {
5140 const target = self.dg.module.getTarget();
5258 var ty_buf: Type.Payload.Pointer = undefined;5141 var ty_buf: Type.Payload.Pointer = undefined;
5259 if (self.dg.llvmFieldIndex(struct_ty, field_index, &ty_buf)) |llvm_field_index| {5142 if (llvmFieldIndex(struct_ty, field_index, target, &ty_buf)) |llvm_field_index| {
5260 return self.builder.buildStructGEP(struct_ptr, llvm_field_index, "");5143 return self.builder.buildStructGEP(struct_ptr, llvm_field_index, "");
5261 } else {5144 } else {
5262 // If we found no index then this means this is a zero sized field at the5145 // If we found no index then this means this is a zero sized field at the
...@@ -5667,6 +5550,88 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca...@@ -5667,6 +5550,88 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca
5667 };5550 };
5668}5551}
56695552
5553/// Take into account 0 bit fields and padding. Returns null if an llvm
5554/// field could not be found.
5555/// This only happens if you want the field index of a zero sized field at
5556/// the end of the struct.
5557fn llvmFieldIndex(
5558 ty: Type,
5559 field_index: usize,
5560 target: std.Target,
5561 ptr_pl_buf: *Type.Payload.Pointer,
5562) ?c_uint {
5563 // Detects where we inserted extra padding fields so that we can skip
5564 // over them in this function.
5565 comptime assert(struct_layout_version == 2);
5566 var offset: u64 = 0;
5567 var big_align: u32 = 0;
5568
5569 if (ty.isTupleOrAnonStruct()) {
5570 const tuple = ty.tupleFields();
5571 var llvm_field_index: c_uint = 0;
5572 for (tuple.types) |field_ty, i| {
5573 if (tuple.values[i].tag() != .unreachable_value) continue;
5574
5575 const field_align = field_ty.abiAlignment(target);
5576 big_align = @maximum(big_align, field_align);
5577 const prev_offset = offset;
5578 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
5579
5580 const padding_len = offset - prev_offset;
5581 if (padding_len > 0) {
5582 llvm_field_index += 1;
5583 }
5584
5585 if (field_index == i) {
5586 ptr_pl_buf.* = .{
5587 .data = .{
5588 .pointee_type = field_ty,
5589 .@"align" = field_align,
5590 .@"addrspace" = .generic,
5591 },
5592 };
5593 return llvm_field_index;
5594 }
5595
5596 llvm_field_index += 1;
5597 offset += field_ty.abiSize(target);
5598 }
5599 return null;
5600 }
5601 assert(ty.containerLayout() != .Packed);
5602
5603 var llvm_field_index: c_uint = 0;
5604 for (ty.structFields().values()) |field, i| {
5605 if (field.is_comptime or !field.ty.hasRuntimeBits()) continue;
5606
5607 const field_align = field.normalAlignment(target);
5608 big_align = @maximum(big_align, field_align);
5609 const prev_offset = offset;
5610 offset = std.mem.alignForwardGeneric(u64, offset, field_align);
5611
5612 const padding_len = offset - prev_offset;
5613 if (padding_len > 0) {
5614 llvm_field_index += 1;
5615 }
5616
5617 if (field_index == i) {
5618 ptr_pl_buf.* = .{
5619 .data = .{
5620 .pointee_type = field.ty,
5621 .@"align" = field_align,
5622 .@"addrspace" = .generic,
5623 },
5624 };
5625 return llvm_field_index;
5626 }
5627
5628 llvm_field_index += 1;
5629 offset += field.ty.abiSize(target);
5630 } else {
5631 // We did not find an llvm field that corresponds to this zig field.
5632 return null;
5633 }
5634}
5670fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {5635fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool {
5671 switch (fn_info.cc) {5636 switch (fn_info.cc) {
5672 .Unspecified, .Inline => return isByRef(fn_info.return_type),5637 .Unspecified, .Inline => return isByRef(fn_info.return_type),
...@@ -5757,3 +5722,12 @@ fn backendSupportsF80(target: std.Target) bool {...@@ -5757,3 +5722,12 @@ fn backendSupportsF80(target: std.Target) bool {
5757 else => false,5722 else => false,
5758 };5723 };
5759}5724}
5725
5726/// We need to insert extra padding if LLVM's isn't enough.
5727/// However we don't want to ever call LLVMABIAlignmentOfType or
5728/// LLVMABISizeOfType because these functions will trip assertions
5729/// when using them for self-referential types. So our strategy is
5730/// to use non-packed llvm structs but to emit all padding explicitly.
5731/// We can do this because for all types, Zig ABI alignment >= LLVM ABI
5732/// alignment.
5733const struct_layout_version = 2;
src/codegen/llvm/bindings.zig-6
...@@ -919,12 +919,6 @@ pub const TargetMachine = opaque {...@@ -919,12 +919,6 @@ pub const TargetMachine = opaque {
919pub const TargetData = opaque {919pub const TargetData = opaque {
920 pub const dispose = LLVMDisposeTargetData;920 pub const dispose = LLVMDisposeTargetData;
921 extern fn LLVMDisposeTargetData(*const TargetData) void;921 extern fn LLVMDisposeTargetData(*const TargetData) void;
922
923 pub const ABISizeOfType = LLVMABISizeOfType;
924 extern fn LLVMABISizeOfType(TD: *const TargetData, Ty: *const Type) c_ulonglong;
925
926 pub const ABIAlignmentOfType = LLVMABIAlignmentOfType;
927 extern fn LLVMABIAlignmentOfType(TD: *const TargetData, Ty: *const Type) c_uint;
928};922};
929923
930pub const CodeModel = enum(c_int) {924pub const CodeModel = enum(c_int) {