authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-07-15 08:44:30-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-07-27 14:38:37-04:00
log6aed49f6baad85fc4a509f4c631da8c623e7c28f
tree2dfb92be8edb34b8363d0a0d149fbcbd00e276da
parentb425e6869452ebeaf9b15ceb5739ce4b4bb5b87d

llvm.Builder: parse llvm alignment information from data layout

This is required to correctly lower types in the llvm backend.

3 files changed, 618 insertions(+), 236 deletions(-)

lib/std/zig/llvm/Builder.zig+547-16
...@@ -17,7 +17,7 @@ gpa: Allocator,...@@ -17,7 +17,7 @@ gpa: Allocator,
17strip: bool,17strip: bool,
1818
19source_filename: String,19source_filename: String,
20data_layout: String,20data_layout: DataLayout,
21target_triple: String,21target_triple: String,
22module_asm: std.ArrayList(u8),22module_asm: std.ArrayList(u8),
2323
...@@ -87,6 +87,455 @@ pub const Options = struct {...@@ -87,6 +87,455 @@ pub const Options = struct {
87 triple: []const u8 = &.{},87 triple: []const u8 = &.{},
88};88};
8989
90pub const DataLayout = struct {
91 endian: ?std.lang.Endian,
92 int_specs: PrimitiveSpec.Map,
93 float_specs: PrimitiveSpec.Map,
94 vector_specs: PrimitiveSpec.Map,
95 pointer_specs: PointerSpec.Map,
96 string_repr: String,
97
98 const PrimitiveSpec = packed struct(u32) {
99 bit_width: BitWidth,
100 abi_align: Alignment,
101 pref_align: Alignment,
102
103 const BitWidth = u20;
104
105 const Map = std.array_hash_map.Custom(PrimitiveSpec, void, Context, false);
106
107 const Context = struct {
108 pub fn hash(_: Context, spec: PrimitiveSpec) u32 {
109 return std.hash.int(spec.bit_width);
110 }
111
112 pub fn eql(_: Context, lhs_spec: PrimitiveSpec, rhs_spec: PrimitiveSpec, _: usize) bool {
113 return lhs_spec.bit_width == rhs_spec.bit_width;
114 }
115 };
116 };
117
118 const PointerSpec = struct {
119 bit_width: BitWidth,
120 index_bit_width: BitWidth,
121 flags: packed struct(u32) {
122 abi_align: Alignment,
123 pref_align: Alignment,
124 has_unstable_repr: bool,
125 has_external_state: bool,
126 null_ptr_repr: NullPtrRepr,
127 unused: u17 = 0,
128 },
129 addr_space_name: String,
130
131 const BitWidth = u32;
132
133 const NullPtrRepr = enum(u1) { all_zeros, all_ones };
134
135 const Map = std.array_hash_map.Auto(AddrSpace, PointerSpec);
136 };
137
138 pub fn stringForTarget(target: *const std.Target) []const u8 {
139 // These data layouts should match Clang.
140 return switch (target.cpu.arch) {
141 .arc => "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-f32:32:32-i64:32-f64:32-a:0:32-n32",
142 .xcore => "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:32-f64:32-a:0:32-n32",
143 .hexagon => "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32:32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32:32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048:2048:2048",
144 .lanai => "E-m:e-p:32:32-i64:64-a:0:32-n32-S64",
145 .aarch64 => if (target.ofmt == .macho)
146 if (target.os.tag == .windows or target.os.tag == .uefi)
147 "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
148 else if (target.abi == .ilp32)
149 "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
150 else
151 "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
152 else if (target.os.tag == .windows or target.os.tag == .uefi)
153 "e-m:w-p270:32:32-p271:32:32-p272:64:64-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32"
154 else
155 "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32",
156 .aarch64_be => "E-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32",
157 .arm => if (target.ofmt == .macho)
158 "e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
159 else
160 "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
161 .armeb, .thumbeb => if (target.ofmt == .macho)
162 "E-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
163 else
164 "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
165 .thumb => if (target.ofmt == .macho)
166 "e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
167 else if (target.os.tag == .windows or target.os.tag == .uefi)
168 "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
169 else
170 "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
171 .avr => "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8:16-a:8",
172 .bpfeb => "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
173 .bpfel => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
174 .msp430 => "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16",
175 .mips => "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64",
176 .mipsel => "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64",
177 .mips64 => switch (target.abi) {
178 .gnuabin32, .muslabin32, .abin32 => "E-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
179 else => "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
180 },
181 .mips64el => switch (target.abi) {
182 .gnuabin32, .muslabin32, .abin32 => "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
183 else => "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
184 },
185 .m68k => "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16",
186 .powerpc => "E-m:e-p:32:32-Fn32-i64:64-n32",
187 .powerpcle => "e-m:e-p:32:32-Fn32-i64:64-n32",
188 .powerpc64 => switch (target.os.tag) {
189 .linux => "E-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512",
190 .ps3 => "E-m:e-p:32:32-Fi64-i64:64-i128:128-n32:64",
191 else => "E-m:e-Fn32-i64:64-i128:128-n32:64",
192 },
193 .powerpc64le => if (target.os.tag == .linux)
194 "e-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512"
195 else
196 "e-m:e-Fn32-i64:64-i128:128-n32:64",
197 .nvptx => "e-p:32:32-p6:32:32-p7:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64",
198 .nvptx64 => "e-p6:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64",
199 .amdgcn => "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9",
200 .riscv32 => if (target.cpu.has(.riscv, .e))
201 "e-m:e-p:32:32-i64:64-n32-S32"
202 else
203 "e-m:e-p:32:32-i64:64-n32-S128",
204 .riscv32be => if (target.cpu.has(.riscv, .e))
205 "E-m:e-p:32:32-i64:64-n32-S32"
206 else
207 "E-m:e-p:32:32-i64:64-n32-S128",
208 .riscv64 => if (target.cpu.has(.riscv, .e))
209 "e-m:e-p:64:64-i64:64-i128:128-n32:64-S64"
210 else
211 "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
212 .riscv64be => if (target.cpu.has(.riscv, .e))
213 "E-m:e-p:64:64-i64:64-i128:128-n32:64-S64"
214 else
215 "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
216 .sparc => "E-m:e-p:32:32-i64:64-i128:128-f128:64-n32-S64",
217 .sparc64 => "E-m:e-i64:64-i128:128-n32:64-S128",
218 .s390x => "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64",
219 .x86 => if (target.os.tag == .windows or target.os.tag == .uefi) switch (target.abi) {
220 .gnu => if (target.ofmt == .coff)
221 "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32"
222 else
223 "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32",
224 else => blk: {
225 const msvc = switch (target.abi) {
226 .none, .msvc => true,
227 else => false,
228 };
229
230 break :blk if (target.ofmt == .coff)
231 if (msvc)
232 "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32"
233 else
234 "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32"
235 else if (msvc)
236 "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32"
237 else
238 "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32";
239 },
240 } else if (target.ofmt == .macho)
241 "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128"
242 else
243 "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
244 .x86_64 => if (target.os.tag.isDarwin() or target.ofmt == .macho)
245 "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
246 else switch (target.abi) {
247 .gnux32, .muslx32, .x32 => "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
248 else => if ((target.os.tag == .windows or target.os.tag == .uefi) and target.ofmt == .coff)
249 "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
250 else
251 "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
252 },
253 .spirv32 => switch (target.os.tag) {
254 .vulkan, .opengl => "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1",
255 else => "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1",
256 },
257 .spirv64 => "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1",
258 .wasm32 => if (target.os.tag == .emscripten)
259 "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-f128:64-n32:64-S128-ni:1:10:20"
260 else
261 "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20",
262 .wasm64 => if (target.os.tag == .emscripten)
263 "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-f128:64-n32:64-S128-ni:1:10:20"
264 else
265 "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20",
266 .ve => "e-m:e-i64:64-n32:64-S128-v64:64:64-v128:64:64-v256:64:64-v512:64:64-v1024:64:64-v2048:64:64-v4096:64:64-v8192:64:64-v16384:64:64",
267 .csky => "e-m:e-S32-p:32:32-i32:32:32-i64:32:32-f32:32:32-f64:32:32-v64:32:32-v128:32:32-a:0:32-Fi32-n32",
268 .loongarch32 => "e-m:e-p:32:32-i64:64-n32-S128",
269 .loongarch64 => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
270 .xtensa => "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32",
271
272 .alpha,
273 .arceb,
274 .ez80,
275 .hppa,
276 .hppa64,
277 .kalimba,
278 .kvx,
279 .m88k,
280 .microblaze,
281 .microblazeel,
282 .or1k,
283 .propeller,
284 .sh,
285 .sheb,
286 .x86_16,
287 .xtensaeb,
288 => unreachable,
289 };
290 }
291
292 const default_int_specs: []const PrimitiveSpec = &.{
293 .{ .bit_width = 8, .abi_align = .fromByteUnits(1), .pref_align = .fromByteUnits(1) }, // i8:8:8
294 .{ .bit_width = 16, .abi_align = .fromByteUnits(2), .pref_align = .fromByteUnits(2) }, // i16:16:16
295 .{ .bit_width = 32, .abi_align = .fromByteUnits(4), .pref_align = .fromByteUnits(4) }, // i32:32:32
296 .{ .bit_width = 64, .abi_align = .fromByteUnits(4), .pref_align = .fromByteUnits(8) }, // i64:32:64
297 };
298 const default_float_specs: []const PrimitiveSpec = &.{
299 .{ .bit_width = 16, .abi_align = .fromByteUnits(2), .pref_align = .fromByteUnits(2) }, // f16:16:16
300 .{ .bit_width = 32, .abi_align = .fromByteUnits(4), .pref_align = .fromByteUnits(4) }, // f32:32:32
301 .{ .bit_width = 64, .abi_align = .fromByteUnits(8), .pref_align = .fromByteUnits(8) }, // f64:64:64
302 .{ .bit_width = 128, .abi_align = .fromByteUnits(16), .pref_align = .fromByteUnits(16) }, // f128:128:128
303 };
304 const default_vector_specs: []const PrimitiveSpec = &.{
305 .{ .bit_width = 64, .abi_align = .fromByteUnits(8), .pref_align = .fromByteUnits(8) }, // v64:64:64
306 .{ .bit_width = 128, .abi_align = .fromByteUnits(16), .pref_align = .fromByteUnits(16) }, // v128:128:128
307 };
308
309 pub fn parseString(string_repr: String, builder: *Builder) Allocator.Error!DataLayout {
310 const gpa = builder.gpa;
311
312 var int_specs: PrimitiveSpec.Map = .empty;
313 defer int_specs.deinit(gpa);
314 var float_specs: PrimitiveSpec.Map = .empty;
315 defer float_specs.deinit(gpa);
316 var vector_specs: PrimitiveSpec.Map = .empty;
317 defer vector_specs.deinit(gpa);
318 var pointer_specs: PointerSpec.Map = .empty;
319 defer pointer_specs.deinit(gpa);
320 var non_integral_addr_spaces: std.ArrayList(AddrSpace) = .empty;
321 defer non_integral_addr_spaces.deinit(gpa);
322
323 try int_specs.ensureTotalCapacity(gpa, default_int_specs.len);
324 for (default_int_specs) |int_spec| int_specs.putAssumeCapacityNoClobber(int_spec, {});
325 try float_specs.ensureTotalCapacity(gpa, default_float_specs.len);
326 for (default_float_specs) |float_spec| float_specs.putAssumeCapacityNoClobber(float_spec, {});
327 try vector_specs.ensureTotalCapacity(gpa, default_vector_specs.len);
328 for (default_vector_specs) |vector_spec| vector_specs.putAssumeCapacityNoClobber(vector_spec, {});
329 try pointer_specs.putNoClobber(gpa, .default, comptime .{
330 .bit_width = 64,
331 .index_bit_width = 64,
332 .flags = .{
333 .abi_align = .fromByteUnits(8),
334 .pref_align = .fromByteUnits(8),
335 .has_unstable_repr = false,
336 .has_external_state = false,
337 .null_ptr_repr = .all_zeros,
338 },
339 .addr_space_name = .none,
340 });
341
342 var endian: ?std.lang.Endian = null;
343 var spec_it = std.mem.splitScalar(u8, string_repr.slice(builder).?, '-');
344 while (spec_it.next()) |spec| switch (spec[0]) {
345 else => {},
346 'E' => {
347 assert(spec.len == 1);
348 assert(endian == null);
349 endian = .big;
350 },
351 'e' => {
352 assert(spec.len == 1);
353 assert(endian == null);
354 endian = .little;
355 },
356 'p' => {
357 var field_it = std.mem.splitScalar(u8, spec[1..], ':');
358
359 const first = field_it.first();
360 var has_unstable_repr = false;
361 var has_external_state = false;
362 var null_ptr_repr: ?PointerSpec.NullPtrRepr = null;
363 var addr_space_name: String = .none;
364 const addr_space = for (first, 0..) |flag, as_start| switch (flag) {
365 'u' => has_unstable_repr = true,
366 'e' => has_external_state = true,
367 'z' => {
368 assert(null_ptr_repr == null);
369 null_ptr_repr = .all_zeros;
370 },
371 'o' => {
372 assert(null_ptr_repr == null);
373 null_ptr_repr = .all_ones;
374 },
375 else => {
376 if (first[first.len - ")".len] != ')') break first[as_start..];
377 const name_start = std.mem.findScalarPos(u8, first, as_start, '(').?;
378 addr_space_name = try builder.string(first[name_start + "(".len .. first.len - ")".len]);
379 break first[as_start..name_start];
380 },
381 } else first[first.len..];
382 const bit_width = std.fmt.parseInt(PointerSpec.BitWidth, field_it.next().?, 10) catch unreachable;
383 const abi_align: Alignment = .fromByteUnits(std.fmt.parseInt(u64, field_it.next().?, 10) catch unreachable);
384 const pref_align: Alignment = if (field_it.next()) |pref_align|
385 .fromByteUnits(std.fmt.parseInt(u64, pref_align, 10) catch unreachable)
386 else
387 abi_align;
388 const index_bit_width = if (field_it.next()) |index_bit_width|
389 std.fmt.parseInt(PointerSpec.BitWidth, index_bit_width, 10) catch unreachable
390 else
391 bit_width;
392 assert(field_it.peek() == null);
393
394 try pointer_specs.put(gpa, switch (addr_space.len) {
395 0 => .default,
396 else => @fromBackingInt(std.fmt.parseInt(u24, addr_space, 10) catch unreachable),
397 }, .{
398 .bit_width = bit_width,
399 .index_bit_width = index_bit_width,
400 .flags = .{
401 .abi_align = abi_align,
402 .pref_align = pref_align,
403 .has_unstable_repr = has_unstable_repr,
404 .has_external_state = has_external_state,
405 .null_ptr_repr = null_ptr_repr orelse .all_zeros,
406 },
407 .addr_space_name = addr_space_name,
408 });
409 },
410 'i', 'f', 'v' => |kind| {
411 if (std.mem.eql(u8, spec, "ve")) {
412 vector_specs.clearRetainingCapacity();
413 continue;
414 }
415 var field_it = std.mem.splitScalar(u8, spec[1..], ':');
416 const bit_width = std.fmt.parseInt(PrimitiveSpec.BitWidth, field_it.first(), 10) catch unreachable;
417 const abi_align: Alignment = .fromByteUnits(std.fmt.parseInt(u64, field_it.next().?, 10) catch unreachable);
418 const pref_align: Alignment = if (field_it.next()) |pref_align|
419 .fromByteUnits(std.fmt.parseInt(u64, pref_align, 10) catch unreachable)
420 else
421 abi_align;
422 assert(field_it.peek() == null);
423 const specs = switch (kind) {
424 else => unreachable,
425 'i' => &int_specs,
426 'f' => &float_specs,
427 'v' => &vector_specs,
428 };
429 try specs.put(gpa, .{ .bit_width = bit_width, .abi_align = abi_align, .pref_align = pref_align }, {});
430 },
431 'n' => {
432 var field_it = std.mem.splitScalar(u8, spec[1..], ':');
433 if (std.mem.eql(u8, field_it.first(), "i")) {
434 while (field_it.next()) |non_integral_addr_space| try non_integral_addr_spaces.append(
435 gpa,
436 @fromBackingInt(std.fmt.parseInt(u24, non_integral_addr_space, 10) catch unreachable),
437 );
438 } else {
439 field_it.reset();
440 while (field_it.next()) |native_bit_width| {
441 _ = std.fmt.parseInt(PrimitiveSpec.BitWidth, native_bit_width, 10) catch unreachable;
442 }
443 }
444 },
445 };
446
447 for (non_integral_addr_spaces.items) |non_integral_addr_space| {
448 const pointer_spec_gop = try pointer_specs.getOrPut(gpa, non_integral_addr_space);
449 if (!pointer_spec_gop.found_existing) pointer_spec_gop.value_ptr.* = pointer_specs.get(.default).?;
450 pointer_spec_gop.value_ptr.flags.has_unstable_repr = true;
451 pointer_spec_gop.value_ptr.flags.has_external_state = false;
452 }
453
454 {
455 const SortContext = struct {
456 specs: []const PrimitiveSpec,
457 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
458 return ctx.specs[lhs_index].bit_width < ctx.specs[rhs_index].bit_width;
459 }
460 };
461 int_specs.sortUnstable(SortContext{ .specs = int_specs.keys() });
462 float_specs.sortUnstable(SortContext{ .specs = float_specs.keys() });
463 vector_specs.sortUnstable(SortContext{ .specs = vector_specs.keys() });
464 }
465 {
466 const SortContext = struct {
467 addr_spaces: []const AddrSpace,
468 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
469 return @backingInt(ctx.addr_spaces[lhs_index]) < @backingInt(ctx.addr_spaces[rhs_index]);
470 }
471 };
472 pointer_specs.sortUnstable(SortContext{ .addr_spaces = pointer_specs.keys() });
473 assert(pointer_specs.keys()[0] == .default);
474 }
475
476 return .{
477 .endian = endian,
478 .int_specs = int_specs.move(),
479 .float_specs = float_specs.move(),
480 .vector_specs = vector_specs.move(),
481 .pointer_specs = pointer_specs.move(),
482 .string_repr = string_repr,
483 };
484 }
485
486 pub fn deinit(data_layout: *DataLayout, gpa: Allocator) void {
487 data_layout.int_specs.deinit(gpa);
488 data_layout.float_specs.deinit(gpa);
489 data_layout.vector_specs.deinit(gpa);
490 data_layout.pointer_specs.deinit(gpa);
491 }
492
493 pub fn getIntegerSpec(data_layout: *const DataLayout, bit_width: PrimitiveSpec.BitWidth) PrimitiveSpec {
494 const specs = data_layout.int_specs.keys();
495 return specs[
496 @min(std.sort.lowerBound(PrimitiveSpec, specs, bit_width, struct {
497 fn order(ctx: PrimitiveSpec.BitWidth, spec: PrimitiveSpec) std.math.Order {
498 return std.math.order(ctx, spec.bit_width);
499 }
500 }.order), specs.len - 1)
501 ];
502 }
503
504 pub fn getFloatSpec(data_layout: *const DataLayout, bit_width: PrimitiveSpec.BitWidth) PrimitiveSpec {
505 if (data_layout.float_specs.getEntry(.{
506 .bit_width = bit_width,
507 .abi_align = .default,
508 .pref_align = .default,
509 })) |entry| return entry.key_ptr.*;
510 const default_align: Alignment = .fromByteUnits(
511 std.math.ceilPowerOfTwoAssert(PrimitiveSpec.BitWidth, bit_width / 8),
512 );
513 return .{ .bit_width = bit_width, .abi_align = default_align, .pref_align = default_align };
514 }
515
516 pub fn getVectorSpec(
517 data_layout: *const DataLayout,
518 bit_width: PrimitiveSpec.BitWidth,
519 store_size: Type.Size,
520 ) PrimitiveSpec {
521 if (data_layout.float_specs.getEntry(.{
522 .bit_width = bit_width,
523 .abi_align = .default,
524 .pref_align = .default,
525 })) |entry| return entry.key_ptr.*;
526 const default_align: Alignment = .fromByteUnits(
527 std.math.ceilPowerOfTwoAssert(PrimitiveSpec.BitWidth, switch (store_size) {
528 .fixed, .scalable => |known_min| known_min,
529 }),
530 );
531 return .{ .bit_width = bit_width, .abi_align = default_align, .pref_align = default_align };
532 }
533
534 pub fn getPointerSpec(data_layout: *const DataLayout, addr_space: AddrSpace) PointerSpec {
535 return data_layout.pointer_specs.get(addr_space) orelse data_layout.pointer_specs.values()[0];
536 }
537};
538
90pub const String = enum(u32) {539pub const String = enum(u32) {
91 none = maxInt(u31),540 none = maxInt(u31),
92 empty,541 empty,
...@@ -489,7 +938,10 @@ pub const Type = enum(u32) {...@@ -489,7 +938,10 @@ pub const Type = enum(u32) {
489 .double, .i64, .x86_mmx => 64,938 .double, .i64, .x86_mmx => 64,
490 .x86_fp80, .i80 => 80,939 .x86_fp80, .i80 => 80,
491 .fp128, .ppc_fp128, .i128 => 128,940 .fp128, .ppc_fp128, .i128 => 128,
492 .ptr, .@"ptr addrspace(4)" => @panic("TODO: query data layout"),941 .ptr => @intCast(builder.data_layout.getPointerSpec(.default).bit_width),
942 .@"ptr addrspace(4)" => @intCast(
943 builder.data_layout.getPointerSpec(@fromBackingInt(@intCast(4))).bit_width,
944 ),
493 _ => {945 _ => {
494 const item = builder.type_items.items[@backingInt(self)];946 const item = builder.type_items.items[@backingInt(self)];
495 return switch (item.tag) {947 return switch (item.tag) {
...@@ -498,7 +950,9 @@ pub const Type = enum(u32) {...@@ -498,7 +950,9 @@ pub const Type = enum(u32) {
498 .vararg_function,950 .vararg_function,
499 => unreachable,951 => unreachable,
500 .integer => @intCast(item.data),952 .integer => @intCast(item.data),
501 .pointer => @panic("TODO: query data layout"),953 .pointer => @intCast(
954 builder.data_layout.getPointerSpec(@fromBackingInt(@intCast(item.data))).bit_width,
955 ),
502 .target => unreachable,956 .target => unreachable,
503 .vector,957 .vector,
504 .scalable_vector,958 .scalable_vector,
...@@ -931,6 +1385,67 @@ pub const Type = enum(u32) {...@@ -931,6 +1385,67 @@ pub const Type = enum(u32) {
931 },1385 },
932 };1386 };
933 }1387 }
1388
1389 const Size = union(enum) { fixed: u64, scalable: u64 };
1390 pub fn bits(ty: Type, builder: *const Builder) Size {
1391 const item = builder.type_items.items[@backingInt(ty)];
1392 return switch (item.tag) {
1393 else => unreachable,
1394 .simple => switch (@as(Simple, @fromBackingInt(@intCast(item.data)))) {
1395 else => unreachable,
1396 .label => .{ .fixed = builder.data_layout.getPointerSpec(.default).bit_width },
1397 .half, .bfloat => .{ .fixed = 16 },
1398 .float => .{ .fixed = 32 },
1399 .double => .{ .fixed = 64 },
1400 .ppc_fp128, .fp128 => .{ .fixed = 128 },
1401 .x86_amx => .{ .fixed = 8192 },
1402 .x86_fp80 => .{ .fixed = 80 },
1403 },
1404 .integer => .{ .fixed = item.data },
1405 .pointer => .{
1406 .fixed = builder.data_layout.getPointerSpec(@fromBackingInt(@intCast(item.data))).bit_width,
1407 },
1408 };
1409 }
1410
1411 pub fn alignment(ty: Type, kind: enum { abi, pref }, builder: *const Builder) Alignment {
1412 const item = builder.type_items.items[@backingInt(ty)];
1413 switch (item.tag) {
1414 else => unreachable,
1415 .simple => switch (@as(Simple, @fromBackingInt(@intCast(item.data)))) {
1416 else => unreachable,
1417 .label => {
1418 const spec = builder.data_layout.getPointerSpec(.default);
1419 return switch (kind) {
1420 .abi => spec.flags.abi_align,
1421 .pref => spec.flags.pref_align,
1422 };
1423 },
1424 .half, .bfloat, .float, .double, .ppc_fp128, .fp128, .x86_fp80 => {
1425 const spec = builder.data_layout.getFloatSpec(@intCast(ty.bits(builder).fixed));
1426 return switch (kind) {
1427 .abi => spec.abi_align,
1428 .pref => spec.pref_align,
1429 };
1430 },
1431 .x86_amx => return comptime .fromByteUnits(64),
1432 },
1433 .integer => {
1434 const spec = builder.data_layout.getIntegerSpec(@intCast(item.data));
1435 return switch (kind) {
1436 .abi => spec.abi_align,
1437 .pref => spec.pref_align,
1438 };
1439 },
1440 .pointer => {
1441 const spec = builder.data_layout.getPointerSpec(@fromBackingInt(@intCast(item.data)));
1442 return switch (kind) {
1443 .abi => spec.flags.abi_align,
1444 .pref => spec.flags.pref_align,
1445 };
1446 },
1447 }
1448 }
934};1449};
9351450
936pub const Attribute = union(Kind) {1451pub const Attribute = union(Kind) {
...@@ -2182,11 +2697,18 @@ pub const Alignment = enum(u6) {...@@ -2182,11 +2697,18 @@ pub const Alignment = enum(u6) {
2182 };2697 };
2183 }2698 }
21842699
2185 /// Asserts that neither `a` nor `b` is `.default`.2700 /// Asserts that neither `lhs` nor `rhs` is `.default`.
2186 pub fn max(a: Alignment, b: Alignment) Alignment {2701 pub fn max(lhs: Alignment, rhs: Alignment) Alignment {
2187 assert(a != .default);2702 assert(lhs != .default);
2188 assert(b != .default);2703 assert(rhs != .default);
2189 return @fromBackingInt(@intCast(@max(@backingInt(a), @backingInt(b))));2704 return @fromBackingInt(@max(@backingInt(lhs), @backingInt(rhs)));
2705 }
2706
2707 /// Asserts that neither `lhs` nor `rhs` is `.default`.
2708 pub fn order(lhs: Alignment, rhs: Alignment) std.math.Order {
2709 assert(lhs != .default);
2710 assert(rhs != .default);
2711 return std.math.order(@backingInt(lhs), @backingInt(rhs));
2190 }2712 }
21912713
2192 pub fn toLlvm(self: Alignment) u6 {2714 pub fn toLlvm(self: Alignment) u6 {
...@@ -9023,7 +9545,14 @@ pub fn init(options: Options) Allocator.Error!Builder {...@@ -9023,7 +9545,14 @@ pub fn init(options: Options) Allocator.Error!Builder {
9023 .strip = options.strip,9545 .strip = options.strip,
90249546
9025 .source_filename = .none,9547 .source_filename = .none,
9026 .data_layout = .none,9548 .data_layout = .{
9549 .endian = null,
9550 .int_specs = .empty,
9551 .float_specs = .empty,
9552 .vector_specs = .empty,
9553 .pointer_specs = .empty,
9554 .string_repr = .none,
9555 },
9027 .target_triple = .none,9556 .target_triple = .none,
9028 .module_asm = .empty,9557 .module_asm = .empty,
90299558
...@@ -9079,14 +9608,14 @@ pub fn init(options: Options) Allocator.Error!Builder {...@@ -9079,14 +9608,14 @@ pub fn init(options: Options) Allocator.Error!Builder {
9079 try self.string_indices.append(self.gpa, 0);9608 try self.string_indices.append(self.gpa, 0);
9080 assert(try self.string("") == .empty);9609 assert(try self.string("") == .empty);
90819610
9611 self.data_layout = try .parseString(try self.string(DataLayout.stringForTarget(options.target)), &self);
9612
9082 try self.strtab_string_indices.append(self.gpa, 0);9613 try self.strtab_string_indices.append(self.gpa, 0);
9083 assert(try self.strtabString("") == .empty);9614 assert(try self.strtabString("") == .empty);
90849615
9085 if (options.name.len > 0) self.source_filename = try self.string(options.name);9616 if (options.name.len > 0) self.source_filename = try self.string(options.name);
90869617
9087 if (options.triple.len > 0) {9618 if (options.triple.len > 0) self.target_triple = try self.string(options.triple);
9088 self.target_triple = try self.string(options.triple);
9089 }
90909619
9091 {9620 {
9092 const static_len = @typeInfo(Type).@"enum".field_names.len - 1;9621 const static_len = @typeInfo(Type).@"enum".field_names.len - 1;
...@@ -9179,6 +9708,8 @@ pub fn clearAndFree(self: *Builder) void {...@@ -9179,6 +9708,8 @@ pub fn clearAndFree(self: *Builder) void {
9179pub fn deinit(self: *Builder) void {9708pub fn deinit(self: *Builder) void {
9180 const gpa = self.gpa;9709 const gpa = self.gpa;
91819710
9711 self.data_layout.deinit(gpa);
9712
9182 self.module_asm.deinit(gpa);9713 self.module_asm.deinit(gpa);
91839714
9184 self.string_map.deinit(gpa);9715 self.string_map.deinit(gpa);
...@@ -9998,17 +10529,17 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void...@@ -9998,17 +10529,17 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void
9998 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };10529 var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined };
9999 defer metadata_formatter.map.deinit(self.gpa);10530 defer metadata_formatter.map.deinit(self.gpa);
1000010531
10001 if (self.source_filename != .none or self.data_layout != .none or self.target_triple != .none) {10532 if (self.source_filename != .none or self.data_layout.string_repr != .none or self.target_triple != .none) {
10002 if (need_newline) try w.writeByte('\n') else need_newline = true;10533 if (need_newline) try w.writeByte('\n') else need_newline = true;
10003 if (self.source_filename != .none) try w.print(10534 if (self.source_filename != .none) try w.print(
10004 \\; ModuleID = '{s}'10535 \\; ModuleID = '{s}'
10005 \\source_filename = {f}10536 \\source_filename = {f}
10006 \\10537 \\
10007 , .{ self.source_filename.slice(self).?, self.source_filename.fmtQ(self) });10538 , .{ self.source_filename.slice(self).?, self.source_filename.fmtQ(self) });
10008 if (self.data_layout != .none) try w.print(10539 if (self.data_layout.string_repr != .none) try w.print(
10009 \\target datalayout = {f}10540 \\target datalayout = {f}
10010 \\10541 \\
10011 , .{self.data_layout.fmtQ(self)});10542 , .{self.data_layout.string_repr.fmtQ(self)});
10012 if (self.target_triple != .none) try w.print(10543 if (self.target_triple != .none) try w.print(
10013 \\target triple = {f}10544 \\target triple = {f}
10014 \\10545 \\
...@@ -13706,7 +14237,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco...@@ -13706,7 +14237,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco
13706 });14237 });
13707 }14238 }
1370814239
13709 if (self.data_layout.slice(self)) |data_layout| {14240 if (self.data_layout.string_repr.slice(self)) |data_layout| {
13710 try module_block.writeAbbrev(ModuleBlock.String{14241 try module_block.writeAbbrev(ModuleBlock.String{
13711 .code = 3,14242 .code = 3,
13712 .string = data_layout,14243 .string = data_layout,
src/codegen/llvm.zig+62-208
...@@ -345,160 +345,6 @@ pub fn supportsTailCall(target: *const std.Target) bool {...@@ -345,160 +345,6 @@ pub fn supportsTailCall(target: *const std.Target) bool {
345 };345 };
346}346}
347347
348pub fn dataLayout(target: *const std.Target) []const u8 {
349 // These data layouts should match Clang.
350 return switch (target.cpu.arch) {
351 .arc => "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-f32:32:32-i64:32-f64:32-a:0:32-n32",
352 .xcore => "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:32-f64:32-a:0:32-n32",
353 .hexagon => "e-m:e-p:32:32:32-a:0-n16:32-i64:64:64-i32:32:32-i16:16:16-i1:8:8-f32:32:32-f64:64:64-v32:32:32-v64:64:64-v512:512:512-v1024:1024:1024-v2048:2048:2048",
354 .lanai => "E-m:e-p:32:32-i64:64-a:0:32-n32-S64",
355 .aarch64 => if (target.ofmt == .macho)
356 if (target.os.tag == .windows or target.os.tag == .uefi)
357 "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
358 else if (target.abi == .ilp32)
359 "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
360 else
361 "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
362 else if (target.os.tag == .windows or target.os.tag == .uefi)
363 "e-m:w-p270:32:32-p271:32:32-p272:64:64-p:64:64-i32:32-i64:64-i128:128-n32:64-S128-Fn32"
364 else
365 "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32",
366 .aarch64_be => "E-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32",
367 .arm => if (target.ofmt == .macho)
368 "e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
369 else
370 "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
371 .armeb, .thumbeb => if (target.ofmt == .macho)
372 "E-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
373 else
374 "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
375 .thumb => if (target.ofmt == .macho)
376 "e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
377 else if (target.os.tag == .windows or target.os.tag == .uefi)
378 "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"
379 else
380 "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64",
381 .avr => "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8:16-a:8",
382 .bpfeb => "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
383 .bpfel => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
384 .msp430 => "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16",
385 .mips => "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64",
386 .mipsel => "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64",
387 .mips64 => switch (target.abi) {
388 .gnuabin32, .muslabin32, .abin32 => "E-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
389 else => "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
390 },
391 .mips64el => switch (target.abi) {
392 .gnuabin32, .muslabin32, .abin32 => "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
393 else => "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
394 },
395 .m68k => "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16",
396 .powerpc => "E-m:e-p:32:32-Fn32-i64:64-n32",
397 .powerpcle => "e-m:e-p:32:32-Fn32-i64:64-n32",
398 .powerpc64 => switch (target.os.tag) {
399 .linux => "E-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512",
400 .ps3 => "E-m:e-p:32:32-Fi64-i64:64-i128:128-n32:64",
401 else => "E-m:e-Fn32-i64:64-i128:128-n32:64",
402 },
403 .powerpc64le => if (target.os.tag == .linux)
404 "e-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512"
405 else
406 "e-m:e-Fn32-i64:64-i128:128-n32:64",
407 .nvptx => "e-p:32:32-p6:32:32-p7:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64",
408 .nvptx64 => "e-p6:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64",
409 .amdgcn => "e-m:e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128:128:48-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9",
410 .riscv32 => if (target.cpu.has(.riscv, .e))
411 "e-m:e-p:32:32-i64:64-n32-S32"
412 else
413 "e-m:e-p:32:32-i64:64-n32-S128",
414 .riscv32be => if (target.cpu.has(.riscv, .e))
415 "E-m:e-p:32:32-i64:64-n32-S32"
416 else
417 "E-m:e-p:32:32-i64:64-n32-S128",
418 .riscv64 => if (target.cpu.has(.riscv, .e))
419 "e-m:e-p:64:64-i64:64-i128:128-n32:64-S64"
420 else
421 "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
422 .riscv64be => if (target.cpu.has(.riscv, .e))
423 "E-m:e-p:64:64-i64:64-i128:128-n32:64-S64"
424 else
425 "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
426 .sparc => "E-m:e-p:32:32-i64:64-i128:128-f128:64-n32-S64",
427 .sparc64 => "E-m:e-i64:64-i128:128-n32:64-S128",
428 .s390x => "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64",
429 .x86 => if (target.os.tag == .windows or target.os.tag == .uefi) switch (target.abi) {
430 .gnu => if (target.ofmt == .coff)
431 "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32"
432 else
433 "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32",
434 else => blk: {
435 const msvc = switch (target.abi) {
436 .none, .msvc => true,
437 else => false,
438 };
439
440 break :blk if (target.ofmt == .coff)
441 if (msvc)
442 "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32"
443 else
444 "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32"
445 else if (msvc)
446 "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32-a:0:32-S32"
447 else
448 "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:32-n8:16:32-a:0:32-S32";
449 },
450 } else if (target.ofmt == .macho)
451 "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128"
452 else
453 "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
454 .x86_64 => if (target.os.tag.isDarwin() or target.ofmt == .macho)
455 "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
456 else switch (target.abi) {
457 .gnux32, .muslx32, .x32 => "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
458 else => if ((target.os.tag == .windows or target.os.tag == .uefi) and target.ofmt == .coff)
459 "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
460 else
461 "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
462 },
463 .spirv32 => switch (target.os.tag) {
464 .vulkan, .opengl => "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1",
465 else => "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1",
466 },
467 .spirv64 => "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1",
468 .wasm32 => if (target.os.tag == .emscripten)
469 "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-f128:64-n32:64-S128-ni:1:10:20"
470 else
471 "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20",
472 .wasm64 => if (target.os.tag == .emscripten)
473 "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-f128:64-n32:64-S128-ni:1:10:20"
474 else
475 "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20",
476 .ve => "e-m:e-i64:64-n32:64-S128-v64:64:64-v128:64:64-v256:64:64-v512:64:64-v1024:64:64-v2048:64:64-v4096:64:64-v8192:64:64-v16384:64:64",
477 .csky => "e-m:e-S32-p:32:32-i32:32:32-i64:32:32-f32:32:32-f64:32:32-v64:32:32-v128:32:32-a:0:32-Fi32-n32",
478 .loongarch32 => "e-m:e-p:32:32-i64:64-n32-S128",
479 .loongarch64 => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
480 .xtensa => "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32",
481
482 .alpha,
483 .arceb,
484 .ez80,
485 .hppa,
486 .hppa64,
487 .kalimba,
488 .kvx,
489 .m88k,
490 .microblaze,
491 .microblazeel,
492 .or1k,
493 .propeller,
494 .sh,
495 .sheb,
496 .x86_16,
497 .xtensaeb,
498 => unreachable, // Gated by hasLlvmSupport().
499 };
500}
501
502// Avoid depending on `bindings.CodeModel` in the bitcode-only case.348// Avoid depending on `bindings.CodeModel` in the bitcode-only case.
503const CodeModel = enum {349const CodeModel = enum {
504 default,350 default,
...@@ -618,8 +464,6 @@ pub const Object = struct {...@@ -618,8 +464,6 @@ pub const Object = struct {
618 });464 });
619 errdefer builder.deinit();465 errdefer builder.deinit();
620466
621 builder.data_layout = try builder.string(dataLayout(target));
622
623 const debug_compile_unit, const debug_enums_fwd_ref, const debug_globals_fwd_ref =467 const debug_compile_unit, const debug_enums_fwd_ref, const debug_globals_fwd_ref =
624 if (!builder.strip) debug_info: {468 if (!builder.strip) debug_info: {
625 // We fully resolve all paths at this point to avoid lack of469 // We fully resolve all paths at this point to avoid lack of
...@@ -2865,18 +2709,38 @@ pub const Object = struct {...@@ -2865,18 +2709,38 @@ pub const Object = struct {
28652709
2866 pub const TypeRepr = enum {2710 pub const TypeRepr = enum {
2867 /// The representation of the type when it is being manipulated as a value in a function.2711 /// The representation of the type when it is being manipulated as a value in a function.
2868 /// e.g. Zig `u5` -> LLVM `i5`2712 /// e.g. Zig `u90` -> LLVM `i90`
2869 as_value,2713 as_value,
2870 /// The representation of the type when it is stored in memory.2714 /// The representation of the type when it is loaded from or stored to memory.
2871 /// e.g. Zig `u5` -> LLVM `i8`2715 /// e.g. Zig `u90` -> LLVM `i96`
2716 memory_access,
2717 /// The representation of the type when it is in memory.
2718 /// e.g. Zig `u90` -> LLVM `[12 x i8]`
2872 in_memory,2719 in_memory,
2873 };2720 };
28742721
2722 pub fn intType(o: *Object, bits: u16, repr: TypeRepr) Allocator.Error!Builder.Type {
2723 switch (repr) {
2724 .as_value => return o.builder.intType(bits),
2725 .memory_access, .in_memory => {},
2726 }
2727 const target = o.zcu.getTarget();
2728 const abi_size = std.zig.target.intByteSize(target, bits);
2729 const llvm_bit_width = @as(u20, 8) * abi_size;
2730 switch (repr) {
2731 .as_value => unreachable,
2732 .memory_access => {},
2733 .in_memory => {
2734 const zig_align = std.zig.target.intAlignment(target, bits);
2735 const llvm_align = o.builder.data_layout.getIntegerSpec(llvm_bit_width).abi_align;
2736 if (zig_align < llvm_align.toByteUnits().?) return o.builder.arrayType(abi_size, .i8);
2737 },
2738 }
2739 return o.builder.intType(llvm_bit_width);
2740 }
2741
2875 pub fn errorIntType(o: *Object, repr: TypeRepr) Allocator.Error!Builder.Type {2742 pub fn errorIntType(o: *Object, repr: TypeRepr) Allocator.Error!Builder.Type {
2876 return o.builder.intType(switch (repr) {2743 return o.intType(o.zcu.errorSetBits(), repr);
2877 .as_value => o.zcu.errorSetBits(),
2878 .in_memory => @intCast(Type.anyerror.abiSize(o.zcu) * 8),
2879 });
2880 }2744 }
28812745
2882 pub const SoftF80Layout = struct {2746 pub const SoftF80Layout = struct {
...@@ -3029,42 +2893,31 @@ pub const Object = struct {...@@ -3029,42 +2893,31 @@ pub const Object = struct {
3029 const target = zcu.getTarget();2893 const target = zcu.getTarget();
3030 const ip = &zcu.intern_pool;2894 const ip = &zcu.intern_pool;
30312895
3032 if (repr == .as_value) {2896 switch (repr) {
3033 assert(!isByRef(t, zcu)); // by-ref types must only be manipulated in memory2897 .as_value => assert(!isByRef(t, zcu)), // by-ref types must only be manipulated in memory
2898 .memory_access, .in_memory => {},
3034 }2899 }
30352900
3036 return switch (t.toIntern()) {2901 return switch (t.toIntern()) {
3037 .u0_type => unreachable, // no runtime bits2902 .u0_type => unreachable, // no runtime bits
3038 inline .u1_type,2903 .u1_type => try o.intType(1, repr),
3039 .u8_type,2904 .u8_type, .i8_type => try o.intType(8, repr),
3040 .i8_type,2905 .u16_type, .i16_type => try o.intType(16, repr),
3041 .u16_type,2906 .u29_type => try o.intType(29, repr),
3042 .i16_type,2907 .u32_type, .i32_type => try o.intType(32, repr),
3043 .u29_type,2908 .u64_type, .i64_type => try o.intType(64, repr),
3044 .u32_type,2909 .u80_type => try o.intType(80, repr),
3045 .i32_type,2910 .u128_type, .i128_type => try o.intType(128, repr),
3046 .u64_type,2911 .usize_type, .isize_type => try o.intType(target.ptrBitWidth(), repr),
3047 .i64_type,2912 .c_char_type => try o.intType(target.cTypeBitSize(.char).?, repr),
3048 .u80_type,2913 .c_short_type => try o.intType(target.cTypeBitSize(.short).?, repr),
3049 .u128_type,2914 .c_ushort_type => try o.intType(target.cTypeBitSize(.ushort).?, repr),
3050 .i128_type,2915 .c_int_type => try o.intType(target.cTypeBitSize(.int).?, repr),
3051 => |tag| switch (repr) {2916 .c_uint_type => try o.intType(target.cTypeBitSize(.uint).?, repr),
3052 .as_value => @field(Builder.Type, "i" ++ @tagName(tag)[1 .. @tagName(tag).len - "_type".len]),2917 .c_long_type => try o.intType(target.cTypeBitSize(.long).?, repr),
3053 .in_memory => try o.builder.intType(@intCast(t.abiSize(zcu) * 8)),2918 .c_ulong_type => try o.intType(target.cTypeBitSize(.ulong).?, repr),
3054 },2919 .c_longlong_type => try o.intType(target.cTypeBitSize(.longlong).?, repr),
3055 .usize_type, .isize_type => try o.builder.intType(target.ptrBitWidth()),2920 .c_ulonglong_type => try o.intType(target.cTypeBitSize(.ulonglong).?, repr),
3056 inline .c_char_type,
3057 .c_short_type,
3058 .c_ushort_type,
3059 .c_int_type,
3060 .c_uint_type,
3061 .c_long_type,
3062 .c_ulong_type,
3063 .c_longlong_type,
3064 .c_ulonglong_type,
3065 => |tag| try o.builder.intType(target.cTypeBitSize(
3066 @field(std.Target.CType, @tagName(tag)["c_".len .. @tagName(tag).len - "_type".len]),
3067 ).?),
3068 .c_longdouble_type,2921 .c_longdouble_type,
3069 .f16_type,2922 .f16_type,
3070 .f32_type,2923 .f32_type,
...@@ -3168,10 +3021,7 @@ pub const Object = struct {...@@ -3168,10 +3021,7 @@ pub const Object = struct {
3168 .none,3021 .none,
3169 => unreachable,3022 => unreachable,
3170 else => switch (ip.indexToKey(t.toIntern())) {3023 else => switch (ip.indexToKey(t.toIntern())) {
3171 .int_type => |int_type| switch (repr) {3024 .int_type => |int_type| o.intType(int_type.bits, repr),
3172 .as_value => try o.builder.intType(int_type.bits),
3173 .in_memory => try o.builder.intType(@intCast(t.abiSize(zcu) * 8)),
3174 },
3175 .ptr_type => |ptr_type| type: {3025 .ptr_type => |ptr_type| type: {
3176 const ptr_ty = try o.builder.ptrType(3026 const ptr_ty = try o.builder.ptrType(
3177 toLlvmAddressSpace(ptr_type.flags.address_space, target),3027 toLlvmAddressSpace(ptr_type.flags.address_space, target),
...@@ -3189,7 +3039,7 @@ pub const Object = struct {...@@ -3189,7 +3039,7 @@ pub const Object = struct {
3189 try o.lowerType(.fromInterned(array_type.child), repr),3039 try o.lowerType(.fromInterned(array_type.child), repr),
3190 ),3040 ),
3191 .vector_type => |vector_type| if (isByRef(t, zcu)) {3041 .vector_type => |vector_type| if (isByRef(t, zcu)) {
3192 const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), .in_memory);3042 const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), repr);
3193 return o.builder.arrayType(vector_type.len, child_llvm_ty);3043 return o.builder.arrayType(vector_type.len, child_llvm_ty);
3194 } else {3044 } else {
3195 const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), .as_value);3045 const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), .as_value);
...@@ -3454,7 +3304,7 @@ pub const Object = struct {...@@ -3454,7 +3304,7 @@ pub const Object = struct {
3454 return ty;3304 return ty;
3455 },3305 },
3456 .opaque_type, .spirv_type => unreachable, // no runtime bits3306 .opaque_type, .spirv_type => unreachable, // no runtime bits
3457 .enum_type => try o.lowerType(t.backingIntType(zcu), repr),3307 .enum_type => try o.intType(t.backingIntType(zcu).intInfo(zcu).bits, repr),
3458 .func_type => |func_type| {3308 .func_type => |func_type| {
3459 assert(t.fnHasRuntimeBits(zcu));3309 assert(t.fnHasRuntimeBits(zcu));
3460 return o.lowerFnType(.fromIntern(func_type, ip));3310 return o.lowerFnType(.fromIntern(func_type, ip));
...@@ -3525,7 +3375,7 @@ pub const Object = struct {...@@ -3525,7 +3375,7 @@ pub const Object = struct {
3525 .no_bits => continue,3375 .no_bits => continue,
3526 .byval => {3376 .byval => {
3527 const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]);3377 const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]);
3528 try llvm_params.append(o.gpa, try o.lowerType(param_ty, if (isByRef(param_ty, zcu)) .in_memory else .as_value));3378 try llvm_params.append(o.gpa, try o.lowerType(param_ty, if (isByRef(param_ty, zcu)) .memory_access else .as_value));
3529 },3379 },
3530 .byref, .byref_mut => {3380 .byref, .byref_mut => {
3531 try llvm_params.append(o.gpa, .ptr);3381 try llvm_params.append(o.gpa, .ptr);
...@@ -3548,7 +3398,7 @@ pub const Object = struct {...@@ -3548,7 +3398,7 @@ pub const Object = struct {
3548 },3398 },
3549 .float_array => |count| {3399 .float_array => |count| {
3550 const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]);3400 const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]);
3551 const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(param_ty, zcu).?, .in_memory);3401 const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(param_ty, zcu).?, .memory_access);
3552 try llvm_params.append(o.gpa, try o.builder.arrayType(count, float_ty));3402 try llvm_params.append(o.gpa, try o.builder.arrayType(count, float_ty));
3553 },3403 },
3554 .i32_array, .i64_array => |arr_len| {3404 .i32_array, .i64_array => |arr_len| {
...@@ -3620,7 +3470,12 @@ pub const Object = struct {...@@ -3620,7 +3470,12 @@ pub const Object = struct {
3620 var bigint_space: Value.BigIntSpace = undefined;3470 var bigint_space: Value.BigIntSpace = undefined;
3621 const bigint = val.toBigInt(&bigint_space, zcu);3471 const bigint = val.toBigInt(&bigint_space, zcu);
3622 const llvm_int_ty = try o.lowerType(ty, repr);3472 const llvm_int_ty = try o.lowerType(ty, repr);
3623 return o.builder.bigIntConst(llvm_int_ty, bigint);3473 if (llvm_int_ty.isInteger(&o.builder))
3474 return o.builder.bigIntConst(llvm_int_ty, bigint);
3475 const buffer = try o.gpa.alloc(u8, llvm_int_ty.aggregateLen(&o.builder));
3476 defer o.gpa.free(buffer);
3477 bigint.writeTwosComplement(buffer, target.cpu.arch.endian());
3478 return o.builder.stringConst(try o.builder.string(buffer));
3624 },3479 },
3625 .err => |err| {3480 .err => |err| {
3626 const int = zcu.intern_pool.getErrorValueIfExists(err.name).?;3481 const int = zcu.intern_pool.getErrorValueIfExists(err.name).?;
...@@ -3814,7 +3669,7 @@ pub const Object = struct {...@@ -3814,7 +3669,7 @@ pub const Object = struct {
3814 result_val.* = try o.builder.intConst(.i8, byte);3669 result_val.* = try o.builder.intConst(.i8, byte);
3815 },3670 },
3816 .elems => |elems| for (vals, elems) |*result_val, elem| {3671 .elems => |elems| for (vals, elems) |*result_val, elem| {
3817 result_val.* = try o.lowerValue(elem, if (is_by_ref) .in_memory else .as_value);3672 result_val.* = try o.lowerValue(elem, if (is_by_ref) repr else .as_value);
3818 },3673 },
3819 .repeated_elem => unreachable,3674 .repeated_elem => unreachable,
3820 }3675 }
...@@ -3826,7 +3681,7 @@ pub const Object = struct {...@@ -3826,7 +3681,7 @@ pub const Object = struct {
3826 .repeated_elem => |elem| if (is_by_ref) {3681 .repeated_elem => |elem| if (is_by_ref) {
3827 const vals = try allocator.alloc(Builder.Constant, vector_type.len);3682 const vals = try allocator.alloc(Builder.Constant, vector_type.len);
3828 defer allocator.free(vals);3683 defer allocator.free(vals);
3829 @memset(vals, try o.lowerValue(elem, .in_memory));3684 @memset(vals, try o.lowerValue(elem, repr));
3830 return o.builder.arrayConst(vector_ty, vals);3685 return o.builder.arrayConst(vector_ty, vals);
3831 } else return o.builder.splatConst(vector_ty, try o.lowerValue(elem, .as_value)),3686 } else return o.builder.splatConst(vector_ty, try o.lowerValue(elem, .as_value)),
3832 }3687 }
...@@ -4287,9 +4142,8 @@ pub const Object = struct {...@@ -4287,9 +4142,8 @@ pub const Object = struct {
4287 }4142 }
4288 errdefer assert(o.uav_map.remove(.{ .val = uav_val, .@"addrspace" = @"addrspace" }));4143 errdefer assert(o.uav_map.remove(.{ .val = uav_val, .@"addrspace" = @"addrspace" }));
42894144
4290 const llvm_ty = try o.lowerType(uav_ty, .in_memory);
4291 const llvm_name = try o.builder.strtabStringFmt("__anon_{d}", .{@backingInt(uav_val)});4145 const llvm_name = try o.builder.strtabStringFmt("__anon_{d}", .{@backingInt(uav_val)});
4292 const llvm_variable = try o.builder.addVariable(llvm_name, llvm_ty, llvm_addrspace);4146 const llvm_variable = try o.builder.addVariable(llvm_name, .void, llvm_addrspace);
4293 gop.value_ptr.* = llvm_variable;4147 gop.value_ptr.* = llvm_variable;
4294 try llvm_variable.setInitializer(try o.lowerValue(uav_val, .in_memory), &o.builder);4148 try llvm_variable.setInitializer(try o.lowerValue(uav_val, .in_memory), &o.builder);
4295 llvm_variable.setMutability(.constant, &o.builder);4149 llvm_variable.setMutability(.constant, &o.builder);
src/codegen/llvm/FuncGen.zig+9-12
...@@ -830,7 +830,7 @@ fn buildCall(...@@ -830,7 +830,7 @@ fn buildCall(
830 const alignment = arg_ty.abiAlignment(zcu).toLlvm();830 const alignment = arg_ty.abiAlignment(zcu).toLlvm();
831 // We don't need to handle non-ABI-sized integer types in memory here since they are831 // We don't need to handle non-ABI-sized integer types in memory here since they are
832 // never by-ref.832 // never by-ref.
833 const llvm_arg_ty = try o.lowerType(arg_ty, .in_memory);833 const llvm_arg_ty = try o.lowerType(arg_ty, .memory_access);
834 const loaded = try fg.wip.load(.normal, llvm_arg_ty, arg_val, alignment, "");834 const loaded = try fg.wip.load(.normal, llvm_arg_ty, arg_val, alignment, "");
835 try llvm_args.append(fg.gpa, loaded);835 try llvm_args.append(fg.gpa, loaded);
836 } else {836 } else {
...@@ -893,7 +893,7 @@ fn buildCall(...@@ -893,7 +893,7 @@ fn buildCall(
893 break :ptr ptr;893 break :ptr ptr;
894 } else arg_val;894 } else arg_val;
895895
896 const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty, zcu).?, .in_memory);896 const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty, zcu).?, .memory_access);
897 const array_ty = try o.builder.arrayType(count, float_ty);897 const array_ty = try o.builder.arrayType(count, float_ty);
898898
899 const loaded = try fg.wip.load(.normal, array_ty, arg_ptr, arg_ty.abiAlignment(zcu).toLlvm(), "");899 const loaded = try fg.wip.load(.normal, array_ty, arg_ptr, arg_ty.abiAlignment(zcu).toLlvm(), "");
...@@ -4967,10 +4967,7 @@ fn buildZigAlloca(fg: *FuncGen, ty: Type, @"align": InternPool.Alignment) Alloca...@@ -4967,10 +4967,7 @@ fn buildZigAlloca(fg: *FuncGen, ty: Type, @"align": InternPool.Alignment) Alloca
4967 .none => ty.abiAlignment(o.zcu),4967 .none => ty.abiAlignment(o.zcu),
4968 else => |a| a,4968 else => |a| a,
4969 };4969 };
4970 return fg.buildAlloca(4970 return fg.buildAlloca(try o.lowerType(ty, .in_memory), resolved_align.toLlvm());
4971 try o.lowerType(ty, .in_memory),
4972 resolved_align.toLlvm(),
4973 );
4974}4971}
49754972
4976/// Unlike `WipFunction.alloca`, this puts the alloca instruction at the top of the function.4973/// Unlike `WipFunction.alloca`, this puts the alloca instruction at the top of the function.
...@@ -6657,10 +6654,10 @@ fn load(...@@ -6657,10 +6654,10 @@ fn load(
6657 return result_ptr;6654 return result_ptr;
6658 }6655 }
66596656
6660 const llvm_memory_ty = try o.lowerType(load_ty, .in_memory);6657 const llvm_access_ty = try o.lowerType(load_ty, .memory_access);
6661 const llvm_value_ty = try o.lowerType(load_ty, .as_value);6658 const llvm_value_ty = try o.lowerType(load_ty, .as_value);
66626659
6663 if (llvm_memory_ty != llvm_value_ty) {6660 if (llvm_access_ty != llvm_value_ty) {
6664 assert(load_ty.isAbiInt(zcu));6661 assert(load_ty.isAbiInt(zcu));
6665 // `load_ty` is an integer type with padding bits. In theory, we shouldn't need any special6662 // `load_ty` is an integer type with padding bits. In theory, we shouldn't need any special
6666 // handling for these, as LLVM's documented semantics are a valid implementation of Zig's6663 // handling for these, as LLVM's documented semantics are a valid implementation of Zig's
...@@ -6674,7 +6671,7 @@ fn load(...@@ -6674,7 +6671,7 @@ fn load(
6674 //6671 //
6675 // Therefore, we handle these memory accesses specially: in this case we will actually load6672 // Therefore, we handle these memory accesses specially: in this case we will actually load
6676 // the next-largest "natural" integer type and then truncate to `load_ty`.6673 // the next-largest "natural" integer type and then truncate to `load_ty`.
6677 const loaded = try fg.wip.load(access_kind, llvm_memory_ty, ptr, llvm_ptr_align, "");6674 const loaded = try fg.wip.load(access_kind, llvm_access_ty, ptr, llvm_ptr_align, "");
6678 // For packed structs, current Zig semantics don't really allow us to make the padding bits6675 // For packed structs, current Zig semantics don't really allow us to make the padding bits
6679 // well-defined. This should be solved once https://github.com/ziglang/zig/issues/24061 is6676 // well-defined. This should be solved once https://github.com/ziglang/zig/issues/24061 is
6680 // implemented, but until then, do a normal trunc for packed types.6677 // implemented, but until then, do a normal trunc for packed types.
...@@ -6731,17 +6728,17 @@ fn store(...@@ -6731,17 +6728,17 @@ fn store(
67316728
6732 assert(elem.typeOfWip(&fg.wip) == try o.lowerType(elem_ty, .as_value));6729 assert(elem.typeOfWip(&fg.wip) == try o.lowerType(elem_ty, .as_value));
67336730
6734 const llvm_memory_ty = try o.lowerType(elem_ty, .in_memory);6731 const llvm_access_ty = try o.lowerType(elem_ty, .memory_access);
6735 const llvm_value_ty = try o.lowerType(elem_ty, .as_value);6732 const llvm_value_ty = try o.lowerType(elem_ty, .as_value);
67366733
6737 if (llvm_memory_ty != llvm_value_ty) {6734 if (llvm_access_ty != llvm_value_ty) {
6738 assert(elem_ty.isAbiInt(zcu));6735 assert(elem_ty.isAbiInt(zcu));
6739 // `elem_ty` is an integer type with padding bits, so we need to handle it specially---see6736 // `elem_ty` is an integer type with padding bits, so we need to handle it specially---see
6740 // the corresponding comment in `FuncGen.load` for more details.6737 // the corresponding comment in `FuncGen.load` for more details.
6741 const extended = try fg.wip.cast(switch (elem_ty.intInfo(zcu).signedness) {6738 const extended = try fg.wip.cast(switch (elem_ty.intInfo(zcu).signedness) {
6742 .unsigned => .zext,6739 .unsigned => .zext,
6743 .signed => .sext,6740 .signed => .sext,
6744 }, elem, llvm_memory_ty, "");6741 }, elem, llvm_access_ty, "");
6745 _ = try fg.wip.store(access_kind, extended, ptr, llvm_ptr_align);6742 _ = try fg.wip.store(access_kind, extended, ptr, llvm_ptr_align);
6746 return;6743 return;
6747 }6744 }