From 6aed49f6baad85fc4a509f4c631da8c623e7c28f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 15 Jul 2026 08:44:30 -0400 Subject: [PATCH] llvm.Builder: parse llvm alignment information from data layout This is required to correctly lower types in the llvm backend. --- lib/std/zig/llvm/Builder.zig | 563 ++++++++++++++++++++++++++++++++++- src/codegen/llvm.zig | 270 ++++------------- src/codegen/llvm/FuncGen.zig | 21 +- 3 files changed, 618 insertions(+), 236 deletions(-) diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index a19a54651db7736b3a31ca926c534690ee27bf79..8b9e87f8139f951b7abc475c5afb3035176cb209 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -17,7 +17,7 @@ gpa: Allocator, strip: bool, source_filename: String, -data_layout: String, +data_layout: DataLayout, target_triple: String, module_asm: std.ArrayList(u8), @@ -87,6 +87,455 @@ pub const Options = struct { triple: []const u8 = &.{}, }; +pub const DataLayout = struct { + endian: ?std.lang.Endian, + int_specs: PrimitiveSpec.Map, + float_specs: PrimitiveSpec.Map, + vector_specs: PrimitiveSpec.Map, + pointer_specs: PointerSpec.Map, + string_repr: String, + + const PrimitiveSpec = packed struct(u32) { + bit_width: BitWidth, + abi_align: Alignment, + pref_align: Alignment, + + const BitWidth = u20; + + const Map = std.array_hash_map.Custom(PrimitiveSpec, void, Context, false); + + const Context = struct { + pub fn hash(_: Context, spec: PrimitiveSpec) u32 { + return std.hash.int(spec.bit_width); + } + + pub fn eql(_: Context, lhs_spec: PrimitiveSpec, rhs_spec: PrimitiveSpec, _: usize) bool { + return lhs_spec.bit_width == rhs_spec.bit_width; + } + }; + }; + + const PointerSpec = struct { + bit_width: BitWidth, + index_bit_width: BitWidth, + flags: packed struct(u32) { + abi_align: Alignment, + pref_align: Alignment, + has_unstable_repr: bool, + has_external_state: bool, + null_ptr_repr: NullPtrRepr, + unused: u17 = 0, + }, + addr_space_name: String, + + const BitWidth = u32; + + const NullPtrRepr = enum(u1) { all_zeros, all_ones }; + + const Map = std.array_hash_map.Auto(AddrSpace, PointerSpec); + }; + + pub fn stringForTarget(target: *const std.Target) []const u8 { + // These data layouts should match Clang. + return switch (target.cpu.arch) { + .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", + .xcore => "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:32-f64:32-a:0:32-n32", + .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", + .lanai => "E-m:e-p:32:32-i64:64-a:0:32-n32-S64", + .aarch64 => if (target.ofmt == .macho) + if (target.os.tag == .windows or target.os.tag == .uefi) + "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + else if (target.abi == .ilp32) + "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + else + "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + else if (target.os.tag == .windows or target.os.tag == .uefi) + "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" + else + "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", + .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", + .arm => if (target.ofmt == .macho) + "e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" + else + "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", + .armeb, .thumbeb => if (target.ofmt == .macho) + "E-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" + else + "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", + .thumb => if (target.ofmt == .macho) + "e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" + else if (target.os.tag == .windows or target.os.tag == .uefi) + "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" + else + "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", + .avr => "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8:16-a:8", + .bpfeb => "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + .bpfel => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + .msp430 => "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16", + .mips => "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64", + .mipsel => "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64", + .mips64 => switch (target.abi) { + .gnuabin32, .muslabin32, .abin32 => "E-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + else => "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + }, + .mips64el => switch (target.abi) { + .gnuabin32, .muslabin32, .abin32 => "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + else => "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", + }, + .m68k => "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16", + .powerpc => "E-m:e-p:32:32-Fn32-i64:64-n32", + .powerpcle => "e-m:e-p:32:32-Fn32-i64:64-n32", + .powerpc64 => switch (target.os.tag) { + .linux => "E-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512", + .ps3 => "E-m:e-p:32:32-Fi64-i64:64-i128:128-n32:64", + else => "E-m:e-Fn32-i64:64-i128:128-n32:64", + }, + .powerpc64le => if (target.os.tag == .linux) + "e-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512" + else + "e-m:e-Fn32-i64:64-i128:128-n32:64", + .nvptx => "e-p:32:32-p6:32:32-p7:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64", + .nvptx64 => "e-p6:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64", + .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", + .riscv32 => if (target.cpu.has(.riscv, .e)) + "e-m:e-p:32:32-i64:64-n32-S32" + else + "e-m:e-p:32:32-i64:64-n32-S128", + .riscv32be => if (target.cpu.has(.riscv, .e)) + "E-m:e-p:32:32-i64:64-n32-S32" + else + "E-m:e-p:32:32-i64:64-n32-S128", + .riscv64 => if (target.cpu.has(.riscv, .e)) + "e-m:e-p:64:64-i64:64-i128:128-n32:64-S64" + else + "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + .riscv64be => if (target.cpu.has(.riscv, .e)) + "E-m:e-p:64:64-i64:64-i128:128-n32:64-S64" + else + "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + .sparc => "E-m:e-p:32:32-i64:64-i128:128-f128:64-n32-S64", + .sparc64 => "E-m:e-i64:64-i128:128-n32:64-S128", + .s390x => "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64", + .x86 => if (target.os.tag == .windows or target.os.tag == .uefi) switch (target.abi) { + .gnu => if (target.ofmt == .coff) + "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" + else + "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", + else => blk: { + const msvc = switch (target.abi) { + .none, .msvc => true, + else => false, + }; + + break :blk if (target.ofmt == .coff) + if (msvc) + "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" + else + "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" + else if (msvc) + "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" + else + "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"; + }, + } else if (target.ofmt == .macho) + "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" + else + "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", + .x86_64 => if (target.os.tag.isDarwin() or target.ofmt == .macho) + "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" + else switch (target.abi) { + .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", + else => if ((target.os.tag == .windows or target.os.tag == .uefi) and target.ofmt == .coff) + "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" + else + "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + }, + .spirv32 => switch (target.os.tag) { + .vulkan, .opengl => "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1", + 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", + }, + .spirv64 => "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1", + .wasm32 => if (target.os.tag == .emscripten) + "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" + else + "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20", + .wasm64 => if (target.os.tag == .emscripten) + "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" + else + "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20", + .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", + .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", + .loongarch32 => "e-m:e-p:32:32-i64:64-n32-S128", + .loongarch64 => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + .xtensa => "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32", + + .alpha, + .arceb, + .ez80, + .hppa, + .hppa64, + .kalimba, + .kvx, + .m88k, + .microblaze, + .microblazeel, + .or1k, + .propeller, + .sh, + .sheb, + .x86_16, + .xtensaeb, + => unreachable, + }; + } + + const default_int_specs: []const PrimitiveSpec = &.{ + .{ .bit_width = 8, .abi_align = .fromByteUnits(1), .pref_align = .fromByteUnits(1) }, // i8:8:8 + .{ .bit_width = 16, .abi_align = .fromByteUnits(2), .pref_align = .fromByteUnits(2) }, // i16:16:16 + .{ .bit_width = 32, .abi_align = .fromByteUnits(4), .pref_align = .fromByteUnits(4) }, // i32:32:32 + .{ .bit_width = 64, .abi_align = .fromByteUnits(4), .pref_align = .fromByteUnits(8) }, // i64:32:64 + }; + const default_float_specs: []const PrimitiveSpec = &.{ + .{ .bit_width = 16, .abi_align = .fromByteUnits(2), .pref_align = .fromByteUnits(2) }, // f16:16:16 + .{ .bit_width = 32, .abi_align = .fromByteUnits(4), .pref_align = .fromByteUnits(4) }, // f32:32:32 + .{ .bit_width = 64, .abi_align = .fromByteUnits(8), .pref_align = .fromByteUnits(8) }, // f64:64:64 + .{ .bit_width = 128, .abi_align = .fromByteUnits(16), .pref_align = .fromByteUnits(16) }, // f128:128:128 + }; + const default_vector_specs: []const PrimitiveSpec = &.{ + .{ .bit_width = 64, .abi_align = .fromByteUnits(8), .pref_align = .fromByteUnits(8) }, // v64:64:64 + .{ .bit_width = 128, .abi_align = .fromByteUnits(16), .pref_align = .fromByteUnits(16) }, // v128:128:128 + }; + + pub fn parseString(string_repr: String, builder: *Builder) Allocator.Error!DataLayout { + const gpa = builder.gpa; + + var int_specs: PrimitiveSpec.Map = .empty; + defer int_specs.deinit(gpa); + var float_specs: PrimitiveSpec.Map = .empty; + defer float_specs.deinit(gpa); + var vector_specs: PrimitiveSpec.Map = .empty; + defer vector_specs.deinit(gpa); + var pointer_specs: PointerSpec.Map = .empty; + defer pointer_specs.deinit(gpa); + var non_integral_addr_spaces: std.ArrayList(AddrSpace) = .empty; + defer non_integral_addr_spaces.deinit(gpa); + + try int_specs.ensureTotalCapacity(gpa, default_int_specs.len); + for (default_int_specs) |int_spec| int_specs.putAssumeCapacityNoClobber(int_spec, {}); + try float_specs.ensureTotalCapacity(gpa, default_float_specs.len); + for (default_float_specs) |float_spec| float_specs.putAssumeCapacityNoClobber(float_spec, {}); + try vector_specs.ensureTotalCapacity(gpa, default_vector_specs.len); + for (default_vector_specs) |vector_spec| vector_specs.putAssumeCapacityNoClobber(vector_spec, {}); + try pointer_specs.putNoClobber(gpa, .default, comptime .{ + .bit_width = 64, + .index_bit_width = 64, + .flags = .{ + .abi_align = .fromByteUnits(8), + .pref_align = .fromByteUnits(8), + .has_unstable_repr = false, + .has_external_state = false, + .null_ptr_repr = .all_zeros, + }, + .addr_space_name = .none, + }); + + var endian: ?std.lang.Endian = null; + var spec_it = std.mem.splitScalar(u8, string_repr.slice(builder).?, '-'); + while (spec_it.next()) |spec| switch (spec[0]) { + else => {}, + 'E' => { + assert(spec.len == 1); + assert(endian == null); + endian = .big; + }, + 'e' => { + assert(spec.len == 1); + assert(endian == null); + endian = .little; + }, + 'p' => { + var field_it = std.mem.splitScalar(u8, spec[1..], ':'); + + const first = field_it.first(); + var has_unstable_repr = false; + var has_external_state = false; + var null_ptr_repr: ?PointerSpec.NullPtrRepr = null; + var addr_space_name: String = .none; + const addr_space = for (first, 0..) |flag, as_start| switch (flag) { + 'u' => has_unstable_repr = true, + 'e' => has_external_state = true, + 'z' => { + assert(null_ptr_repr == null); + null_ptr_repr = .all_zeros; + }, + 'o' => { + assert(null_ptr_repr == null); + null_ptr_repr = .all_ones; + }, + else => { + if (first[first.len - ")".len] != ')') break first[as_start..]; + const name_start = std.mem.findScalarPos(u8, first, as_start, '(').?; + addr_space_name = try builder.string(first[name_start + "(".len .. first.len - ")".len]); + break first[as_start..name_start]; + }, + } else first[first.len..]; + const bit_width = std.fmt.parseInt(PointerSpec.BitWidth, field_it.next().?, 10) catch unreachable; + const abi_align: Alignment = .fromByteUnits(std.fmt.parseInt(u64, field_it.next().?, 10) catch unreachable); + const pref_align: Alignment = if (field_it.next()) |pref_align| + .fromByteUnits(std.fmt.parseInt(u64, pref_align, 10) catch unreachable) + else + abi_align; + const index_bit_width = if (field_it.next()) |index_bit_width| + std.fmt.parseInt(PointerSpec.BitWidth, index_bit_width, 10) catch unreachable + else + bit_width; + assert(field_it.peek() == null); + + try pointer_specs.put(gpa, switch (addr_space.len) { + 0 => .default, + else => @fromBackingInt(std.fmt.parseInt(u24, addr_space, 10) catch unreachable), + }, .{ + .bit_width = bit_width, + .index_bit_width = index_bit_width, + .flags = .{ + .abi_align = abi_align, + .pref_align = pref_align, + .has_unstable_repr = has_unstable_repr, + .has_external_state = has_external_state, + .null_ptr_repr = null_ptr_repr orelse .all_zeros, + }, + .addr_space_name = addr_space_name, + }); + }, + 'i', 'f', 'v' => |kind| { + if (std.mem.eql(u8, spec, "ve")) { + vector_specs.clearRetainingCapacity(); + continue; + } + var field_it = std.mem.splitScalar(u8, spec[1..], ':'); + const bit_width = std.fmt.parseInt(PrimitiveSpec.BitWidth, field_it.first(), 10) catch unreachable; + const abi_align: Alignment = .fromByteUnits(std.fmt.parseInt(u64, field_it.next().?, 10) catch unreachable); + const pref_align: Alignment = if (field_it.next()) |pref_align| + .fromByteUnits(std.fmt.parseInt(u64, pref_align, 10) catch unreachable) + else + abi_align; + assert(field_it.peek() == null); + const specs = switch (kind) { + else => unreachable, + 'i' => &int_specs, + 'f' => &float_specs, + 'v' => &vector_specs, + }; + try specs.put(gpa, .{ .bit_width = bit_width, .abi_align = abi_align, .pref_align = pref_align }, {}); + }, + 'n' => { + var field_it = std.mem.splitScalar(u8, spec[1..], ':'); + if (std.mem.eql(u8, field_it.first(), "i")) { + while (field_it.next()) |non_integral_addr_space| try non_integral_addr_spaces.append( + gpa, + @fromBackingInt(std.fmt.parseInt(u24, non_integral_addr_space, 10) catch unreachable), + ); + } else { + field_it.reset(); + while (field_it.next()) |native_bit_width| { + _ = std.fmt.parseInt(PrimitiveSpec.BitWidth, native_bit_width, 10) catch unreachable; + } + } + }, + }; + + for (non_integral_addr_spaces.items) |non_integral_addr_space| { + const pointer_spec_gop = try pointer_specs.getOrPut(gpa, non_integral_addr_space); + if (!pointer_spec_gop.found_existing) pointer_spec_gop.value_ptr.* = pointer_specs.get(.default).?; + pointer_spec_gop.value_ptr.flags.has_unstable_repr = true; + pointer_spec_gop.value_ptr.flags.has_external_state = false; + } + + { + const SortContext = struct { + specs: []const PrimitiveSpec, + pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { + return ctx.specs[lhs_index].bit_width < ctx.specs[rhs_index].bit_width; + } + }; + int_specs.sortUnstable(SortContext{ .specs = int_specs.keys() }); + float_specs.sortUnstable(SortContext{ .specs = float_specs.keys() }); + vector_specs.sortUnstable(SortContext{ .specs = vector_specs.keys() }); + } + { + const SortContext = struct { + addr_spaces: []const AddrSpace, + pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { + return @backingInt(ctx.addr_spaces[lhs_index]) < @backingInt(ctx.addr_spaces[rhs_index]); + } + }; + pointer_specs.sortUnstable(SortContext{ .addr_spaces = pointer_specs.keys() }); + assert(pointer_specs.keys()[0] == .default); + } + + return .{ + .endian = endian, + .int_specs = int_specs.move(), + .float_specs = float_specs.move(), + .vector_specs = vector_specs.move(), + .pointer_specs = pointer_specs.move(), + .string_repr = string_repr, + }; + } + + pub fn deinit(data_layout: *DataLayout, gpa: Allocator) void { + data_layout.int_specs.deinit(gpa); + data_layout.float_specs.deinit(gpa); + data_layout.vector_specs.deinit(gpa); + data_layout.pointer_specs.deinit(gpa); + } + + pub fn getIntegerSpec(data_layout: *const DataLayout, bit_width: PrimitiveSpec.BitWidth) PrimitiveSpec { + const specs = data_layout.int_specs.keys(); + return specs[ + @min(std.sort.lowerBound(PrimitiveSpec, specs, bit_width, struct { + fn order(ctx: PrimitiveSpec.BitWidth, spec: PrimitiveSpec) std.math.Order { + return std.math.order(ctx, spec.bit_width); + } + }.order), specs.len - 1) + ]; + } + + pub fn getFloatSpec(data_layout: *const DataLayout, bit_width: PrimitiveSpec.BitWidth) PrimitiveSpec { + if (data_layout.float_specs.getEntry(.{ + .bit_width = bit_width, + .abi_align = .default, + .pref_align = .default, + })) |entry| return entry.key_ptr.*; + const default_align: Alignment = .fromByteUnits( + std.math.ceilPowerOfTwoAssert(PrimitiveSpec.BitWidth, bit_width / 8), + ); + return .{ .bit_width = bit_width, .abi_align = default_align, .pref_align = default_align }; + } + + pub fn getVectorSpec( + data_layout: *const DataLayout, + bit_width: PrimitiveSpec.BitWidth, + store_size: Type.Size, + ) PrimitiveSpec { + if (data_layout.float_specs.getEntry(.{ + .bit_width = bit_width, + .abi_align = .default, + .pref_align = .default, + })) |entry| return entry.key_ptr.*; + const default_align: Alignment = .fromByteUnits( + std.math.ceilPowerOfTwoAssert(PrimitiveSpec.BitWidth, switch (store_size) { + .fixed, .scalable => |known_min| known_min, + }), + ); + return .{ .bit_width = bit_width, .abi_align = default_align, .pref_align = default_align }; + } + + pub fn getPointerSpec(data_layout: *const DataLayout, addr_space: AddrSpace) PointerSpec { + return data_layout.pointer_specs.get(addr_space) orelse data_layout.pointer_specs.values()[0]; + } +}; + pub const String = enum(u32) { none = maxInt(u31), empty, @@ -489,7 +938,10 @@ pub const Type = enum(u32) { .double, .i64, .x86_mmx => 64, .x86_fp80, .i80 => 80, .fp128, .ppc_fp128, .i128 => 128, - .ptr, .@"ptr addrspace(4)" => @panic("TODO: query data layout"), + .ptr => @intCast(builder.data_layout.getPointerSpec(.default).bit_width), + .@"ptr addrspace(4)" => @intCast( + builder.data_layout.getPointerSpec(@fromBackingInt(@intCast(4))).bit_width, + ), _ => { const item = builder.type_items.items[@backingInt(self)]; return switch (item.tag) { @@ -498,7 +950,9 @@ pub const Type = enum(u32) { .vararg_function, => unreachable, .integer => @intCast(item.data), - .pointer => @panic("TODO: query data layout"), + .pointer => @intCast( + builder.data_layout.getPointerSpec(@fromBackingInt(@intCast(item.data))).bit_width, + ), .target => unreachable, .vector, .scalable_vector, @@ -931,6 +1385,67 @@ pub const Type = enum(u32) { }, }; } + + const Size = union(enum) { fixed: u64, scalable: u64 }; + pub fn bits(ty: Type, builder: *const Builder) Size { + const item = builder.type_items.items[@backingInt(ty)]; + return switch (item.tag) { + else => unreachable, + .simple => switch (@as(Simple, @fromBackingInt(@intCast(item.data)))) { + else => unreachable, + .label => .{ .fixed = builder.data_layout.getPointerSpec(.default).bit_width }, + .half, .bfloat => .{ .fixed = 16 }, + .float => .{ .fixed = 32 }, + .double => .{ .fixed = 64 }, + .ppc_fp128, .fp128 => .{ .fixed = 128 }, + .x86_amx => .{ .fixed = 8192 }, + .x86_fp80 => .{ .fixed = 80 }, + }, + .integer => .{ .fixed = item.data }, + .pointer => .{ + .fixed = builder.data_layout.getPointerSpec(@fromBackingInt(@intCast(item.data))).bit_width, + }, + }; + } + + pub fn alignment(ty: Type, kind: enum { abi, pref }, builder: *const Builder) Alignment { + const item = builder.type_items.items[@backingInt(ty)]; + switch (item.tag) { + else => unreachable, + .simple => switch (@as(Simple, @fromBackingInt(@intCast(item.data)))) { + else => unreachable, + .label => { + const spec = builder.data_layout.getPointerSpec(.default); + return switch (kind) { + .abi => spec.flags.abi_align, + .pref => spec.flags.pref_align, + }; + }, + .half, .bfloat, .float, .double, .ppc_fp128, .fp128, .x86_fp80 => { + const spec = builder.data_layout.getFloatSpec(@intCast(ty.bits(builder).fixed)); + return switch (kind) { + .abi => spec.abi_align, + .pref => spec.pref_align, + }; + }, + .x86_amx => return comptime .fromByteUnits(64), + }, + .integer => { + const spec = builder.data_layout.getIntegerSpec(@intCast(item.data)); + return switch (kind) { + .abi => spec.abi_align, + .pref => spec.pref_align, + }; + }, + .pointer => { + const spec = builder.data_layout.getPointerSpec(@fromBackingInt(@intCast(item.data))); + return switch (kind) { + .abi => spec.flags.abi_align, + .pref => spec.flags.pref_align, + }; + }, + } + } }; pub const Attribute = union(Kind) { @@ -2182,11 +2697,18 @@ pub const Alignment = enum(u6) { }; } - /// Asserts that neither `a` nor `b` is `.default`. - pub fn max(a: Alignment, b: Alignment) Alignment { - assert(a != .default); - assert(b != .default); - return @fromBackingInt(@intCast(@max(@backingInt(a), @backingInt(b)))); + /// Asserts that neither `lhs` nor `rhs` is `.default`. + pub fn max(lhs: Alignment, rhs: Alignment) Alignment { + assert(lhs != .default); + assert(rhs != .default); + return @fromBackingInt(@max(@backingInt(lhs), @backingInt(rhs))); + } + + /// Asserts that neither `lhs` nor `rhs` is `.default`. + pub fn order(lhs: Alignment, rhs: Alignment) std.math.Order { + assert(lhs != .default); + assert(rhs != .default); + return std.math.order(@backingInt(lhs), @backingInt(rhs)); } pub fn toLlvm(self: Alignment) u6 { @@ -9023,7 +9545,14 @@ pub fn init(options: Options) Allocator.Error!Builder { .strip = options.strip, .source_filename = .none, - .data_layout = .none, + .data_layout = .{ + .endian = null, + .int_specs = .empty, + .float_specs = .empty, + .vector_specs = .empty, + .pointer_specs = .empty, + .string_repr = .none, + }, .target_triple = .none, .module_asm = .empty, @@ -9079,14 +9608,14 @@ pub fn init(options: Options) Allocator.Error!Builder { try self.string_indices.append(self.gpa, 0); assert(try self.string("") == .empty); + self.data_layout = try .parseString(try self.string(DataLayout.stringForTarget(options.target)), &self); + try self.strtab_string_indices.append(self.gpa, 0); assert(try self.strtabString("") == .empty); if (options.name.len > 0) self.source_filename = try self.string(options.name); - if (options.triple.len > 0) { - self.target_triple = try self.string(options.triple); - } + if (options.triple.len > 0) self.target_triple = try self.string(options.triple); { const static_len = @typeInfo(Type).@"enum".field_names.len - 1; @@ -9179,6 +9708,8 @@ pub fn clearAndFree(self: *Builder) void { pub fn deinit(self: *Builder) void { const gpa = self.gpa; + self.data_layout.deinit(gpa); + self.module_asm.deinit(gpa); self.string_map.deinit(gpa); @@ -9998,17 +10529,17 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined }; defer metadata_formatter.map.deinit(self.gpa); - if (self.source_filename != .none or self.data_layout != .none or self.target_triple != .none) { + if (self.source_filename != .none or self.data_layout.string_repr != .none or self.target_triple != .none) { if (need_newline) try w.writeByte('\n') else need_newline = true; if (self.source_filename != .none) try w.print( \\; ModuleID = '{s}' \\source_filename = {f} \\ , .{ self.source_filename.slice(self).?, self.source_filename.fmtQ(self) }); - if (self.data_layout != .none) try w.print( + if (self.data_layout.string_repr != .none) try w.print( \\target datalayout = {f} \\ - , .{self.data_layout.fmtQ(self)}); + , .{self.data_layout.string_repr.fmtQ(self)}); if (self.target_triple != .none) try w.print( \\target triple = {f} \\ @@ -13706,7 +14237,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco }); } - if (self.data_layout.slice(self)) |data_layout| { + if (self.data_layout.string_repr.slice(self)) |data_layout| { try module_block.writeAbbrev(ModuleBlock.String{ .code = 3, .string = data_layout, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 221c9423e3a035367c010f0d6225940ab70a8ffb..689a0349ce7cf9aa2c49c2101f476303667c50cd 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -345,160 +345,6 @@ pub fn supportsTailCall(target: *const std.Target) bool { }; } -pub fn dataLayout(target: *const std.Target) []const u8 { - // These data layouts should match Clang. - return switch (target.cpu.arch) { - .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", - .xcore => "e-m:e-p:32:32-i1:8:32-i8:8:32-i16:16:32-i64:32-f64:32-a:0:32-n32", - .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", - .lanai => "E-m:e-p:32:32-i64:64-a:0:32-n32-S64", - .aarch64 => if (target.ofmt == .macho) - if (target.os.tag == .windows or target.os.tag == .uefi) - "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" - else if (target.abi == .ilp32) - "e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" - else - "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" - else if (target.os.tag == .windows or target.os.tag == .uefi) - "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" - else - "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", - .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", - .arm => if (target.ofmt == .macho) - "e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" - else - "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", - .armeb, .thumbeb => if (target.ofmt == .macho) - "E-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" - else - "E-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", - .thumb => if (target.ofmt == .macho) - "e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" - else if (target.os.tag == .windows or target.os.tag == .uefi) - "e-m:w-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" - else - "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", - .avr => "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8:16-a:8", - .bpfeb => "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128", - .bpfel => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", - .msp430 => "e-m:e-p:16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S16", - .mips => "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64", - .mipsel => "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64", - .mips64 => switch (target.abi) { - .gnuabin32, .muslabin32, .abin32 => "E-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", - else => "E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", - }, - .mips64el => switch (target.abi) { - .gnuabin32, .muslabin32, .abin32 => "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", - else => "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", - }, - .m68k => "E-m:e-p:32:16:32-i8:8:8-i16:16:16-i32:16:32-n8:16:32-a:0:16-S16", - .powerpc => "E-m:e-p:32:32-Fn32-i64:64-n32", - .powerpcle => "e-m:e-p:32:32-Fn32-i64:64-n32", - .powerpc64 => switch (target.os.tag) { - .linux => "E-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512", - .ps3 => "E-m:e-p:32:32-Fi64-i64:64-i128:128-n32:64", - else => "E-m:e-Fn32-i64:64-i128:128-n32:64", - }, - .powerpc64le => if (target.os.tag == .linux) - "e-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512" - else - "e-m:e-Fn32-i64:64-i128:128-n32:64", - .nvptx => "e-p:32:32-p6:32:32-p7:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64", - .nvptx64 => "e-p6:32:32-i64:64-i128:128-i256:256-v16:16-v32:32-n16:32:64", - .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", - .riscv32 => if (target.cpu.has(.riscv, .e)) - "e-m:e-p:32:32-i64:64-n32-S32" - else - "e-m:e-p:32:32-i64:64-n32-S128", - .riscv32be => if (target.cpu.has(.riscv, .e)) - "E-m:e-p:32:32-i64:64-n32-S32" - else - "E-m:e-p:32:32-i64:64-n32-S128", - .riscv64 => if (target.cpu.has(.riscv, .e)) - "e-m:e-p:64:64-i64:64-i128:128-n32:64-S64" - else - "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", - .riscv64be => if (target.cpu.has(.riscv, .e)) - "E-m:e-p:64:64-i64:64-i128:128-n32:64-S64" - else - "E-m:e-p:64:64-i64:64-i128:128-n32:64-S128", - .sparc => "E-m:e-p:32:32-i64:64-i128:128-f128:64-n32-S64", - .sparc64 => "E-m:e-i64:64-i128:128-n32:64-S128", - .s390x => "E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-v128:64-a:8:16-n32:64", - .x86 => if (target.os.tag == .windows or target.os.tag == .uefi) switch (target.abi) { - .gnu => if (target.ofmt == .coff) - "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" - else - "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", - else => blk: { - const msvc = switch (target.abi) { - .none, .msvc => true, - else => false, - }; - - break :blk if (target.ofmt == .coff) - if (msvc) - "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" - else - "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" - else if (msvc) - "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" - else - "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"; - }, - } else if (target.ofmt == .macho) - "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" - else - "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", - .x86_64 => if (target.os.tag.isDarwin() or target.ofmt == .macho) - "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" - else switch (target.abi) { - .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", - else => if ((target.os.tag == .windows or target.os.tag == .uefi) and target.ofmt == .coff) - "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" - else - "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", - }, - .spirv32 => switch (target.os.tag) { - .vulkan, .opengl => "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1", - 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", - }, - .spirv64 => "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-G1", - .wasm32 => if (target.os.tag == .emscripten) - "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" - else - "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20", - .wasm64 => if (target.os.tag == .emscripten) - "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" - else - "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20", - .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", - .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", - .loongarch32 => "e-m:e-p:32:32-i64:64-n32-S128", - .loongarch64 => "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", - .xtensa => "e-m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32", - - .alpha, - .arceb, - .ez80, - .hppa, - .hppa64, - .kalimba, - .kvx, - .m88k, - .microblaze, - .microblazeel, - .or1k, - .propeller, - .sh, - .sheb, - .x86_16, - .xtensaeb, - => unreachable, // Gated by hasLlvmSupport(). - }; -} - // Avoid depending on `bindings.CodeModel` in the bitcode-only case. const CodeModel = enum { default, @@ -618,8 +464,6 @@ pub const Object = struct { }); errdefer builder.deinit(); - builder.data_layout = try builder.string(dataLayout(target)); - const debug_compile_unit, const debug_enums_fwd_ref, const debug_globals_fwd_ref = if (!builder.strip) debug_info: { // We fully resolve all paths at this point to avoid lack of @@ -2865,18 +2709,38 @@ pub const Object = struct { pub const TypeRepr = enum { /// The representation of the type when it is being manipulated as a value in a function. - /// e.g. Zig `u5` -> LLVM `i5` + /// e.g. Zig `u90` -> LLVM `i90` as_value, - /// The representation of the type when it is stored in memory. - /// e.g. Zig `u5` -> LLVM `i8` + /// The representation of the type when it is loaded from or stored to memory. + /// e.g. Zig `u90` -> LLVM `i96` + memory_access, + /// The representation of the type when it is in memory. + /// e.g. Zig `u90` -> LLVM `[12 x i8]` in_memory, }; + pub fn intType(o: *Object, bits: u16, repr: TypeRepr) Allocator.Error!Builder.Type { + switch (repr) { + .as_value => return o.builder.intType(bits), + .memory_access, .in_memory => {}, + } + const target = o.zcu.getTarget(); + const abi_size = std.zig.target.intByteSize(target, bits); + const llvm_bit_width = @as(u20, 8) * abi_size; + switch (repr) { + .as_value => unreachable, + .memory_access => {}, + .in_memory => { + const zig_align = std.zig.target.intAlignment(target, bits); + const llvm_align = o.builder.data_layout.getIntegerSpec(llvm_bit_width).abi_align; + if (zig_align < llvm_align.toByteUnits().?) return o.builder.arrayType(abi_size, .i8); + }, + } + return o.builder.intType(llvm_bit_width); + } + pub fn errorIntType(o: *Object, repr: TypeRepr) Allocator.Error!Builder.Type { - return o.builder.intType(switch (repr) { - .as_value => o.zcu.errorSetBits(), - .in_memory => @intCast(Type.anyerror.abiSize(o.zcu) * 8), - }); + return o.intType(o.zcu.errorSetBits(), repr); } pub const SoftF80Layout = struct { @@ -3029,42 +2893,31 @@ pub const Object = struct { const target = zcu.getTarget(); const ip = &zcu.intern_pool; - if (repr == .as_value) { - assert(!isByRef(t, zcu)); // by-ref types must only be manipulated in memory + switch (repr) { + .as_value => assert(!isByRef(t, zcu)), // by-ref types must only be manipulated in memory + .memory_access, .in_memory => {}, } return switch (t.toIntern()) { .u0_type => unreachable, // no runtime bits - inline .u1_type, - .u8_type, - .i8_type, - .u16_type, - .i16_type, - .u29_type, - .u32_type, - .i32_type, - .u64_type, - .i64_type, - .u80_type, - .u128_type, - .i128_type, - => |tag| switch (repr) { - .as_value => @field(Builder.Type, "i" ++ @tagName(tag)[1 .. @tagName(tag).len - "_type".len]), - .in_memory => try o.builder.intType(@intCast(t.abiSize(zcu) * 8)), - }, - .usize_type, .isize_type => try o.builder.intType(target.ptrBitWidth()), - inline .c_char_type, - .c_short_type, - .c_ushort_type, - .c_int_type, - .c_uint_type, - .c_long_type, - .c_ulong_type, - .c_longlong_type, - .c_ulonglong_type, - => |tag| try o.builder.intType(target.cTypeBitSize( - @field(std.Target.CType, @tagName(tag)["c_".len .. @tagName(tag).len - "_type".len]), - ).?), + .u1_type => try o.intType(1, repr), + .u8_type, .i8_type => try o.intType(8, repr), + .u16_type, .i16_type => try o.intType(16, repr), + .u29_type => try o.intType(29, repr), + .u32_type, .i32_type => try o.intType(32, repr), + .u64_type, .i64_type => try o.intType(64, repr), + .u80_type => try o.intType(80, repr), + .u128_type, .i128_type => try o.intType(128, repr), + .usize_type, .isize_type => try o.intType(target.ptrBitWidth(), repr), + .c_char_type => try o.intType(target.cTypeBitSize(.char).?, repr), + .c_short_type => try o.intType(target.cTypeBitSize(.short).?, repr), + .c_ushort_type => try o.intType(target.cTypeBitSize(.ushort).?, repr), + .c_int_type => try o.intType(target.cTypeBitSize(.int).?, repr), + .c_uint_type => try o.intType(target.cTypeBitSize(.uint).?, repr), + .c_long_type => try o.intType(target.cTypeBitSize(.long).?, repr), + .c_ulong_type => try o.intType(target.cTypeBitSize(.ulong).?, repr), + .c_longlong_type => try o.intType(target.cTypeBitSize(.longlong).?, repr), + .c_ulonglong_type => try o.intType(target.cTypeBitSize(.ulonglong).?, repr), .c_longdouble_type, .f16_type, .f32_type, @@ -3168,10 +3021,7 @@ pub const Object = struct { .none, => unreachable, else => switch (ip.indexToKey(t.toIntern())) { - .int_type => |int_type| switch (repr) { - .as_value => try o.builder.intType(int_type.bits), - .in_memory => try o.builder.intType(@intCast(t.abiSize(zcu) * 8)), - }, + .int_type => |int_type| o.intType(int_type.bits, repr), .ptr_type => |ptr_type| type: { const ptr_ty = try o.builder.ptrType( toLlvmAddressSpace(ptr_type.flags.address_space, target), @@ -3189,7 +3039,7 @@ pub const Object = struct { try o.lowerType(.fromInterned(array_type.child), repr), ), .vector_type => |vector_type| if (isByRef(t, zcu)) { - const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), .in_memory); + const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), repr); return o.builder.arrayType(vector_type.len, child_llvm_ty); } else { const child_llvm_ty = try o.lowerType(.fromInterned(vector_type.child), .as_value); @@ -3454,7 +3304,7 @@ pub const Object = struct { return ty; }, .opaque_type, .spirv_type => unreachable, // no runtime bits - .enum_type => try o.lowerType(t.backingIntType(zcu), repr), + .enum_type => try o.intType(t.backingIntType(zcu).intInfo(zcu).bits, repr), .func_type => |func_type| { assert(t.fnHasRuntimeBits(zcu)); return o.lowerFnType(.fromIntern(func_type, ip)); @@ -3525,7 +3375,7 @@ pub const Object = struct { .no_bits => continue, .byval => { const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); - try llvm_params.append(o.gpa, try o.lowerType(param_ty, if (isByRef(param_ty, zcu)) .in_memory else .as_value)); + try llvm_params.append(o.gpa, try o.lowerType(param_ty, if (isByRef(param_ty, zcu)) .memory_access else .as_value)); }, .byref, .byref_mut => { try llvm_params.append(o.gpa, .ptr); @@ -3548,7 +3398,7 @@ pub const Object = struct { }, .float_array => |count| { const param_ty = Type.fromInterned(fn_info.param_types[it.zig_index - 1]); - const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(param_ty, zcu).?, .in_memory); + const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(param_ty, zcu).?, .memory_access); try llvm_params.append(o.gpa, try o.builder.arrayType(count, float_ty)); }, .i32_array, .i64_array => |arr_len| { @@ -3620,7 +3470,12 @@ pub const Object = struct { var bigint_space: Value.BigIntSpace = undefined; const bigint = val.toBigInt(&bigint_space, zcu); const llvm_int_ty = try o.lowerType(ty, repr); - return o.builder.bigIntConst(llvm_int_ty, bigint); + if (llvm_int_ty.isInteger(&o.builder)) + return o.builder.bigIntConst(llvm_int_ty, bigint); + const buffer = try o.gpa.alloc(u8, llvm_int_ty.aggregateLen(&o.builder)); + defer o.gpa.free(buffer); + bigint.writeTwosComplement(buffer, target.cpu.arch.endian()); + return o.builder.stringConst(try o.builder.string(buffer)); }, .err => |err| { const int = zcu.intern_pool.getErrorValueIfExists(err.name).?; @@ -3814,7 +3669,7 @@ pub const Object = struct { result_val.* = try o.builder.intConst(.i8, byte); }, .elems => |elems| for (vals, elems) |*result_val, elem| { - result_val.* = try o.lowerValue(elem, if (is_by_ref) .in_memory else .as_value); + result_val.* = try o.lowerValue(elem, if (is_by_ref) repr else .as_value); }, .repeated_elem => unreachable, } @@ -3826,7 +3681,7 @@ pub const Object = struct { .repeated_elem => |elem| if (is_by_ref) { const vals = try allocator.alloc(Builder.Constant, vector_type.len); defer allocator.free(vals); - @memset(vals, try o.lowerValue(elem, .in_memory)); + @memset(vals, try o.lowerValue(elem, repr)); return o.builder.arrayConst(vector_ty, vals); } else return o.builder.splatConst(vector_ty, try o.lowerValue(elem, .as_value)), } @@ -4287,9 +4142,8 @@ pub const Object = struct { } errdefer assert(o.uav_map.remove(.{ .val = uav_val, .@"addrspace" = @"addrspace" })); - const llvm_ty = try o.lowerType(uav_ty, .in_memory); const llvm_name = try o.builder.strtabStringFmt("__anon_{d}", .{@backingInt(uav_val)}); - const llvm_variable = try o.builder.addVariable(llvm_name, llvm_ty, llvm_addrspace); + const llvm_variable = try o.builder.addVariable(llvm_name, .void, llvm_addrspace); gop.value_ptr.* = llvm_variable; try llvm_variable.setInitializer(try o.lowerValue(uav_val, .in_memory), &o.builder); llvm_variable.setMutability(.constant, &o.builder); diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig index 5bdf5029d1b4a639c41652f0792d56aba08e927a..f9ba9ecbac5056091f6ff13721ada61080baf5e4 100644 --- a/src/codegen/llvm/FuncGen.zig +++ b/src/codegen/llvm/FuncGen.zig @@ -830,7 +830,7 @@ fn buildCall( const alignment = arg_ty.abiAlignment(zcu).toLlvm(); // We don't need to handle non-ABI-sized integer types in memory here since they are // never by-ref. - const llvm_arg_ty = try o.lowerType(arg_ty, .in_memory); + const llvm_arg_ty = try o.lowerType(arg_ty, .memory_access); const loaded = try fg.wip.load(.normal, llvm_arg_ty, arg_val, alignment, ""); try llvm_args.append(fg.gpa, loaded); } else { @@ -893,7 +893,7 @@ fn buildCall( break :ptr ptr; } else arg_val; - const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty, zcu).?, .in_memory); + const float_ty = try o.lowerType(aarch64_c_abi.getFloatArrayType(arg_ty, zcu).?, .memory_access); const array_ty = try o.builder.arrayType(count, float_ty); 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 .none => ty.abiAlignment(o.zcu), else => |a| a, }; - return fg.buildAlloca( - try o.lowerType(ty, .in_memory), - resolved_align.toLlvm(), - ); + return fg.buildAlloca(try o.lowerType(ty, .in_memory), resolved_align.toLlvm()); } /// Unlike `WipFunction.alloca`, this puts the alloca instruction at the top of the function. @@ -6657,10 +6654,10 @@ fn load( return result_ptr; } - const llvm_memory_ty = try o.lowerType(load_ty, .in_memory); + const llvm_access_ty = try o.lowerType(load_ty, .memory_access); const llvm_value_ty = try o.lowerType(load_ty, .as_value); - if (llvm_memory_ty != llvm_value_ty) { + if (llvm_access_ty != llvm_value_ty) { assert(load_ty.isAbiInt(zcu)); // `load_ty` is an integer type with padding bits. In theory, we shouldn't need any special // handling for these, as LLVM's documented semantics are a valid implementation of Zig's @@ -6674,7 +6671,7 @@ fn load( // // Therefore, we handle these memory accesses specially: in this case we will actually load // the next-largest "natural" integer type and then truncate to `load_ty`. - const loaded = try fg.wip.load(access_kind, llvm_memory_ty, ptr, llvm_ptr_align, ""); + const loaded = try fg.wip.load(access_kind, llvm_access_ty, ptr, llvm_ptr_align, ""); // For packed structs, current Zig semantics don't really allow us to make the padding bits // well-defined. This should be solved once https://github.com/ziglang/zig/issues/24061 is // implemented, but until then, do a normal trunc for packed types. @@ -6731,17 +6728,17 @@ fn store( assert(elem.typeOfWip(&fg.wip) == try o.lowerType(elem_ty, .as_value)); - const llvm_memory_ty = try o.lowerType(elem_ty, .in_memory); + const llvm_access_ty = try o.lowerType(elem_ty, .memory_access); const llvm_value_ty = try o.lowerType(elem_ty, .as_value); - if (llvm_memory_ty != llvm_value_ty) { + if (llvm_access_ty != llvm_value_ty) { assert(elem_ty.isAbiInt(zcu)); // `elem_ty` is an integer type with padding bits, so we need to handle it specially---see // the corresponding comment in `FuncGen.load` for more details. const extended = try fg.wip.cast(switch (elem_ty.intInfo(zcu).signedness) { .unsigned => .zext, .signed => .sext, - }, elem, llvm_memory_ty, ""); + }, elem, llvm_access_ty, ""); _ = try fg.wip.store(access_kind, extended, ptr, llvm_ptr_align); return; } -- 2.54.0