From 1ccada04521d13fe4b43cf36bdd8c980f831f570 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Sat, 28 Feb 2026 10:10:15 +0000 Subject: [PATCH] compiler: stop LLVM bossing the frontend around I previously wrote some weird code in the compiler frontend solely because the LLVM backend has some weird requirements, but the better solution is to avoid those requirements. This commit does that by introducing "alignment forward references" to `std.zig.llvm.Builder`. Much like debug forward references, they allow you to reference an alignment value which will be populated at a later time (and which can be updated many times, which is important for incremental compilation). Then, when we want to reference a type's ABI alignment while the type is not necessarily resolved (required for `@"align"` attributes on function parameters and function call arguments), we create a forward reference and use `link.ConstPool` to populate it when ready. This allows us to remove from the compiler frontend some extremely arbitrary calls to `Sema.ensureLayoutResolved`, so that the language specification is not being built around the particular needs of our compiler implementation's LLVM code generation backend. --- lib/std/zig/llvm/Builder.zig | 199 ++++++++++++++++++++++------------- src/Sema.zig | 15 --- src/Zcu/PerThread.zig | 14 --- src/codegen/llvm.zig | 154 ++++++++++++++++++--------- 4 files changed, 228 insertions(+), 154 deletions(-) diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig index f40b63c85f..5001a0c375 100644 --- a/lib/std/zig/llvm/Builder.zig +++ b/lib/std/zig/llvm/Builder.zig @@ -7,6 +7,7 @@ const Allocator = std.mem.Allocator; const assert = std.debug.assert; const DW = std.dwarf; const log = std.log.scoped(.llvm); +const maxInt = std.math.maxInt; const Writer = std.Io.Writer; const bitcode_writer = @import("bitcode_writer.zig"); @@ -55,6 +56,8 @@ constant_items: std.MultiArrayList(Constant.Item), constant_extra: std.ArrayList(u32), constant_limbs: std.ArrayList(std.math.big.Limb), +alignment_forward_references: std.ArrayList(Alignment), + metadata_map: std.AutoArrayHashMapUnmanaged(void, void), metadata_items: std.MultiArrayList(Metadata.Item), metadata_extra: std.ArrayList(u32), @@ -85,7 +88,7 @@ pub const Options = struct { }; pub const String = enum(u32) { - none = std.math.maxInt(u31), + none = maxInt(u31), empty, _, @@ -245,7 +248,7 @@ pub const Type = enum(u32) { ptr, @"ptr addrspace(4)", - none = std.math.maxInt(u32), + none = maxInt(u32), _, pub const ptr_amdgpu_constant = @@ -941,7 +944,7 @@ pub const Attribute = union(Kind) { inalloca: Type, sret: Type, elementtype: Type, - @"align": Alignment, + @"align": Alignment.Lazy, @"noalias", nocapture, nofree, @@ -956,7 +959,7 @@ pub const Attribute = union(Kind) { immarg, noundef, nofpclass: FpClass, - alignstack: Alignment, + alignstack: Alignment.Lazy, allocalign, allocptr, readnone, @@ -964,7 +967,7 @@ pub const Attribute = union(Kind) { writeonly, // Function Attributes - //alignstack: Alignment, + //alignstack: Alignment.Lazy, allockind: AllocKind, allocsize: AllocSize, alwaysinline, @@ -1145,7 +1148,7 @@ pub const Attribute = union(Kind) { return @unionInit(Attribute, field.name, switch (field.type) { void => {}, u32 => storage.value, - Alignment, String, Type, UwTable => @enumFromInt(storage.value), + Alignment.Lazy, String, Type, UwTable => @enumFromInt(storage.value), AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(storage.value), else => @compileError("bad payload type: " ++ field.name ++ ": " ++ @typeName(field.type)), @@ -1246,7 +1249,7 @@ pub const Attribute = union(Kind) { .sret, .elementtype, => |ty| try w.print(" {s}({f})", .{ @tagName(attribute), ty.fmt(data.builder, .percent) }), - .@"align" => |alignment| try w.print("{f}", .{alignment.fmt(" ")}), + .@"align" => |alignment| try w.print("{f}", .{alignment.resolve(data.builder).fmt(" ")}), .dereferenceable, .dereferenceable_or_null, => |size| try w.print(" {s}({d})", .{ @tagName(attribute), size }), @@ -1270,7 +1273,7 @@ pub const Attribute = union(Kind) { }, .alignstack => |alignment| { try w.print(" {t}", .{attribute}); - const alignment_bytes = alignment.toByteUnits() orelse return; + const alignment_bytes = alignment.resolve(data.builder).toByteUnits() orelse return; if (data.flags.pound) { try w.print("={d}", .{alignment_bytes}); } else { @@ -1435,8 +1438,8 @@ pub const Attribute = union(Kind) { //sanitize_memtag, sanitize_address_dyninit = 102, - string = std.math.maxInt(u31), - none = std.math.maxInt(u32), + string = maxInt(u31), + none = maxInt(u32), _, pub const len = @typeInfo(Kind).@"enum".fields.len - 2; @@ -1516,12 +1519,12 @@ pub const Attribute = union(Kind) { elem_size: u16, num_elems: u16, - pub const none = std.math.maxInt(u16); + pub const none = maxInt(u16); fn toLlvm(self: AllocSize) packed struct(u64) { num_elems: u32, elem_size: u32 } { return .{ .num_elems = switch (self.num_elems) { else => self.num_elems, - none => std.math.maxInt(u32), + none => maxInt(u32), }, .elem_size = self.elem_size }; } }; @@ -1577,7 +1580,7 @@ pub const Attribute = union(Kind) { inline else => |value, tag| .{ .kind = @as(Kind, self), .value = switch (@TypeOf(value)) { void => 0, u32 => value, - Alignment, String, Type, UwTable => @intFromEnum(value), + Alignment.Lazy, String, Type, UwTable => @intFromEnum(value), AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(value), else => @compileError("bad payload type: " ++ @tagName(tag) ++ @typeName(@TypeOf(value))), } }, @@ -2017,9 +2020,32 @@ pub const ExternallyInitialized = enum { }; pub const Alignment = enum(u6) { - default = std.math.maxInt(u6), + default = maxInt(u6), _, + pub const Lazy = enum(u32) { + /// Values which fit in a `u6` are already-resolved `Alignment` values. Other values are + /// indices into `Builder.alignment_forward_references`, offset by `maxInt(u6)`. + _, + + pub fn wrap(a: Alignment) Lazy { + return @enumFromInt(@intFromEnum(a)); + } + pub fn resolve(l: Lazy, b: *const Builder) Alignment { + return switch (@intFromEnum(l)) { + 0...maxInt(u6) => |raw| @enumFromInt(raw), + else => |offset_index| b.alignment_forward_references.items[offset_index - maxInt(u6)], + }; + } + + fn fromFwdRefIndex(index: usize) Lazy { + return @enumFromInt(index + maxInt(u6)); + } + fn toFwdRefIndex(l: Lazy) usize { + return @intFromEnum(l) - maxInt(u6); + } + }; + pub fn fromByteUnits(bytes: u64) Alignment { if (bytes == 0) return .default; assert(std.math.isPowerOfTwo(bytes)); @@ -2028,11 +2054,17 @@ pub const Alignment = enum(u6) { } pub fn toByteUnits(self: Alignment) ?u64 { - return if (self == .default) null else @as(u64, 1) << @intFromEnum(self); + return switch (self) { + .default => null, + else => @as(u64, 1) << @intFromEnum(self), + }; } pub fn toLlvm(self: Alignment) u6 { - return if (self == .default) 0 else (@intFromEnum(self) + 1); + return switch (self) { + .default => 0, + else => @intFromEnum(self) + 1, + }; } pub const Prefixed = struct { @@ -2180,7 +2212,7 @@ pub const CallConv = enum(u10) { }; pub const StrtabString = enum(u32) { - none = std.math.maxInt(u31), + none = maxInt(u31), empty, _, @@ -2308,7 +2340,7 @@ pub const Global = struct { }, pub const Index = enum(u32) { - none = std.math.maxInt(u32), + none = maxInt(u32), _, pub fn unwrap(self: Index, builder: *const Builder) Index { @@ -2478,7 +2510,7 @@ pub const Alias = struct { aliasee: Constant = .no_init, pub const Index = enum(u32) { - none = std.math.maxInt(u32), + none = maxInt(u32), _, pub fn ptr(self: Index, builder: *Builder) *Alias { @@ -2530,7 +2562,7 @@ pub const Variable = struct { alignment: Alignment = .default, pub const Index = enum(u32) { - none = std.math.maxInt(u32), + none = maxInt(u32), _, pub fn ptr(self: Index, builder: *Builder) *Variable { @@ -3949,7 +3981,7 @@ pub const Intrinsic = enum { .params = &.{ .{ .kind = .{ .type = Type.ptr_amdgpu_constant }, - .attrs = &.{.{ .@"align" = Builder.Alignment.fromByteUnits(4) }}, + .attrs = &.{.{ .@"align" = .wrap(.fromByteUnits(4)) }}, }, }, .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, @@ -4057,7 +4089,7 @@ pub const Function = struct { extra: []const u32 = &.{}, pub const Index = enum(u32) { - none = std.math.maxInt(u32), + none = maxInt(u32), _, pub fn ptr(self: Index, builder: *Builder) *Function { @@ -4411,7 +4443,7 @@ pub const Function = struct { }; pub const Index = enum(u32) { - none = std.math.maxInt(u31), + none = maxInt(u31), _, pub fn name(self: Instruction.Index, function: *const Function) String { @@ -5007,7 +5039,7 @@ pub const Function = struct { fsub = 12, fmax = 13, fmin = 14, - none = std.math.maxInt(u5), + none = maxInt(u5), }; }; @@ -6132,8 +6164,8 @@ pub const WipFunction = struct { kind: MemoryAccessKind, @"inline": bool, ) Allocator.Error!Instruction.Index { - var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; - var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = src_align })}; + var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = .wrap(dst_align) })}; + var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = .wrap(src_align) })}; const value = try self.callIntrinsic( .normal, try self.builder.fnAttrs(&.{ @@ -6162,8 +6194,8 @@ pub const WipFunction = struct { len: Value, kind: MemoryAccessKind, ) Allocator.Error!Instruction.Index { - var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; - var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = src_align })}; + var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = .wrap(dst_align) })}; + var src_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = .wrap(src_align) })}; const value = try self.callIntrinsic( .normal, try self.builder.fnAttrs(&.{ @@ -6192,7 +6224,7 @@ pub const WipFunction = struct { kind: MemoryAccessKind, @"inline": bool, ) Allocator.Error!Instruction.Index { - var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = dst_align })}; + var dst_attrs = [_]Attribute.Index{try self.builder.attr(.{ .@"align" = .wrap(dst_align) })}; const value = try self.callIntrinsic( .normal, try self.builder.fnAttrs(&.{ .none, .none, try self.builder.attrs(&dst_attrs) }), @@ -7329,7 +7361,7 @@ pub const Constant = enum(u32) { //indices: [info.indices_len]Constant, pub const Kind = enum { normal, inbounds }; - pub const InRangeIndex = enum(u16) { none = std.math.maxInt(u16), _ }; + pub const InRangeIndex = enum(u16) { none = maxInt(u16), _ }; pub const Info = packed struct(u32) { indices_len: u16, inrange: InRangeIndex }; }; @@ -7579,7 +7611,7 @@ pub const Constant = enum(u32) { string: [ (std.math.big.int.Const{ .limbs = &([1]std.math.big.Limb{ - std.math.maxInt(std.math.big.Limb), + maxInt(std.math.big.Limb), } ** expected_limbs), .positive = false, }).sizeInBaseUpperBound(10) @@ -7643,7 +7675,7 @@ pub const Constant = enum(u32) { std.math.minInt(Exponent64), else => @as(Exponent64, repr.exponent) + (std.math.floatExponentMax(f64) - std.math.floatExponentMax(f32)), - std.math.maxInt(Exponent32) => std.math.maxInt(Exponent64), + maxInt(Exponent32) => maxInt(Exponent64), }, .sign = repr.sign, }))}); @@ -7820,7 +7852,7 @@ pub const Constant = enum(u32) { }; pub const Value = enum(u32) { - none = std.math.maxInt(u31), + none = maxInt(u31), false = first_constant + @intFromEnum(Constant.false), true = first_constant + @intFromEnum(Constant.true), @"0" = first_constant + @intFromEnum(Constant.@"0"), @@ -8688,6 +8720,8 @@ pub fn init(options: Options) Allocator.Error!Builder { .constant_extra = .empty, .constant_limbs = .empty, + .alignment_forward_references = .empty, + .metadata_map = .empty, .metadata_items = .empty, .metadata_extra = .empty, @@ -8800,51 +8834,55 @@ pub fn clearAndFree(self: *Builder) void { } pub fn deinit(self: *Builder) void { - self.module_asm.deinit(self.gpa); + const gpa = self.gpa; - self.string_map.deinit(self.gpa); - self.string_indices.deinit(self.gpa); - self.string_bytes.deinit(self.gpa); + self.module_asm.deinit(gpa); - self.types.deinit(self.gpa); - self.next_unique_type_id.deinit(self.gpa); - self.type_map.deinit(self.gpa); - self.type_items.deinit(self.gpa); - self.type_extra.deinit(self.gpa); + self.string_map.deinit(gpa); + self.string_indices.deinit(gpa); + self.string_bytes.deinit(gpa); - self.attributes.deinit(self.gpa); - self.attributes_map.deinit(self.gpa); - self.attributes_indices.deinit(self.gpa); - self.attributes_extra.deinit(self.gpa); + self.types.deinit(gpa); + self.next_unique_type_id.deinit(gpa); + self.type_map.deinit(gpa); + self.type_items.deinit(gpa); + self.type_extra.deinit(gpa); - self.function_attributes_set.deinit(self.gpa); + self.attributes.deinit(gpa); + self.attributes_map.deinit(gpa); + self.attributes_indices.deinit(gpa); + self.attributes_extra.deinit(gpa); - self.globals.deinit(self.gpa); - self.next_unique_global_id.deinit(self.gpa); - self.aliases.deinit(self.gpa); - self.variables.deinit(self.gpa); - for (self.functions.items) |*function| function.deinit(self.gpa); - self.functions.deinit(self.gpa); + self.function_attributes_set.deinit(gpa); - self.strtab_string_map.deinit(self.gpa); - self.strtab_string_indices.deinit(self.gpa); - self.strtab_string_bytes.deinit(self.gpa); + self.globals.deinit(gpa); + self.next_unique_global_id.deinit(gpa); + self.aliases.deinit(gpa); + self.variables.deinit(gpa); + for (self.functions.items) |*function| function.deinit(gpa); + self.functions.deinit(gpa); - self.constant_map.deinit(self.gpa); - self.constant_items.deinit(self.gpa); - self.constant_extra.deinit(self.gpa); - self.constant_limbs.deinit(self.gpa); + self.strtab_string_map.deinit(gpa); + self.strtab_string_indices.deinit(gpa); + self.strtab_string_bytes.deinit(gpa); - self.metadata_map.deinit(self.gpa); - self.metadata_items.deinit(self.gpa); - self.metadata_extra.deinit(self.gpa); - self.metadata_limbs.deinit(self.gpa); - self.metadata_forward_references.deinit(self.gpa); - self.metadata_named.deinit(self.gpa); + self.constant_map.deinit(gpa); + self.constant_items.deinit(gpa); + self.constant_extra.deinit(gpa); + self.constant_limbs.deinit(gpa); - self.metadata_string_map.deinit(self.gpa); - self.metadata_string_indices.deinit(self.gpa); - self.metadata_string_bytes.deinit(self.gpa); + self.alignment_forward_references.deinit(gpa); + + self.metadata_map.deinit(gpa); + self.metadata_items.deinit(gpa); + self.metadata_extra.deinit(gpa); + self.metadata_limbs.deinit(gpa); + self.metadata_forward_references.deinit(gpa); + self.metadata_named.deinit(gpa); + + self.metadata_string_map.deinit(gpa); + self.metadata_string_indices.deinit(gpa); + self.metadata_string_bytes.deinit(gpa); self.* = undefined; } @@ -8962,7 +9000,7 @@ pub fn structType( pub fn opaqueType(self: *Builder, name: String) Allocator.Error!Type { try self.string_map.ensureUnusedCapacity(self.gpa, 1); if (name.slice(self)) |id| { - const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)}); + const count: usize = comptime std.fmt.count("{d}", .{maxInt(u32)}); try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); } try self.string_indices.ensureUnusedCapacity(self.gpa, 1); @@ -9578,6 +9616,21 @@ pub fn asmValue( return (try self.asmConst(ty, info, assembly, constraints)).toValue(); } +/// The initial "resolved" value of the forward reference is `Alignment.default`. +pub fn alignmentForwardReference(b: *Builder) Allocator.Error!Alignment.Lazy { + const index = b.alignment_forward_references.items.len; + try b.alignment_forward_references.append(b.gpa, .default); + return .fromFwdRefIndex(index); +} + +/// Updates the "resolved" value of the alignment forward reference `fwd_ref` to `value`. +/// +/// Asserts that `fwd_ref` is a forward reference, as opposed to a resolved alignment value. +pub fn resolveAlignmentForwardReference(b: *Builder, fwd_ref: Alignment.Lazy, value: Alignment) void { + const index = fwd_ref.toFwdRefIndex(); + b.alignment_forward_references.items[index] = value; +} + pub fn dump(b: *Builder, io: Io) void { var buffer: [4000]u8 = undefined; const stderr: Io.File = .stderr(); @@ -10515,7 +10568,7 @@ pub fn print(self: *Builder, w: *Writer) (Writer.Error || Allocator.Error)!void string: [ (std.math.big.int.Const{ .limbs = &([1]std.math.big.Limb{ - std.math.maxInt(std.math.big.Limb), + maxInt(std.math.big.Limb), } ** expected_limbs), .positive = false, }).sizeInBaseUpperBound(10) @@ -10665,7 +10718,7 @@ fn printEscapedString(slice: []const u8, quotes: QuoteBehavior, w: *Writer) Writ fn ensureUnusedGlobalCapacity(self: *Builder, name: StrtabString) Allocator.Error!void { try self.strtab_string_map.ensureUnusedCapacity(self.gpa, 1); if (name.slice(self)) |id| { - const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)}); + const count: usize = comptime std.fmt.count("{d}", .{maxInt(u32)}); try self.strtab_string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); } try self.strtab_string_indices.ensureUnusedCapacity(self.gpa, 1); @@ -13518,7 +13571,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator, producer: Producer) bitco try record.ensureUnusedCapacity(self.gpa, 3); record.appendAssumeCapacity(1); record.appendAssumeCapacity(@intFromEnum(kind)); - record.appendAssumeCapacity(alignment.toByteUnits() orelse 0); + record.appendAssumeCapacity(alignment.resolve(self).toByteUnits() orelse 0); }, .dereferenceable, .dereferenceable_or_null, diff --git a/src/Sema.zig b/src/Sema.zig index 02c2efd2ee..9e3024b7ed 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -7110,12 +7110,7 @@ fn analyzeCall( } for (args, 0..) |arg, arg_idx| { const arg_src = args_info.argSrc(block, arg_idx); - const arg_ty = sema.typeOf(arg); try sema.validateRuntimeValue(block, arg_src, arg); - if (arg_ty.isPtrAtRuntime(zcu) or arg_ty.isSliceAtRuntime(zcu)) { - // LLVM wants this information for an "align" attribute on the argument. - try sema.ensureLayoutResolved(arg_ty.nullablePtrElem(zcu), arg_src, .init); - } } const runtime_func: Air.Inst.Ref, const runtime_args: []const Air.Inst.Ref = func: { if (!any_generic_types and !any_comptime_params) break :func .{ callee, args }; @@ -24779,16 +24774,6 @@ fn zirBuiltinExtern( } const ptr_info = ty.ptrInfo(zcu); - if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") { - const func_type = ip.indexToKey(ptr_info.child).func_type; - for (func_type.param_types.get(ip)) |param_ty_ip| { - const param_ty: Type = .fromInterned(param_ty_ip); - if (param_ty.isPtrAtRuntime(zcu) or param_ty.isSliceAtRuntime(zcu)) { - // LLVM wants this information for an "align" attribute on the parameter. - try sema.ensureLayoutResolved(param_ty.nullablePtrElem(zcu), ty_src, .parameter); - } - } - } const extern_val = try pt.getExtern(.{ .name = options.name, .ty = ptr_info.child, diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index bf45502fac..9fde840232 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -1832,16 +1832,6 @@ fn analyzeNavVal( const lib_name_src = block.src(.{ .node_offset_lib_name = .zero }); try sema.handleExternLibName(&block, lib_name_src, l); } - if (nav_ty.zigTypeTag(zcu) == .@"fn") { - const func_type = ip.indexToKey(nav_ty.toIntern()).func_type; - for (func_type.param_types.get(ip)) |param_ty_ip| { - const param_ty: Type = .fromInterned(param_ty_ip); - if (param_ty.isPtrAtRuntime(zcu) or param_ty.isSliceAtRuntime(zcu)) { - // LLVM wants this information for an "align" attribute on the parameter. - try sema.ensureLayoutResolved(param_ty.nullablePtrElem(zcu), ty_src, .parameter); - } - } - } break :val .fromInterned(try pt.getExtern(.{ .name = old_nav.name, .ty = nav_ty.toIntern(), @@ -3398,10 +3388,6 @@ fn analyzeFuncBodyInner( const param_ty_src = inner_block.src(.{ .func_decl_param_ty = @intCast(zir_param_index) }); try sema.ensureLayoutResolved(param_ty, param_ty_src, .parameter); - if (param_ty.isPtrAtRuntime(zcu) or param_ty.isSliceAtRuntime(zcu)) { - // LLVM wants this information for an "align" attribute on the parameter. - try sema.ensureLayoutResolved(param_ty.nullablePtrElem(zcu), param_ty_src, .parameter); - } if (try param_ty.onePossibleValue(pt)) |opv| { gop.value_ptr.* = .fromValue(opv); continue; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 972a536eef..009e472d06 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -520,6 +520,21 @@ pub const Object = struct { gpa: Allocator, builder: Builder, + /// This pool contains only types (and not `@as(type, undefined)`). It has two purposes: + /// + /// * Lazily tracking ABI alignment of types, so that `@"align"` attributes can be set to a + /// type's ABI alignment before that type is fully resolved. Each type in the pool has a + /// corresponding entry in `lazy_abi_aligns`. + /// + /// * If `!Object.builder.strip`, lazily tracking debug information types, so that debug + /// information can handle indirect self-reference (and so that debug information works + /// correctly across incremental updates). Each type has a corresponding entry in + /// `debug_types`, provided that `Object.builder.strip` is `false`. + type_pool: link.ConstPool, + + /// Keyed on `link.ConstPool.Index`. + lazy_abi_aligns: std.ArrayList(Builder.Alignment.Lazy), + debug_compile_unit: Builder.Metadata.Optional, debug_enums_fwd_ref: Builder.Metadata.Optional, @@ -530,8 +545,6 @@ pub const Object = struct { debug_file_map: std.AutoHashMapUnmanaged(Zcu.File.Index, Builder.Metadata), - /// This pool *only* contains types (and does not contain `@as(type, undefined)`). - debug_type_pool: link.ConstPool, /// Keyed on `link.ConstPool.Index`. debug_types: std.ArrayList(Builder.Metadata), /// Initially `.none`, set if the type `anyerror` is lowered to a debug type. The type will not @@ -660,13 +673,14 @@ pub const Object = struct { obj.* = .{ .gpa = gpa, .builder = builder, + .type_pool = .empty, + .lazy_abi_aligns = .empty, .debug_compile_unit = debug_compile_unit, .debug_enums_fwd_ref = debug_enums_fwd_ref, .debug_globals_fwd_ref = debug_globals_fwd_ref, .debug_enums = .empty, .debug_globals = .empty, .debug_file_map = .empty, - .debug_type_pool = .empty, .debug_types = .empty, .debug_anyerror_fwd_ref = .none, .target = target, @@ -685,10 +699,11 @@ pub const Object = struct { pub fn deinit(self: *Object) void { const gpa = self.gpa; + self.type_pool.deinit(gpa); + self.lazy_abi_aligns.deinit(gpa); self.debug_enums.deinit(gpa); self.debug_globals.deinit(gpa); self.debug_file_map.deinit(gpa); - self.debug_type_pool.deinit(gpa); self.debug_types.deinit(gpa); self.nav_map.deinit(gpa); self.uav_map.deinit(gpa); @@ -836,7 +851,7 @@ pub const Object = struct { o.builder.resolveDebugForwardReference(fwd_ref, debug_anyerror_type); } - try o.flushPendingDebugTypes(pt); + try o.flushTypePool(pt); o.builder.resolveDebugForwardReference( o.debug_enums_fwd_ref.unwrap().?, @@ -1396,10 +1411,10 @@ pub const Object = struct { if (ptr_info.flags.is_const) { try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); } - const elem_align = (if (ptr_info.flags.alignment != .none) - @as(InternPool.Alignment, ptr_info.flags.alignment) - else - Type.fromInterned(ptr_info.child).abiAlignment(zcu).max(.@"1")).toLlvm(); + const elem_align: Builder.Alignment.Lazy = switch (ptr_info.flags.alignment) { + else => |a| .wrap(a.toLlvm()), + .none => try o.lazyAbiAlignment(pt, .fromInterned(ptr_info.child)), + }; try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); const ptr_param = wip.arg(llvm_arg_i); llvm_arg_i += 1; @@ -1600,7 +1615,7 @@ pub const Object = struct { } try fg.wip.finish(); - try o.flushPendingDebugTypes(pt); + try o.flushTypePool(pt); } pub fn updateNav(self: *Object, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { @@ -1617,11 +1632,11 @@ pub const Object = struct { }, else => |e| return e, }; - try self.flushPendingDebugTypes(pt); + try self.flushTypePool(pt); } - fn flushPendingDebugTypes(o: *Object, pt: Zcu.PerThread) Allocator.Error!void { - try o.debug_type_pool.flushPending(pt, .{ .llvm = o }); + fn flushTypePool(o: *Object, pt: Zcu.PerThread) Allocator.Error!void { + try o.type_pool.flushPending(pt, .{ .llvm = o }); } pub fn updateExports( @@ -1818,51 +1833,81 @@ pub const Object = struct { } pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void { - if (!o.builder.strip) { - try o.debug_type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success); - } + try o.type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success); } /// Should only be called by the `link.ConstPool` implementation. /// - /// `val` is always a type because `o.debug_type_pool` only contains types. + /// `val` is always a type because `o.type_pool` only contains types. pub fn addConst(o: *Object, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void { const zcu = pt.zcu; const gpa = zcu.comp.gpa; assert(zcu.intern_pool.typeOf(val) == .type_type); - assert(@intFromEnum(index) == o.debug_types.items.len); - try o.debug_types.ensureUnusedCapacity(gpa, 1); - const fwd_ref = try o.builder.debugForwardReference(); - o.debug_types.appendAssumeCapacity(fwd_ref); - if (val == .anyerror_type) { - assert(o.debug_anyerror_fwd_ref.is_none); - o.debug_anyerror_fwd_ref = fwd_ref.toOptional(); + + { + assert(@intFromEnum(index) == o.lazy_abi_aligns.items.len); + try o.lazy_abi_aligns.ensureUnusedCapacity(gpa, 1); + const fwd_ref = try o.builder.alignmentForwardReference(); + o.lazy_abi_aligns.appendAssumeCapacity(fwd_ref); + } + + if (!o.builder.strip) { + assert(@intFromEnum(index) == o.debug_types.items.len); + try o.debug_types.ensureUnusedCapacity(gpa, 1); + const fwd_ref = try o.builder.debugForwardReference(); + o.debug_types.appendAssumeCapacity(fwd_ref); + if (val == .anyerror_type) { + assert(o.debug_anyerror_fwd_ref.is_none); + o.debug_anyerror_fwd_ref = fwd_ref.toOptional(); + } } } /// Should only be called by the `link.ConstPool` implementation. /// - /// `val` is always a type because `o.debug_type_pool` only contains types. + /// `val` is always a type because `o.type_pool` only contains types. pub fn updateConstIncomplete(o: *Object, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void { - assert(pt.zcu.intern_pool.typeOf(val) == .type_type); - const fwd_ref = o.debug_types.items[@intFromEnum(index)]; - assert(val != .anyerror_type); - const name_str = try o.builder.metadataStringFmt("{f}", .{Type.fromInterned(val).fmt(pt)}); - const debug_incomplete_type = try o.builder.debugSignedType(name_str, 0); - o.builder.resolveDebugForwardReference(fwd_ref, debug_incomplete_type); + const zcu = pt.zcu; + assert(zcu.intern_pool.typeOf(val) == .type_type); + + const ty: Type = .fromInterned(val); + + { + const fwd_ref = o.lazy_abi_aligns.items[@intFromEnum(index)]; + o.builder.resolveAlignmentForwardReference(fwd_ref, .fromByteUnits(1)); + } + + if (!o.builder.strip) { + assert(val != .anyerror_type); + const fwd_ref = o.debug_types.items[@intFromEnum(index)]; + const name_str = try o.builder.metadataStringFmt("{f}", .{ty.fmt(pt)}); + const debug_incomplete_type = try o.builder.debugSignedType(name_str, 0); + o.builder.resolveDebugForwardReference(fwd_ref, debug_incomplete_type); + } } /// Should only be called by the `link.ConstPool` implementation. /// - /// `val` is always a type because `o.debug_type_pool` only contains types. + /// `val` is always a type because `o.type_pool` only contains types. pub fn updateConst(o: *Object, pt: Zcu.PerThread, index: link.ConstPool.Index, val: InternPool.Index) Allocator.Error!void { - assert(pt.zcu.intern_pool.typeOf(val) == .type_type); - const fwd_ref = o.debug_types.items[@intFromEnum(index)]; - if (val == .anyerror_type) { - // Don't lower this now; it will be populated in `emit` instead. - assert(o.debug_anyerror_fwd_ref == fwd_ref.toOptional()); - return; + const zcu = pt.zcu; + assert(zcu.intern_pool.typeOf(val) == .type_type); + + const ty: Type = .fromInterned(val); + + { + const fwd_ref = o.lazy_abi_aligns.items[@intFromEnum(index)]; + o.builder.resolveAlignmentForwardReference(fwd_ref, ty.abiAlignment(zcu).toLlvm()); + } + + if (!o.builder.strip) { + const fwd_ref = o.debug_types.items[@intFromEnum(index)]; + if (val == .anyerror_type) { + // Don't lower this now; it will be populated in `emit` instead. + assert(o.debug_anyerror_fwd_ref == fwd_ref.toOptional()); + } else { + const debug_type = try o.lowerDebugType(pt, ty, fwd_ref); + o.builder.resolveDebugForwardReference(fwd_ref, debug_type); + } } - const debug_type = try o.lowerDebugType(pt, .fromInterned(val), fwd_ref); - o.builder.resolveDebugForwardReference(fwd_ref, debug_type); } fn getDebugFile(o: *Object, pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!Builder.Metadata { @@ -1883,7 +1928,7 @@ pub const Object = struct { fn getDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Metadata { assert(!o.builder.strip); - const index = try o.debug_type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); + const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); return o.debug_types.items[@intFromEnum(index)]; } @@ -2680,7 +2725,7 @@ pub const Object = struct { function_index.setCallConv(cc_info.llvm_cc, &o.builder); if (cc_info.align_stack) { - try attributes.addFnAttr(.{ .alignstack = .fromByteUnits(target.stackAlignment()) }, &o.builder); + try attributes.addFnAttr(.{ .alignstack = .wrap(.fromByteUnits(target.stackAlignment())) }, &o.builder); } else { _ = try attributes.removeFnAttr(.alignstack); } @@ -4166,11 +4211,11 @@ pub const Object = struct { if (ptr_info.flags.is_const) { try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); } - const elem_align = if (ptr_info.flags.alignment != .none) - ptr_info.flags.alignment - else - Type.fromInterned(ptr_info.child).abiAlignment(zcu).max(.@"1"); - try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align.toLlvm() }, &o.builder); + const elem_align: Builder.Alignment.Lazy = switch (ptr_info.flags.alignment) { + else => |a| .wrap(a.toLlvm()), + .none => try o.lazyAbiAlignment(pt, .fromInterned(ptr_info.child)), + }; + try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); } else if (ccAbiPromoteInt(fn_info.cc, zcu, param_ty)) |s| switch (s) { .signed => try attributes.addParamAttr(llvm_arg_i, .signext, &o.builder), .unsigned => try attributes.addParamAttr(llvm_arg_i, .zeroext, &o.builder), @@ -4187,7 +4232,7 @@ pub const Object = struct { ) Allocator.Error!void { try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); - try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = alignment }, &o.builder); + try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = .wrap(alignment) }, &o.builder); if (byval) try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder); } @@ -4297,6 +4342,11 @@ pub const Object = struct { try wip.finish(); return function_index; } + + fn lazyAbiAlignment(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Alignment.Lazy { + const index = try o.type_pool.get(pt, .{ .llvm = o }, ty.toIntern()); + return o.lazy_abi_aligns.items[@intFromEnum(index)]; + } }; pub const NavGen = struct { @@ -5259,10 +5309,10 @@ pub const FuncGen = struct { if (ptr_info.flags.is_const) { try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); } - const elem_align = (if (ptr_info.flags.alignment != .none) - @as(InternPool.Alignment, ptr_info.flags.alignment) - else - Type.fromInterned(ptr_info.child).abiAlignment(zcu).max(.@"1")).toLlvm(); + const elem_align: Builder.Alignment.Lazy = switch (ptr_info.flags.alignment) { + else => |a| .wrap(a.toLlvm()), + .none => try o.lazyAbiAlignment(pt, .fromInterned(ptr_info.child)), + }; try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); }, };