zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit f7a1ccfc56ed2d5605aa6ee25ac8d609f4c0d2a5 (tree)
parent 0a7387c41004b8378c75e476c8a4bd058b15030d
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date:   Thu, 12 Feb 2026 11:10:52 +0000

compiler: fix up LLVM backend, and improve its debug info

The LLVM backend can now run the behavior tests and standard library
tests, like the x86_64 backend can. This commit required me to make a
lot of changes to how the LLVM backend lowers debug information, and
while I was doing that, I improved a few things:

* `anyerror` is now an enum type (and other error sets just wrap it), so
  error values appear by name in debuggers

* Fixed broken lowering for tagged unions with zero-width payloads

* Associate container types with source locations in all cases

* Avoid depending on the order of type resolution (using the new
  `DebugConstPool` abstraction), so debug information will contain all
  available type information rather than just the subset which happens
  to be resolved when the backend lowers that debug type

Diffstat:
Mlib/std/zig/llvm/BitcodeReader.zig | 10+++++-----
Mlib/std/zig/llvm/Builder.zig | 107++++++++++++++++++++++++++++++++++++++++---------------------------------------
Msrc/Sema.zig | 29++++++++++++++++++++++++++++-
Msrc/Zcu/PerThread.zig | 8+++++++-
Msrc/codegen/aarch64/abi.zig | 2+-
Msrc/codegen/arm/abi.zig | 24+++++++++++++-----------
Msrc/codegen/llvm.zig | 1615++++++++++++++++++++++++++++++++++++-------------------------------------------
Msrc/codegen/mips/abi.zig | 2+-
Msrc/codegen/riscv64/abi.zig | 4++--
Msrc/codegen/wasm/abi.zig | 6+++---
Msrc/link.zig | 6++++--
Msrc/link/DebugConstPool.zig | 3+++
Msrc/link/Dwarf.zig | 2+-
13 files changed, 851 insertions(+), 967 deletions(-)

diff --git a/lib/std/zig/llvm/BitcodeReader.zig b/lib/std/zig/llvm/BitcodeReader.zig @@ -34,8 +34,8 @@ pub const Block = struct { const default: Info = .{ .block_name = &.{}, - .record_names = .{}, - .abbrevs = .{ .abbrevs = .{} }, + .record_names = .empty, + .abbrevs = .{ .abbrevs = .empty }, }; const set_bid_id: u32 = 1; @@ -109,8 +109,8 @@ pub fn init(allocator: std.mem.Allocator, options: InitOptions) BitcodeReader { .keep_names = options.keep_names, .bit_buffer = 0, .bit_offset = 0, - .stack = .{}, - .block_info = .{}, + .stack = .empty, + .block_info = .empty, }; } @@ -278,7 +278,7 @@ fn startBlock(bc: *BitcodeReader, block_id: ?u32, new_abbrev_len: u6) !void { state.* = .{ .block_id = block_id, .abbrev_id_width = new_abbrev_len, - .abbrevs = .{ .abbrevs = .{} }, + .abbrevs = .{ .abbrevs = .empty }, }; try state.abbrevs.abbrevs.ensureTotalCapacity( bc.allocator, diff --git a/lib/std/zig/llvm/Builder.zig b/lib/std/zig/llvm/Builder.zig @@ -1627,7 +1627,7 @@ pub const FunctionAttributes = enum(u32) { const params_index = 2; pub const Wip = struct { - maps: Maps = .{}, + maps: Maps = .empty, const Map = std.AutoArrayHashMapUnmanaged(Attribute.Kind, Attribute.Index); const Maps = std.ArrayList(Map); @@ -4048,7 +4048,7 @@ pub const Function = struct { section: String = .none, alignment: Alignment = .default, blocks: []const Block = &.{}, - instructions: std.MultiArrayList(Instruction) = .{}, + instructions: std.MultiArrayList(Instruction) = .empty, names: [*]const String = &[0]String{}, value_indices: [*]const u32 = &[0]u32{}, strip: bool, @@ -5222,13 +5222,13 @@ pub const WipFunction = struct { .prev_debug_location = .no_location, .debug_location = .no_location, .cursor = undefined, - .blocks = .{}, - .instructions = .{}, - .names = .{}, + .blocks = .empty, + .instructions = .empty, + .names = .empty, .strip = options.strip, - .debug_locations = .{}, - .debug_values = .{}, - .extra = .{}, + .debug_locations = .empty, + .debug_values = .empty, + .extra = .empty, }; errdefer self.deinit(); @@ -5265,7 +5265,7 @@ pub const WipFunction = struct { self.blocks.appendAssumeCapacity(.{ .name = final_name, .incoming = incoming, - .instructions = .{}, + .instructions = .empty, }); return index; } @@ -6325,7 +6325,7 @@ pub const WipFunction = struct { function.blocks = &.{}; gpa.free(function.names[0..function.instructions.len]); function.debug_locations.deinit(gpa); - function.debug_locations = .{}; + function.debug_locations = .empty; gpa.free(function.debug_values); function.debug_values = &.{}; gpa.free(function.extra); @@ -8391,7 +8391,7 @@ pub const Metadata = packed struct(u32) { map: std.AutoArrayHashMapUnmanaged(union(enum) { metadata: Metadata, debug_location: DebugLocation.Location, - }, void) = .{}, + }, void) = .empty, const FormatData = struct { formatter: *Formatter, @@ -8649,52 +8649,52 @@ pub fn init(options: Options) Allocator.Error!Builder { .source_filename = .none, .data_layout = .none, .target_triple = .none, - .module_asm = .{}, + .module_asm = .empty, - .string_map = .{}, - .string_indices = .{}, - .string_bytes = .{}, + .string_map = .empty, + .string_indices = .empty, + .string_bytes = .empty, - .types = .{}, + .types = .empty, .next_unnamed_type = @enumFromInt(0), - .next_unique_type_id = .{}, - .type_map = .{}, - .type_items = .{}, - .type_extra = .{}, + .next_unique_type_id = .empty, + .type_map = .empty, + .type_items = .empty, + .type_extra = .empty, - .attributes = .{}, - .attributes_map = .{}, - .attributes_indices = .{}, - .attributes_extra = .{}, + .attributes = .empty, + .attributes_map = .empty, + .attributes_indices = .empty, + .attributes_extra = .empty, - .function_attributes_set = .{}, + .function_attributes_set = .empty, - .globals = .{}, + .globals = .empty, .next_unnamed_global = @enumFromInt(0), .next_replaced_global = .none, - .next_unique_global_id = .{}, - .aliases = .{}, - .variables = .{}, - .functions = .{}, - - .strtab_string_map = .{}, - .strtab_string_indices = .{}, - .strtab_string_bytes = .{}, - - .constant_map = .{}, - .constant_items = .{}, - .constant_extra = .{}, - .constant_limbs = .{}, - - .metadata_map = .{}, - .metadata_items = .{}, - .metadata_extra = .{}, - .metadata_limbs = .{}, - .metadata_forward_references = .{}, - .metadata_named = .{}, - .metadata_string_map = .{}, - .metadata_string_indices = .{}, - .metadata_string_bytes = .{}, + .next_unique_global_id = .empty, + .aliases = .empty, + .variables = .empty, + .functions = .empty, + + .strtab_string_map = .empty, + .strtab_string_indices = .empty, + .strtab_string_bytes = .empty, + + .constant_map = .empty, + .constant_items = .empty, + .constant_extra = .empty, + .constant_limbs = .empty, + + .metadata_map = .empty, + .metadata_items = .empty, + .metadata_extra = .empty, + .metadata_limbs = .empty, + .metadata_forward_references = .empty, + .metadata_named = .empty, + .metadata_string_map = .empty, + .metadata_string_indices = .empty, + .metadata_string_bytes = .empty, }; errdefer self.deinit(); @@ -12069,7 +12069,7 @@ pub fn trailingMetadataStringAssumeCapacity(self: *Builder) Metadata.String { const start = self.metadata_string_indices.getLast(); const bytes: []const u8 = self.metadata_string_bytes.items[start..]; assert(bytes.len > 0); - const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); + const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted(bytes, Metadata.String.Adapter{ .builder = self }); if (gop.found_existing) { self.metadata_string_bytes.shrinkRetainingCapacity(start); } else { @@ -12467,11 +12467,12 @@ pub fn metadataConstant(self: *Builder, value: Constant) Allocator.Error!Metadat return self.metadataConstantAssumeCapacity(value); } +/// Resolves the given forward reference to the given value (which is not itself a forward +/// reference). If the forward reference is already resolved, its target is replaced. pub fn resolveDebugForwardReference(self: *Builder, fwd_ref: Metadata, value: Metadata) void { assert(fwd_ref.kind == .forward); - const resolved = &self.metadata_forward_references.items[fwd_ref.index]; - assert(resolved.is_none); - resolved.* = value.toOptional(); + assert(value.kind != .forward); + self.metadata_forward_references.items[fwd_ref.index] = value.toOptional(); } fn metadataSimpleAssumeCapacity(self: *Builder, tag: Metadata.Tag, value: anytype) Metadata { diff --git a/src/Sema.zig b/src/Sema.zig @@ -7096,7 +7096,13 @@ fn analyzeCall( } } for (args, 0..) |arg, arg_idx| { - try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg); + 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 }; @@ -24270,6 +24276,13 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src); + const comptime_only_elem = switch (dest_elem_ty.classify(zcu)) { + .no_possible_value => unreachable, // `elem` is a value of this type + .one_possible_value => return, // no work to do + .runtime => false, + .partially_comptime, .fully_comptime => true, + }; + const runtime_src = rs: { const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls), dest_src); const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src; @@ -24299,6 +24312,15 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void return sema.storePtrVal(block, src, array_ptr_val, array_val, array_ty); }; + if (comptime_only_elem) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "cannot store comptime-only element '{f}' at runtime", .{dest_elem_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(dest_src, msg, "operation is runtime due to destination pointer", .{}); + break :msg msg; + }); + } + try sema.requireRuntimeBlock(block, src, runtime_src); try sema.validateRuntimeValue(block, dest_src, dest_ptr); try sema.validateRuntimeValue(block, value_src, elem); @@ -27063,6 +27085,11 @@ fn elemPtrArray( try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); } + if (array_ty.childType(zcu).abiSize(zcu) == 0) { + // zero-bit child type; just bitcast the pointer + return block.addBitCast(elem_ptr_ty, array_ptr); + } + return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); } diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig @@ -2926,7 +2926,13 @@ fn analyzeFuncBodyInner( const param_ty: Type = .fromInterned(fn_ty_info.param_types.get(ip)[runtime_param_index]); runtime_param_index += 1; - try sema.ensureLayoutResolved(param_ty, inner_block.src(.{ .func_decl_param_ty = @intCast(zir_param_index) }), .parameter); + 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/aarch64/abi.zig b/src/codegen/aarch64/abi.zig @@ -13,7 +13,7 @@ pub const Class = union(enum) { /// For `float_array` the second element will be the amount of floats. pub fn classifyType(ty: Type, zcu: *Zcu) Class { - assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(ty.hasRuntimeBits(zcu)); var maybe_float_bits: ?u16 = null; switch (ty.zigTypeTag(zcu)) { diff --git a/src/codegen/arm/abi.zig b/src/codegen/arm/abi.zig @@ -23,7 +23,7 @@ pub const Class = union(enum) { pub const Context = enum { ret, arg }; pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { - assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(ty.hasRuntimeBits(zcu)); var maybe_float_bits: ?u16 = null; const max_byval_size = 512; @@ -39,22 +39,22 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { const float_count = countFloats(ty, zcu, &maybe_float_bits); if (float_count <= byval_float_count) return .byval; + if (ty.abiAlignment(zcu).compare(.gt, .@"32")) { + return Class.arrSize(bit_size, 64); + } + const fields = ty.structFieldCount(zcu); var i: u32 = 0; while (i < fields) : (i += 1) { const field_ty = ty.fieldType(i, zcu); - const field_alignment = ty.fieldAlignment(i, zcu); - const field_size = field_ty.bitSize(zcu); - if (field_size > 32 or field_alignment.compare(.gt, .@"32")) { - return Class.arrSize(bit_size, 64); - } + if (field_ty.bitSize(zcu) > 32) return Class.arrSize(bit_size, 64); } return Class.arrSize(bit_size, 32); }, .@"union" => { const bit_size = ty.bitSize(zcu); const union_obj = zcu.typeToUnion(ty).?; - if (union_obj.flagsUnordered(ip).layout == .@"packed") { + if (union_obj.layout == .@"packed") { if (bit_size > 64) return .memory; return .byval; } @@ -62,10 +62,12 @@ pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { const float_count = countFloats(ty, zcu, &maybe_float_bits); if (float_count <= byval_float_count) return .byval; - for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| { - if (Type.fromInterned(field_ty).bitSize(zcu) > 32 or - ty.fieldAlignment(field_index, zcu).compare(.gt, .@"32")) - { + if (union_obj.alignment.compareStrict(.gt, .@"32")) { + return Class.arrSize(bit_size, 64); + } + + for (union_obj.field_types.get(ip)) |field_ty| { + if (Type.fromInterned(field_ty).bitSize(zcu) > 32) { return Class.arrSize(bit_size, 64); } } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig @@ -23,6 +23,7 @@ const Package = @import("../Package.zig"); const Air = @import("../Air.zig"); const Value = @import("../Value.zig"); const Type = @import("../Type.zig"); +const DebugConstPool = link.DebugConstPool; const codegen = @import("../codegen.zig"); const x86_64_abi = @import("x86_64/abi.zig"); const wasm_c_abi = @import("wasm/abi.zig"); @@ -529,9 +530,15 @@ pub const Object = struct { debug_globals: std.ArrayList(Builder.Metadata), debug_file_map: std.AutoHashMapUnmanaged(Zcu.File.Index, Builder.Metadata), - debug_type_map: std.AutoHashMapUnmanaged(InternPool.Index, Builder.Metadata), - debug_unresolved_namespace_scopes: std.AutoArrayHashMapUnmanaged(InternPool.NamespaceIndex, Builder.Metadata), + /// This pool *only* contains types (and does not contain `@as(type, undefined)`). + debug_type_pool: DebugConstPool, + /// Keyed on `DebugConstPool.Index`. + debug_types: std.ArrayList(Builder.Metadata), + /// Initially `.none`, set if the type `anyerror` is lowered to a debug type. The type will not + /// actually be created until `emit`, which must resolve this reference with an appropriate enum + /// type from the global error set. + debug_anyerror_fwd_ref: Builder.Metadata.Optional, target: *const std.Target, /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function, @@ -657,21 +664,22 @@ pub const Object = struct { .debug_compile_unit = debug_compile_unit, .debug_enums_fwd_ref = debug_enums_fwd_ref, .debug_globals_fwd_ref = debug_globals_fwd_ref, - .debug_enums = .{}, - .debug_globals = .{}, - .debug_file_map = .{}, - .debug_type_map = .{}, - .debug_unresolved_namespace_scopes = .{}, + .debug_enums = .empty, + .debug_globals = .empty, + .debug_file_map = .empty, + .debug_type_pool = .empty, + .debug_types = .empty, + .debug_anyerror_fwd_ref = .none, .target = target, - .nav_map = .{}, - .uav_map = .{}, - .enum_tag_name_map = .{}, - .named_enum_map = .{}, - .type_map = .{}, + .nav_map = .empty, + .uav_map = .empty, + .enum_tag_name_map = .empty, + .named_enum_map = .empty, + .type_map = .empty, .error_name_table = .none, .null_opt_usize = .no_init, - .struct_field_map = .{}, - .used = .{}, + .struct_field_map = .empty, + .used = .empty, }; return obj; } @@ -681,8 +689,8 @@ pub const Object = struct { self.debug_enums.deinit(gpa); self.debug_globals.deinit(gpa); self.debug_file_map.deinit(gpa); - self.debug_type_map.deinit(gpa); - self.debug_unresolved_namespace_scopes.deinit(gpa); + self.debug_type_pool.deinit(gpa); + self.debug_types.deinit(gpa); self.nav_map.deinit(gpa); self.uav_map.deinit(gpa); self.enum_tag_name_map.deinit(gpa); @@ -824,19 +832,13 @@ pub const Object = struct { } if (!o.builder.strip) { - { - var i: usize = 0; - while (i < o.debug_unresolved_namespace_scopes.count()) : (i += 1) { - const namespace_index = o.debug_unresolved_namespace_scopes.keys()[i]; - const fwd_ref = o.debug_unresolved_namespace_scopes.values()[i]; - - const namespace = zcu.namespacePtr(namespace_index); - const debug_type = try o.lowerDebugType(pt, Type.fromInterned(namespace.owner_type)); - - o.builder.resolveDebugForwardReference(fwd_ref, debug_type); - } + if (o.debug_anyerror_fwd_ref.unwrap()) |fwd_ref| { + const debug_anyerror_type = try o.lowerDebugAnyerrorType(pt); + o.builder.resolveDebugForwardReference(fwd_ref, debug_anyerror_type); } + try o.flushPendingDebugTypes(pt); + o.builder.resolveDebugForwardReference( o.debug_enums_fwd_ref.unwrap().?, try o.builder.metadataTuple(o.debug_enums.items), @@ -1472,7 +1474,7 @@ pub const Object = struct { const line_number = zcu.navSrcLine(func.owner_nav) + 1; const is_internal_linkage = ip.indexToKey(nav.status.fully_resolved.val) != .@"extern"; - const debug_decl_type = try o.lowerDebugType(pt, fn_ty); + const debug_decl_type = try o.getDebugType(pt, fn_ty); const subprogram = try o.builder.debugSubprogram( file, @@ -1522,7 +1524,7 @@ pub const Object = struct { break :f .{ .counters_variable = counters_variable, - .pcs = .{}, + .pcs = .empty, }; }; @@ -1538,10 +1540,10 @@ pub const Object = struct { .args = args.items, .arg_index = 0, .arg_inline_index = 0, - .func_inst_table = .{}, - .blocks = .{}, - .loops = .{}, - .switch_dispatch_info = .{}, + .func_inst_table = .empty, + .blocks = .empty, + .loops = .empty, + .switch_dispatch_info = .empty, .sync_scope = if (owner_mod.single_threaded) .singlethread else .system, .file = file, .scope = subprogram, @@ -1599,6 +1601,7 @@ pub const Object = struct { } try fg.wip.finish(); + try o.flushPendingDebugTypes(pt); } pub fn updateNav(self: *Object, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void { @@ -1615,6 +1618,14 @@ pub const Object = struct { }, else => |e| return e, }; + try self.flushPendingDebugTypes(pt); + } + + fn flushPendingDebugTypes(o: *Object, pt: Zcu.PerThread) Allocator.Error!void { + o.debug_type_pool.flushPending(pt, .{ .llvm = o }) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => unreachable, // TODO: stop self-hosted backends from returning all of this crap! + }; } pub fn updateExports( @@ -1810,6 +1821,57 @@ pub const Object = struct { } } + pub fn updateContainerType(o: *Object, pt: Zcu.PerThread, ty: InternPool.Index, success: bool) Allocator.Error!void { + if (!o.builder.strip) { + o.debug_type_pool.updateContainerType(pt, .{ .llvm = o }, ty, success) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => unreachable, // TODO: stop self-hosted backends from returning all of this crap! + }; + } + } + + /// Should only be called by the `DebugConstPool` implementation. + /// + /// `val` is always a type because `o.debug_type_pool` only contains types. + pub fn addConst(o: *Object, pt: Zcu.PerThread, index: DebugConstPool.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(); + } + } + /// Should only be called by the `DebugConstPool` implementation. + /// + /// `val` is always a type because `o.debug_type_pool` only contains types. + pub fn updateConstIncomplete(o: *Object, pt: Zcu.PerThread, index: DebugConstPool.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); + } + /// Should only be called by the `DebugConstPool` implementation. + /// + /// `val` is always a type because `o.debug_type_pool` only contains types. + pub fn updateConst(o: *Object, pt: Zcu.PerThread, index: DebugConstPool.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 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 { const gpa = o.gpa; const gop = try o.debug_file_map.getOrPut(gpa, file_index); @@ -1826,10 +1888,22 @@ pub const Object = struct { return gop.value_ptr.*; } - pub fn lowerDebugType( + fn getDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error!Builder.Metadata { + assert(!o.builder.strip); + const index = o.debug_type_pool.get(pt, .{ .llvm = o }, ty.toIntern()) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + else => unreachable, // TODO: stop self-hosted backends from returning all of this crap! + }; + return o.debug_types.items[@intFromEnum(index)]; + } + + /// In codegen logic, instead of calling this directly, use `getDebugType` to get a forward + /// reference which will be populated only when all necessary type resolution is complete. + fn lowerDebugType( o: *Object, pt: Zcu.PerThread, ty: Type, + ty_fwd_ref: Builder.Metadata, ) Allocator.Error!Builder.Metadata { assert(!o.builder.strip); @@ -1838,267 +1912,96 @@ pub const Object = struct { const zcu = pt.zcu; const ip = &zcu.intern_pool; - if (o.debug_type_map.get(ty.toIntern())) |debug_type| return debug_type; + const name = try o.builder.metadataStringFmt("{f}", .{ty.fmt(pt)}); switch (ty.zigTypeTag(zcu)) { .void, .noreturn, - => { - const debug_void_type = try o.builder.debugSignedType( - try o.builder.metadataString("void"), - 0, - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_void_type); - return debug_void_type; - }, + .comptime_int, + .comptime_float, + .type, + .undefined, + .null, + .enum_literal, + => return o.builder.debugSignedType(name, 0), + .int => { const info = ty.intInfo(zcu); - assert(info.bits != 0); - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const builder_name = try o.builder.metadataString(name); - const debug_bits = ty.abiSize(zcu) * 8; // lldb cannot handle non-byte sized types - const debug_int_type = switch (info.signedness) { - .signed => try o.builder.debugSignedType(builder_name, debug_bits), - .unsigned => try o.builder.debugUnsignedType(builder_name, debug_bits), + const bits = ty.abiSize(zcu) * 8; // lldb cannot handle non-byte sized types + return switch (info.signedness) { + .signed => try o.builder.debugSignedType(name, bits), + .unsigned => try o.builder.debugUnsignedType(name, bits), }; - try o.debug_type_map.put(gpa, ty.toIntern(), debug_int_type); - return debug_int_type; - }, - .@"enum" => { - if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const debug_enum_type = try o.makeEmptyNamespaceDebugType(pt, ty); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_enum_type); - return debug_enum_type; - } - - const enum_type = ip.loadEnumType(ty.toIntern()); - const enumerators = try gpa.alloc(Builder.Metadata, enum_type.names.len); - defer gpa.free(enumerators); - - const int_ty = Type.fromInterned(enum_type.tag_ty); - const int_info = ty.intInfo(zcu); - assert(int_info.bits != 0); - - for (enum_type.names.get(ip), 0..) |field_name_ip, i| { - var bigint_space: Value.BigIntSpace = undefined; - const bigint = if (enum_type.values.len != 0) - Value.fromInterned(enum_type.values.get(ip)[i]).toBigInt(&bigint_space, zcu) - else - std.math.big.int.Mutable.init(&bigint_space.limbs, i).toConst(); - - enumerators[i] = try o.builder.debugEnumerator( - try o.builder.metadataString(field_name_ip.toSlice(ip)), - int_info.signedness == .unsigned, - int_info.bits, - bigint, - ); - } - - const file = try o.getDebugFile(pt, ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); - const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| - try o.namespaceToDebugScope(pt, parent_namespace) - else - file; - - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - - const debug_enum_type = try o.builder.debugEnumerationType( - try o.builder.metadataString(name), - file, - scope, - ty.typeDeclSrcLine(zcu).? + 1, // Line - try o.lowerDebugType(pt, int_ty), - ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.metadataTuple(enumerators), - ); - - try o.debug_type_map.put(gpa, ty.toIntern(), debug_enum_type); - try o.debug_enums.append(gpa, debug_enum_type); - return debug_enum_type; }, .float => { - const bits = ty.floatBits(target); - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const debug_float_type = try o.builder.debugFloatType( - try o.builder.metadataString(name), - bits, - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_float_type); - return debug_float_type; + return o.builder.debugFloatType(name, ty.floatBits(target)); }, .bool => { - const debug_bool_type = try o.builder.debugBoolType( - try o.builder.metadataString("bool"), + return o.builder.debugBoolType( + name, 8, // lldb cannot handle non-byte sized types ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_bool_type); - return debug_bool_type; }, .pointer => { - // Normalize everything that the debug info does not represent. - const ptr_info = ty.ptrInfo(zcu); - - if (ptr_info.sentinel != .none or - ptr_info.flags.address_space != .generic or - ptr_info.packed_offset.bit_offset != 0 or - ptr_info.packed_offset.host_size != 0 or - ptr_info.flags.vector_index != .none or - ptr_info.flags.is_allowzero or - ptr_info.flags.is_const or - ptr_info.flags.is_volatile or - ptr_info.flags.size == .many or ptr_info.flags.size == .c or - !Type.fromInterned(ptr_info.child).hasRuntimeBitsIgnoreComptime(zcu)) - { - const bland_ptr_ty = try pt.ptrType(.{ - .child = if (!Type.fromInterned(ptr_info.child).hasRuntimeBitsIgnoreComptime(zcu)) - .anyopaque_type - else - ptr_info.child, - .flags = .{ - .alignment = ptr_info.flags.alignment, - .size = switch (ptr_info.flags.size) { - .many, .c, .one => .one, - .slice => .slice, - }, - }, - }); - const debug_ptr_type = try o.lowerDebugType(pt, bland_ptr_ty); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_ptr_type); - return debug_ptr_type; - } - - const debug_fwd_ref = try o.builder.debugForwardReference(); - - // Set as forward reference while the type is lowered in case it references itself - try o.debug_type_map.put(gpa, ty.toIntern(), debug_fwd_ref); + const ptr_size = Type.ptrAbiSize(zcu.getTarget()); + const ptr_align = Type.ptrAbiAlignment(zcu.getTarget()); if (ty.isSlice(zcu)) { - const ptr_ty = ty.slicePtrFieldType(zcu); - const len_ty = Type.usize; - - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const line = 0; - - const ptr_size = ptr_ty.abiSize(zcu); - const ptr_align = ptr_ty.abiAlignment(zcu); - const len_size = len_ty.abiSize(zcu); - const len_align = len_ty.abiAlignment(zcu); - - const len_offset = len_align.forward(ptr_size); - const debug_ptr_type = try o.builder.debugMemberType( try o.builder.metadataString("ptr"), null, // File - debug_fwd_ref, + ty_fwd_ref, 0, // Line - try o.lowerDebugType(pt, ptr_ty), + try o.getDebugType(pt, ty.slicePtrFieldType(zcu)), ptr_size * 8, - (ptr_align.toByteUnits() orelse 0) * 8, + ptr_align.toByteUnits().? * 8, 0, // Offset ); const debug_len_type = try o.builder.debugMemberType( try o.builder.metadataString("len"), null, // File - debug_fwd_ref, + ty_fwd_ref, 0, // Line - try o.lowerDebugType(pt, len_ty), - len_size * 8, - (len_align.toByteUnits() orelse 0) * 8, - len_offset * 8, + try o.getDebugType(pt, .usize), + ptr_size * 8, + ptr_align.toByteUnits().? * 8, + ptr_size * 8, ); - const debug_slice_type = try o.builder.debugStructType( - try o.builder.metadataString(name), + return o.builder.debugStructType( + name, null, // File o.debug_compile_unit.unwrap().?, // Scope - line, + 0, // Line null, // Underlying type - ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + ptr_size * 2 * 8, + ptr_align.toByteUnits().? * 8, try o.builder.metadataTuple(&.{ debug_ptr_type, debug_len_type, }), ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_slice_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_slice_type; - - return debug_slice_type; } - const debug_elem_ty = try o.lowerDebugType(pt, Type.fromInterned(ptr_info.child)); - - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - - const debug_ptr_type = try o.builder.debugPointerType( - try o.builder.metadataString(name), + return o.builder.debugPointerType( + name, null, // File null, // Scope 0, // Line - debug_elem_ty, - target.ptrBitWidth(), - (ty.ptrAlignment(zcu).toByteUnits() orelse 0) * 8, + try o.getDebugType(pt, ty.childType(zcu)), + ptr_size * 8, + ptr_align.toByteUnits().? * 8, 0, // Offset ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_ptr_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_ptr_type; - - return debug_ptr_type; - }, - .@"opaque" => { - if (ty.toIntern() == .anyopaque_type) { - const debug_opaque_type = try o.builder.debugSignedType( - try o.builder.metadataString("anyopaque"), - 0, - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_opaque_type); - return debug_opaque_type; - } - - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - - const file = try o.getDebugFile(pt, ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); - const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| - try o.namespaceToDebugScope(pt, parent_namespace) - else - file; - - const debug_opaque_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - file, - scope, - ty.typeDeclSrcLine(zcu).? + 1, // Line - null, // Underlying type - 0, // Size - 0, // Align - null, // Fields - ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_opaque_type); - return debug_opaque_type; }, .array => { - const debug_array_type = try o.builder.debugArrayType( + return o.builder.debugArrayType( null, // Name null, // File null, // Scope 0, // Line - try o.lowerDebugType(pt, ty.childType(zcu)), + try o.getDebugType(pt, ty.childType(zcu)), ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, try o.builder.metadataTuple(&.{ @@ -2108,8 +2011,6 @@ pub const Object = struct { ), }), ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_array_type); - return debug_array_type; }, .vector => { const elem_ty = ty.childType(zcu); @@ -2120,23 +2021,17 @@ pub const Object = struct { const debug_elem_type = switch (elem_ty.zigTypeTag(zcu)) { .int => blk: { const info = elem_ty.intInfo(zcu); - assert(info.bits != 0); - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const builder_name = try o.builder.metadataString(name); break :blk switch (info.signedness) { - .signed => try o.builder.debugSignedType(builder_name, info.bits), - .unsigned => try o.builder.debugUnsignedType(builder_name, info.bits), + .signed => try o.builder.debugSignedType(name, info.bits), + .unsigned => try o.builder.debugUnsignedType(name, info.bits), }; }, - .bool => try o.builder.debugBoolType( - try o.builder.metadataString("bool"), - 1, - ), - else => try o.lowerDebugType(pt, ty.childType(zcu)), + .bool => try o.builder.debugBoolType(try o.builder.metadataString("bool"), 1), + .pointer, .optional, .float => try o.getDebugType(pt, elem_ty), + else => unreachable, }; - const debug_vector_type = try o.builder.debugVectorType( + return o.builder.debugVectorType( null, // Name null, // File null, // Scope @@ -2151,71 +2046,64 @@ pub const Object = struct { ), }), ); - - try o.debug_type_map.put(gpa, ty.toIntern(), debug_vector_type); - return debug_vector_type; }, .optional => { - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const child_ty = ty.optionalChild(zcu); - if (!child_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const debug_bool_type = try o.builder.debugBoolType( - try o.builder.metadataString(name), - 8, + const payload_ty = ty.optionalChild(zcu); + if (ty.optionalReprIsPayload(zcu)) { + // MLUGG TODO: these should use DW_TAG_typedef instead, but std.zig.llvm.Builder currently lacks support for those. + const payload_member = try o.builder.debugMemberType( + try o.builder.metadataString("payload"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, .anyerror), + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + 0, // offset + ); + return o.builder.debugStructType( + name, + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + null, // underlying type + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(&.{payload_member}), ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_bool_type); - return debug_bool_type; } - const debug_fwd_ref = try o.builder.debugForwardReference(); - - // Set as forward reference while the type is lowered in case it references itself - try o.debug_type_map.put(gpa, ty.toIntern(), debug_fwd_ref); - - if (ty.optionalReprIsPayload(zcu)) { - const debug_optional_type = try o.lowerDebugType(pt, child_ty); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_optional_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_optional_type; - - return debug_optional_type; - } + const payload_size = payload_ty.abiSize(zcu); const non_null_ty = Type.u8; - const payload_size = child_ty.abiSize(zcu); - const payload_align = child_ty.abiAlignment(zcu); const non_null_size = non_null_ty.abiSize(zcu); const non_null_align = non_null_ty.abiAlignment(zcu); const non_null_offset = non_null_align.forward(payload_size); - const debug_data_type = try o.builder.debugMemberType( - try o.builder.metadataString("data"), - null, // File - debug_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, child_ty), + const debug_payload_type = try o.builder.debugMemberType( + try o.builder.metadataString("payload"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, payload_ty), payload_size * 8, - (payload_align.toByteUnits() orelse 0) * 8, - 0, // Offset + payload_ty.abiAlignment(zcu).toByteUnits().? * 8, + 0, // offset ); const debug_some_type = try o.builder.debugMemberType( try o.builder.metadataString("some"), null, - debug_fwd_ref, + ty_fwd_ref, 0, - try o.lowerDebugType(pt, non_null_ty), + try o.getDebugType(pt, non_null_ty), non_null_size * 8, - (non_null_align.toByteUnits() orelse 0) * 8, + non_null_align.toByteUnits().? * 8, non_null_offset * 8, ); - const debug_optional_type = try o.builder.debugStructType( - try o.builder.metadataString(name), + return o.builder.debugStructType( + name, null, // File o.debug_compile_unit.unwrap().?, // Scope 0, // Line @@ -2223,494 +2111,498 @@ pub const Object = struct { ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, try o.builder.metadataTuple(&.{ - debug_data_type, + debug_payload_type, debug_some_type, }), ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_optional_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_optional_type; - - return debug_optional_type; }, .error_union => { + const error_ty = ty.errorUnionSet(zcu); const payload_ty = ty.errorUnionPayload(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - // TODO: Maybe remove? - const debug_error_union_type = try o.lowerDebugType(pt, Type.anyerror); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_error_union_type); - return debug_error_union_type; - } - - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const error_size = Type.anyerror.abiSize(zcu); - const error_align = Type.anyerror.abiAlignment(zcu); + const error_size = error_ty.abiSize(zcu); + const error_align = error_ty.abiAlignment(zcu); const payload_size = payload_ty.abiSize(zcu); const payload_align = payload_ty.abiAlignment(zcu); - var error_index: u32 = undefined; - var payload_index: u32 = undefined; - var error_offset: u64 = undefined; - var payload_offset: u64 = undefined; - if (error_align.compare(.gt, payload_align)) { - error_index = 0; - payload_index = 1; - error_offset = 0; - payload_offset = payload_align.forward(error_size); - } else { - payload_index = 0; - error_index = 1; - payload_offset = 0; - error_offset = error_align.forward(payload_size); - } - - const debug_fwd_ref = try o.builder.debugForwardReference(); + const error_index: u1, const payload_index: u1, const error_offset: u64, const payload_offset: u64 = fields: { + if (error_align.compare(.gt, payload_align)) { + break :fields .{ 0, 1, 0, payload_align.forward(error_size) }; + } else { + break :fields .{ 1, 0, error_align.forward(payload_size), 0 }; + } + }; var fields: [2]Builder.Metadata = undefined; fields[error_index] = try o.builder.debugMemberType( - try o.builder.metadataString("tag"), + try o.builder.metadataString("error"), null, // File - debug_fwd_ref, + ty_fwd_ref, 0, // Line - try o.lowerDebugType(pt, Type.anyerror), + try o.getDebugType(pt, error_ty), error_size * 8, - (error_align.toByteUnits() orelse 0) * 8, + error_align.toByteUnits().? * 8, error_offset * 8, ); fields[payload_index] = try o.builder.debugMemberType( - try o.builder.metadataString("value"), + try o.builder.metadataString("payload"), null, // File - debug_fwd_ref, + ty_fwd_ref, 0, // Line - try o.lowerDebugType(pt, payload_ty), + try o.getDebugType(pt, payload_ty), payload_size * 8, - (payload_align.toByteUnits() orelse 0) * 8, + payload_align.toByteUnits().? * 8, payload_offset * 8, ); - const debug_error_union_type = try o.builder.debugStructType( - try o.builder.metadataString(name), + return try o.builder.debugStructType( + name, null, // File - o.debug_compile_unit.unwrap().?, // Sope + o.debug_compile_unit.unwrap().?, // Scope 0, // Line null, // Underlying type ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, try o.builder.metadataTuple(&fields), ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_error_union_type); - - try o.debug_type_map.put(gpa, ty.toIntern(), debug_error_union_type); - return debug_error_union_type; }, .error_set => { - const debug_error_set = try o.builder.debugUnsignedType( - try o.builder.metadataString("anyerror"), - 16, + assert(ty.toIntern() != .anyerror_type); // handled specially in `updateConst`; will be populated by `emit` instead + // Error sets are just named wrappers around `anyerror`. + // MLUGG TODO: these should use DW_TAG_typedef instead, but std.zig.llvm.Builder currently lacks support for those. + const anyerror_member = try o.builder.debugMemberType( + try o.builder.metadataString("error"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, .anyerror), + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + 0, // offset + ); + return o.builder.debugStructType( + name, + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + null, // underlying type + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(&.{anyerror_member}), ); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_error_set); - return debug_error_set; }, - .@"struct" => { - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - - if (zcu.typeToPackedStruct(ty)) |struct_type| { - const backing_int_ty = struct_type.backingIntTypeUnordered(ip); - if (backing_int_ty != .none) { - const info = Type.fromInterned(backing_int_ty).intInfo(zcu); - const builder_name = try o.builder.metadataString(name); - const debug_int_type = switch (info.signedness) { - .signed => try o.builder.debugSignedType(builder_name, ty.abiSize(zcu) * 8), - .unsigned => try o.builder.debugUnsignedType(builder_name, ty.abiSize(zcu) * 8), - }; - try o.debug_type_map.put(gpa, ty.toIntern(), debug_int_type); - return debug_int_type; - } + .@"fn" => { + if (!ty.fnHasRuntimeBits(zcu)) { + return o.builder.debugSignedType(name, 0); } - switch (ip.indexToKey(ty.toIntern())) { - .tuple_type => |tuple| { - var fields: std.ArrayList(Builder.Metadata) = .empty; - defer fields.deinit(gpa); + const fn_info = zcu.typeToFunc(ty).?; - try fields.ensureUnusedCapacity(gpa, tuple.types.len); + var debug_param_types: std.ArrayList(Builder.Metadata) = try .initCapacity(gpa, 3 + fn_info.param_types.len); + defer debug_param_types.deinit(gpa); - comptime assert(struct_layout_version == 2); - var offset: u64 = 0; + // Return type goes first. + const sret = firstParamSRet(fn_info, zcu, target); + const ret_ty: Type = if (sret) .void else .fromInterned(fn_info.return_type); + debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, ret_ty)); - const debug_fwd_ref = try o.builder.debugForwardReference(); + if (sret) { + const ptr_ty = try pt.singleMutPtrType(Type.fromInterned(fn_info.return_type)); + debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, ptr_ty)); + } - for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| { - if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; + if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) { + // Stack trace pointer. + debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, .ptr_usize)); + } - const field_size = Type.fromInterned(field_ty).abiSize(zcu); - const field_align = Type.fromInterned(field_ty).abiAlignment(zcu); - const field_offset = field_align.forward(offset); - offset = field_offset + field_size; + for (fn_info.param_types.get(ip)) |param_ty_ip| { + const param_ty: Type = .fromInterned(param_ty_ip); + if (!param_ty.hasRuntimeBits(zcu)) continue; + if (isByRef(param_ty, zcu)) { + const ptr_ty = try pt.singleConstPtrType(param_ty); + debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, ptr_ty)); + } else { + debug_param_types.appendAssumeCapacity(try o.getDebugType(pt, param_ty)); + } + } - var name_buf: [32]u8 = undefined; - const field_name = std.fmt.bufPrint(&name_buf, "{d}", .{i}) catch unreachable; + return o.builder.debugSubroutineType( + try o.builder.metadataTuple(debug_param_types.items), + ); + }, + .@"struct" => { + if (ty.isTuple(zcu)) { + const tuple = ip.indexToKey(ty.toIntern()).tuple_type; + var fields: std.ArrayList(Builder.Metadata) = .empty; + defer fields.deinit(gpa); - fields.appendAssumeCapacity(try o.builder.debugMemberType( - try o.builder.metadataString(field_name), - null, // File - debug_fwd_ref, - 0, - try o.lowerDebugType(pt, Type.fromInterned(field_ty)), - field_size * 8, - (field_align.toByteUnits() orelse 0) * 8, - field_offset * 8, - )); - } + try fields.ensureUnusedCapacity(gpa, tuple.types.len); - const debug_struct_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type - ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.metadataTuple(fields.items), - ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_struct_type); + comptime assert(struct_layout_version == 2); + var offset: u64 = 0; - try o.debug_type_map.put(gpa, ty.toIntern(), debug_struct_type); - return debug_struct_type; - }, - .struct_type => { - if (!ip.loadStructType(ty.toIntern()).haveFieldTypes(ip)) { - // This can happen if a struct type makes it all the way to - // flush() without ever being instantiated or referenced (even - // via pointer). The only reason we are hearing about it now is - // that it is being used as a namespace to put other debug types - // into. Therefore we can satisfy this by making an empty namespace, - // rather than changing the frontend to unnecessarily resolve the - // struct field types. - const debug_struct_type = try o.makeEmptyNamespaceDebugType(pt, ty); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_struct_type); - return debug_struct_type; - } - }, - else => {}, - } + for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty_ip, field_val, i| { + const field_ty: Type = .fromInterned(field_ty_ip); + if (field_val != .none or !field_ty.hasRuntimeBits(zcu)) continue; + + const field_size = field_ty.abiSize(zcu); + const field_align = field_ty.abiAlignment(zcu); + const field_offset = field_align.forward(offset); + offset = field_offset + field_size; + + fields.appendAssumeCapacity(try o.builder.debugMemberType( + try o.builder.metadataStringFmt("{d}", .{i}), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, field_ty), + field_size * 8, + field_align.toByteUnits().? * 8, + field_offset * 8, + )); + } - if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const debug_struct_type = try o.makeEmptyNamespaceDebugType(pt, ty); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_struct_type); - return debug_struct_type; + return o.builder.debugStructType( + name, + null, // file + o.debug_compile_unit.unwrap().?, + 0, // line + null, // underlying type + ty.abiSize(zcu) * 8, + (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + try o.builder.metadataTuple(fields.items), + ); } const struct_type = zcu.typeToStruct(ty).?; - var fields: std.ArrayList(Builder.Metadata) = .empty; - defer fields.deinit(gpa); - - try fields.ensureUnusedCapacity(gpa, struct_type.field_types.len); + const file = try o.getDebugFile(pt, struct_type.zir_index.resolveFile(ip)); + const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| + try o.namespaceToDebugScope(pt, parent_namespace) + else + file; - const debug_fwd_ref = try o.builder.debugForwardReference(); + const line = ty.typeDeclSrcLine(zcu).? + 1; - // Set as forward reference while the type is lowered in case it references itself - try o.debug_type_map.put(gpa, ty.toIntern(), debug_fwd_ref); + var fields: std.ArrayList(Builder.Metadata) = .empty; + defer fields.deinit(gpa); - comptime assert(struct_layout_version == 2); - var it = struct_type.iterateRuntimeOrder(ip); - while (it.next()) |field_index| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - const field_size = field_ty.abiSize(zcu); - const field_align = ty.fieldAlignment(field_index, zcu); - const field_offset = ty.structFieldOffset(field_index, zcu); - const field_name = struct_type.fieldName(ip, field_index); - fields.appendAssumeCapacity(try o.builder.debugMemberType( - try o.builder.metadataString(field_name.toSlice(ip)), - null, // File - debug_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, field_ty), - field_size * 8, - (field_align.toByteUnits() orelse 0) * 8, - field_offset * 8, - )); + switch (struct_type.layout) { + .@"packed" => { + try fields.ensureTotalCapacityPrecise(gpa, 1); + fields.appendAssumeCapacity(try o.builder.debugMemberType( + try o.builder.metadataString("bits"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, .fromInterned(struct_type.packed_backing_int_type)), + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + 0, // offset + )); + }, + .auto, .@"extern" => { + comptime assert(struct_layout_version == 2); + try fields.ensureTotalCapacityPrecise(gpa, struct_type.field_types.len); + var it = struct_type.iterateRuntimeOrder(ip); + while (it.next()) |field_index| { + const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBits(zcu)) continue; + const field_size = field_ty.abiSize(zcu); + const field_align = switch (ty.explicitFieldAlignment(field_index, zcu)) { + .none => field_ty.abiAlignment(zcu), + else => |a| a, + }; + const field_offset = struct_type.field_offsets.get(ip)[field_index]; + const field_name = struct_type.field_names.get(ip)[field_index]; + fields.appendAssumeCapacity(try o.builder.debugMemberType( + try o.builder.metadataString(field_name.toSlice(ip)), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, field_ty), + field_size * 8, + field_align.toByteUnits().? * 8, + field_offset * 8, + )); + } + }, } - const debug_struct_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, try o.builder.metadataTuple(fields.items), ); - - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_struct_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_struct_type; - - return debug_struct_type; }, .@"union" => { - const name = try o.allocTypeName(pt, ty); - defer gpa.free(name); - const union_type = ip.loadUnionType(ty.toIntern()); - if (!union_type.haveFieldTypes(ip) or - !ty.hasRuntimeBitsIgnoreComptime(zcu) or - !union_type.haveLayout(ip)) - { - const debug_union_type = try o.makeEmptyNamespaceDebugType(pt, ty); - try o.debug_type_map.put(gpa, ty.toIntern(), debug_union_type); - return debug_union_type; - } - const layout = Type.getUnionLayout(union_type, zcu); + const file = try o.getDebugFile(pt, union_type.zir_index.resolveFile(ip)); + const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| + try o.namespaceToDebugScope(pt, parent_namespace) + else + file; - const debug_fwd_ref = try o.builder.debugForwardReference(); + const line = ty.typeDeclSrcLine(zcu).? + 1; - // Set as forward reference while the type is lowered in case it references itself - try o.debug_type_map.put(gpa, ty.toIntern(), debug_fwd_ref); + const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type); + const layout = Type.getUnionLayout(union_type, zcu); if (layout.payload_size == 0) { - const debug_union_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type + const tag_member = try o.builder.debugMemberType( + try o.builder.metadataString("tag"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, enum_tag_ty), + layout.tag_size * 8, + layout.tag_align.toByteUnits().? * 8, + 0, // offset + ); + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.metadataTuple( - &.{try o.lowerDebugType(pt, Type.fromInterned(union_type.enum_tag_ty))}, - ), + ty.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(&.{tag_member}), ); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_union_type; - - return debug_union_type; } - var fields: std.ArrayList(Builder.Metadata) = .empty; + var fields: std.ArrayList(Builder.Metadata) = try .initCapacity(gpa, union_type.field_types.len); defer fields.deinit(gpa); - try fields.ensureUnusedCapacity(gpa, union_type.loadTagType(ip).names.len); - - const debug_union_fwd_ref = if (layout.tag_size == 0) - debug_fwd_ref + const payload_fwd_ref = if (layout.tag_size == 0) + ty_fwd_ref else try o.builder.debugForwardReference(); - const tag_type = union_type.loadTagType(ip); - - for (0..tag_type.names.len) |field_index| { + for (0..union_type.field_types.len) |field_index| { const field_ty = union_type.field_types.get(ip)[field_index]; - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(zcu)) continue; const field_size = Type.fromInterned(field_ty).abiSize(zcu); - const field_align: InternPool.Alignment = switch (union_type.flagsUnordered(ip).layout) { + const field_align: InternPool.Alignment = switch (union_type.layout) { .@"packed" => .none, - .auto, .@"extern" => ty.fieldAlignment(field_index, zcu), + .auto, .@"extern" => ty.explicitFieldAlignment(field_index, zcu), }; - const field_name = tag_type.names.get(ip)[field_index]; + const field_name = enum_tag_ty.enumFieldName(field_index, zcu); fields.appendAssumeCapacity(try o.builder.debugMemberType( try o.builder.metadataString(field_name.toSlice(ip)), - null, // File - debug_union_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, Type.fromInterned(field_ty)), + null, // file + payload_fwd_ref, + 0, // line + try o.getDebugType(pt, .fromInterned(field_ty)), field_size * 8, (field_align.toByteUnits() orelse 0) * 8, - 0, // Offset + 0, // offset )); } - var union_name_buf: ?[:0]const u8 = null; - defer if (union_name_buf) |buf| gpa.free(buf); - const union_name = if (layout.tag_size == 0) name else name: { - union_name_buf = try std.fmt.allocPrintSentinel(gpa, "{s}:Payload", .{name}, 0); - break :name union_name_buf.?; - }; - - const debug_union_type = try o.builder.debugUnionType( - try o.builder.metadataString(union_name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type + const debug_payload_type = try o.builder.debugUnionType( + payload_name: { + if (layout.tag_size == 0) break :payload_name name; + break :payload_name try o.builder.metadataStringFmt("{s}:Payload", .{name.slice(&o.builder)}); + }, + file, + scope, + line, + null, // underlying type layout.payload_size * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, try o.builder.metadataTuple(fields.items), ); - o.builder.resolveDebugForwardReference(debug_union_fwd_ref, debug_union_type); - if (layout.tag_size == 0) { - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_union_type; - - return debug_union_type; + return debug_payload_type; } - var tag_offset: u64 = undefined; - var payload_offset: u64 = undefined; - if (layout.tag_align.compare(.gte, layout.payload_align)) { - tag_offset = 0; - payload_offset = layout.payload_align.forward(layout.tag_size); - } else { - payload_offset = 0; - tag_offset = layout.tag_align.forward(layout.payload_size); - } + o.builder.resolveDebugForwardReference(payload_fwd_ref, debug_payload_type); - const debug_tag_type = try o.builder.debugMemberType( + const tag_offset: u64, const payload_offset: u64 = offsets: { + if (layout.tag_align.compare(.gte, layout.payload_align)) { + break :offsets .{ 0, layout.payload_align.forward(layout.tag_size) }; + } else { + break :offsets .{ layout.tag_align.forward(layout.payload_size), 0 }; + } + }; + + const tag_member_type = try o.builder.debugMemberType( try o.builder.metadataString("tag"), - null, // File - debug_fwd_ref, - 0, // Line - try o.lowerDebugType(pt, Type.fromInterned(union_type.enum_tag_ty)), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, enum_tag_ty), layout.tag_size * 8, - (layout.tag_align.toByteUnits() orelse 0) * 8, + layout.tag_align.toByteUnits().? * 8, tag_offset * 8, ); - const debug_payload_type = try o.builder.debugMemberType( + const payload_member_type = try o.builder.debugMemberType( try o.builder.metadataString("payload"), - null, // File - debug_fwd_ref, - 0, // Line - debug_union_type, + null, // file + ty_fwd_ref, + 0, // line + debug_payload_type, layout.payload_size * 8, - (layout.payload_align.toByteUnits() orelse 0) * 8, + layout.payload_align.toByteUnits().? * 8, payload_offset * 8, ); const full_fields: [2]Builder.Metadata = if (layout.tag_align.compare(.gte, layout.payload_align)) - .{ debug_tag_type, debug_payload_type } + .{ tag_member_type, payload_member_type } else - .{ debug_payload_type, debug_tag_type }; + .{ payload_member_type, tag_member_type }; - const debug_tagged_union_type = try o.builder.debugStructType( - try o.builder.metadataString(name), - null, // File - o.debug_compile_unit.unwrap().?, // Scope - 0, // Line - null, // Underlying type + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type ty.abiSize(zcu) * 8, - (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, try o.builder.metadataTuple(&full_fields), ); + }, + .@"enum" => { + const file = try o.getDebugFile(pt, ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); + const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| + try o.namespaceToDebugScope(pt, parent_namespace) + else + file; - o.builder.resolveDebugForwardReference(debug_fwd_ref, debug_tagged_union_type); - - // Set to real type now that it has been lowered fully - const map_ptr = o.debug_type_map.getPtr(ty.toIntern()) orelse unreachable; - map_ptr.* = debug_tagged_union_type; + const line = ty.typeDeclSrcLine(zcu).? + 1; - return debug_tagged_union_type; - }, - .@"fn" => { - const fn_info = zcu.typeToFunc(ty).?; + if (!ty.hasRuntimeBits(zcu)) { + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + null, // fields + ); + } - var debug_param_types = std.array_list.Managed(Builder.Metadata).init(gpa); - defer debug_param_types.deinit(); + const enum_type = ip.loadEnumType(ty.toIntern()); + const enumerators = try gpa.alloc(Builder.Metadata, enum_type.field_names.len); + defer gpa.free(enumerators); - try debug_param_types.ensureUnusedCapacity(3 + fn_info.param_types.len); + const int_ty: Type = .fromInterned(enum_type.int_tag_type); + const int_info = ty.intInfo(zcu); + assert(int_info.bits != 0); - // Return type goes first. - if (Type.fromInterned(fn_info.return_type).hasRuntimeBitsIgnoreComptime(zcu)) { - const sret = firstParamSRet(fn_info, zcu, target); - const ret_ty = if (sret) Type.void else Type.fromInterned(fn_info.return_type); - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, ret_ty)); - - if (sret) { - const ptr_ty = try pt.singleMutPtrType(Type.fromInterned(fn_info.return_type)); - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, ptr_ty)); - } - } else { - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, Type.void)); + for (enumerators, enum_type.field_names.get(ip), 0..) |*out, field_name, field_index| { + var space: Value.BigIntSpace = undefined; + const field_val: std.math.big.int.Const = switch (enum_type.field_values.len) { + 0 => std.math.big.int.Mutable.init(&space.limbs, field_index).toConst(), + else => Value.fromInterned(enum_type.field_values.get(ip)[field_index]).toBigInt(&space, zcu), + }; + out.* = try o.builder.debugEnumerator( + try o.builder.metadataString(field_name.toSlice(ip)), + int_info.signedness == .unsigned, + int_info.bits, + field_val, + ); } - if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) { - // Stack trace pointer. - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, .fromInterned(.ptr_usize_type))); + const debug_enum_type = try o.builder.debugEnumerationType( + name, + file, + scope, + line, + try o.getDebugType(pt, int_ty), + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(enumerators), + ); + try o.debug_enums.append(gpa, debug_enum_type); + return debug_enum_type; + }, + .@"opaque" => { + if (ty.toIntern() == .anyopaque_type) { + return o.builder.debugSignedType(name, 0); } - for (0..fn_info.param_types.len) |i| { - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[i]); - if (!param_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + const file = try o.getDebugFile(pt, ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); + const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| + try o.namespaceToDebugScope(pt, parent_namespace) + else + file; - if (isByRef(param_ty, zcu)) { - const ptr_ty = try pt.singleMutPtrType(param_ty); - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, ptr_ty)); - } else { - debug_param_types.appendAssumeCapacity(try o.lowerDebugType(pt, param_ty)); - } - } + const line = ty.typeDeclSrcLine(zcu).? + 1; - const debug_function_type = try o.builder.debugSubroutineType( - try o.builder.metadataTuple(debug_param_types.items), + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type + 0, // size + ty.abiAlignment(zcu).toByteUnits().? * 8, + null, // fields ); - - try o.debug_type_map.put(gpa, ty.toIntern(), debug_function_type); - return debug_function_type; }, - .comptime_int => unreachable, - .comptime_float => unreachable, - .type => unreachable, - .undefined => unreachable, - .null => unreachable, - .enum_literal => unreachable, - .frame => @panic("TODO implement lowerDebugType for Frame types"), .@"anyframe" => @panic("TODO implement lowerDebugType for AnyFrame types"), } } - fn namespaceToDebugScope(o: *Object, pt: Zcu.PerThread, namespace_index: InternPool.NamespaceIndex) !Builder.Metadata { + /// Called in `emit` so that the global error set is fully populated. + fn lowerDebugAnyerrorType(o: *Object, pt: Zcu.PerThread) Allocator.Error!Builder.Metadata { const zcu = pt.zcu; - const namespace = zcu.namespacePtr(namespace_index); - if (namespace.parent == .none) return try o.getDebugFile(pt, namespace.file_scope); - - const gop = try o.debug_unresolved_namespace_scopes.getOrPut(o.gpa, namespace_index); - - if (!gop.found_existing) gop.value_ptr.* = try o.builder.debugForwardReference(); + const ip = &zcu.intern_pool; + const gpa = zcu.comp.gpa; + + const error_set_bits = zcu.errorSetBits(); + const error_names = ip.global_error_set.getNamesFromMainThread(); + + const enumerators = try gpa.alloc(Builder.Metadata, error_names.len); + defer gpa.free(enumerators); + + for (enumerators, error_names, 1..) |*out, error_name, error_value| { + var space: Value.BigIntSpace = undefined; + var bigint: std.math.big.int.Mutable = .init(&space.limbs, error_value); + out.* = try o.builder.debugEnumerator( + try o.builder.metadataString(error_name.toSlice(ip)), + true, // unsigned + error_set_bits, + bigint.toConst(), + ); + } - return gop.value_ptr.*; + const debug_enum_type = try o.builder.debugEnumerationType( + try o.builder.metadataString("anyerror"), + null, // file + o.debug_compile_unit.unwrap().?, // scope + 0, // line + try o.getDebugType(pt, try pt.intType(.unsigned, error_set_bits)), + Type.anyerror.abiSize(zcu) * 8, + Type.anyerror.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(enumerators), + ); + try o.debug_enums.append(gpa, debug_enum_type); + return debug_enum_type; } - fn makeEmptyNamespaceDebugType(o: *Object, pt: Zcu.PerThread, ty: Type) !Builder.Metadata { + fn namespaceToDebugScope(o: *Object, pt: Zcu.PerThread, namespace_index: InternPool.NamespaceIndex) !Builder.Metadata { const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const file = try o.getDebugFile(pt, ty.typeDeclInstAllowGeneratedTag(zcu).?.resolveFile(ip)); - const scope = if (ty.getParentNamespace(zcu).unwrap()) |parent_namespace| - try o.namespaceToDebugScope(pt, parent_namespace) - else - file; - return o.builder.debugStructType( - try o.builder.metadataString(ty.containerTypeName(ip).toSlice(ip)), // TODO use fully qualified name - file, - scope, - ty.typeDeclSrcLine(zcu).? + 1, - null, - 0, - 0, - null, - ); + const namespace = zcu.namespacePtr(namespace_index); + if (namespace.parent == .none) return try o.getDebugFile(pt, namespace.file_scope); + return o.getDebugType(pt, .fromInterned(namespace.owner_type)); } fn allocTypeName(o: *Object, pt: Zcu.PerThread, ty: Type) Allocator.Error![:0]const u8 { @@ -2885,40 +2777,6 @@ pub const Object = struct { if (fn_info.return_type == .noreturn_type) try attributes.addFnAttr(.noreturn, &o.builder); - // Add parameter attributes. We handle only the case of extern functions (no body) - // because functions with bodies are handled in `updateFunc`. - if (is_extern) { - var it = iterateParamTypes(o, pt, fn_info); - it.llvm_index = llvm_arg_i; - while (try it.next()) |lowering| switch (lowering) { - .byval => { - const param_index = it.zig_index - 1; - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); - if (!isByRef(param_ty, zcu)) { - try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); - } - }, - .byref => { - const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); - const param_llvm_ty = try o.lowerType(pt, param_ty); - const alignment = param_ty.abiAlignment(zcu); - try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment.toLlvm(), it.byval_attr, param_llvm_ty); - }, - .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), - // No attributes needed for these. - .no_bits, - .abi_sized_int, - .multiple_llvm_types, - .float_array, - .i32_array, - .i64_array, - => continue, - - .slice => unreachable, // extern functions do not support slice types. - - }; - } - function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); return function_index; } @@ -3223,7 +3081,7 @@ pub const Object = struct { ), .opt_type => |child_ty| { // Must stay in sync with `opt_payload` logic in `lowerPtr`. - if (!Type.fromInterned(child_ty).hasRuntimeBitsIgnoreComptime(zcu)) return .i8; + if (!Type.fromInterned(child_ty).hasRuntimeBits(zcu)) return .i8; const payload_ty = try o.lowerType(pt, Type.fromInterned(child_ty)); if (t.optionalReprIsPayload(zcu)) return payload_ty; @@ -3245,7 +3103,7 @@ pub const Object = struct { // Must stay in sync with `codegen.errUnionPayloadOffset`. // See logic in `lowerPtr`. const error_type = try o.errorIntType(pt); - if (!Type.fromInterned(error_union_type.payload_type).hasRuntimeBitsIgnoreComptime(zcu)) + if (!Type.fromInterned(error_union_type.payload_type).hasRuntimeBits(zcu)) return error_type; const payload_type = try o.lowerType(pt, Type.fromInterned(error_union_type.payload_type)); @@ -3287,7 +3145,7 @@ pub const Object = struct { const struct_type = ip.loadStructType(t.toIntern()); if (struct_type.layout == .@"packed") { - const int_ty = try o.lowerType(pt, Type.fromInterned(struct_type.backingIntTypeUnordered(ip))); + const int_ty = try o.lowerType(pt, .fromInterned(struct_type.packed_backing_int_type)); try o.type_map.put(o.gpa, t.toIntern(), int_ty); return int_ty; } @@ -3301,18 +3159,16 @@ pub const Object = struct { comptime assert(struct_layout_version == 2); var offset: u64 = 0; - var big_align: InternPool.Alignment = .@"1"; var struct_kind: Builder.Type.Structure.Kind = .normal; // When we encounter a zero-bit field, we place it here so we know to map it to the next non-zero-bit field (if any). var it = struct_type.iterateRuntimeOrder(ip); while (it.next()) |field_index| { const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - const field_align = t.fieldAlignment(field_index, zcu); - const field_ty_align = field_ty.abiAlignment(zcu); - if (field_align.compare(.lt, field_ty_align)) struct_kind = .@"packed"; - big_align = big_align.max(field_align); const prev_offset = offset; - offset = field_align.forward(offset); + offset = struct_type.field_offsets.get(ip)[field_index]; + if (@ctz(offset) < field_ty.abiAlignment(zcu).toLog2Units()) { + struct_kind = .@"packed"; + } const padding_len = offset - prev_offset; if (padding_len > 0) try llvm_field_types.append( @@ -3320,11 +3176,11 @@ pub const Object = struct { try o.builder.arrayType(padding_len, .i8), ); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!field_ty.hasRuntimeBits(zcu)) { // This is a zero-bit field. If there are runtime bits after this field, // map to the next LLVM field (which we know exists): otherwise, don't // map the field, indicating it's at the end of the struct. - if (offset != struct_type.sizeUnordered(ip)) { + if (offset != struct_type.size) { try o.struct_field_map.put(o.gpa, .{ .struct_ty = t.toIntern(), .field_index = field_index, @@ -3343,7 +3199,7 @@ pub const Object = struct { } { const prev_offset = offset; - offset = big_align.forward(offset); + offset = struct_type.alignment.forward(offset); const padding_len = offset - prev_offset; if (padding_len > 0) try llvm_field_types.append( o.gpa, @@ -3391,7 +3247,7 @@ pub const Object = struct { o.gpa, try o.builder.arrayType(padding_len, .i8), ); - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(zcu)) { + if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) { // This is a zero-bit field. If there are runtime bits after this field, // map to the next LLVM field (which we know exists): otherwise, don't // map the field, indicating it's at the end of the struct. @@ -3428,14 +3284,14 @@ pub const Object = struct { const union_obj = ip.loadUnionType(t.toIntern()); const layout = Type.getUnionLayout(union_obj, zcu); - if (union_obj.flagsUnordered(ip).layout == .@"packed") { - const int_ty = try o.builder.intType(@intCast(t.bitSize(zcu))); + if (union_obj.layout == .@"packed") { + const int_ty = try o.lowerType(pt, .fromInterned(union_obj.packed_backing_int_type)); try o.type_map.put(o.gpa, t.toIntern(), int_ty); return int_ty; } if (layout.payload_size == 0) { - const enum_tag_ty = try o.lowerType(pt, Type.fromInterned(union_obj.enum_tag_ty)); + const enum_tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); try o.type_map.put(o.gpa, t.toIntern(), enum_tag_ty); return enum_tag_ty; } @@ -3467,7 +3323,7 @@ pub const Object = struct { ); return ty; } - const enum_tag_ty = try o.lowerType(pt, Type.fromInterned(union_obj.enum_tag_ty)); + const enum_tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); // Put the tag before or after the payload depending on which one's // alignment is greater. @@ -3502,7 +3358,7 @@ pub const Object = struct { } return gop.value_ptr.*; }, - .enum_type => try o.lowerType(pt, Type.fromInterned(ip.loadEnumType(t.toIntern()).tag_ty)), + .enum_type => try o.lowerType(pt, t.intTagType(zcu)), .func_type => |func_type| try o.lowerTypeFn(pt, func_type), .error_set_type, .inferred_error_set_type => try o.errorIntType(pt), // values, not types @@ -3522,6 +3378,7 @@ pub const Object = struct { .opt, .aggregate, .un, + .bitpack, // memoization, not types .memoized_call, => unreachable, @@ -3529,20 +3386,6 @@ pub const Object = struct { }; } - /// Use this instead of lowerType when you want to handle correctly the case of elem_ty - /// being a zero bit type, but it should still be lowered as an i8 in such case. - /// There are other similar cases handled here as well. - fn lowerPtrElemTy(o: *Object, pt: Zcu.PerThread, elem_ty: Type) Allocator.Error!Builder.Type { - const zcu = pt.zcu; - const lower_elem_ty = switch (elem_ty.zigTypeTag(zcu)) { - .@"opaque" => true, - .@"fn" => !zcu.typeToFunc(elem_ty).?.is_generic, - .array => elem_ty.childType(zcu).hasRuntimeBitsIgnoreComptime(zcu), - else => elem_ty.hasRuntimeBitsIgnoreComptime(zcu), - }; - return if (lower_elem_ty) try o.lowerType(pt, elem_ty) else .i8; - } - fn lowerTypeFn(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { const zcu = pt.zcu; const ip = &zcu.intern_pool; @@ -3641,7 +3484,7 @@ pub const Object = struct { if (layout.payload_size == 0) return o.lowerValue(pt, un.tag); const union_obj = zcu.typeToUnion(ty).?; - const container_layout = union_obj.flagsUnordered(ip).layout; + const container_layout = union_obj.layout; assert(container_layout == .@"packed"); @@ -3699,7 +3542,9 @@ pub const Object = struct { return o.builder.undefConst(try o.lowerType(pt, Type.fromInterned(val_key.typeOf()))); } - const ty = Type.fromInterned(val_key.typeOf()); + const ty: Type = .fromInterned(val_key.typeOf()); + ty.assertHasLayout(zcu); + return switch (val_key) { .int_type, .ptr_type, @@ -3759,7 +3604,7 @@ pub const Object = struct { }; const err_int_ty = try pt.errorIntType(); const payload_type = ty.errorUnionPayload(zcu); - if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_type.hasRuntimeBits(zcu)) { // We use the error type directly as the type. return o.lowerValue(pt, err_val); } @@ -3821,7 +3666,7 @@ pub const Object = struct { const payload_ty = ty.optionalChild(zcu); const non_null_bit = try o.builder.intConst(.i8, @intFromBool(opt.val != .none)); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { return non_null_bit; } const llvm_ty = try o.lowerType(pt, ty); @@ -3857,6 +3702,7 @@ pub const Object = struct { fields[0..llvm_ty_fields.len], ), vals[0..llvm_ty_fields.len]); }, + .bitpack => |bitpack| return o.lowerValue(pt, bitpack.backing_int_val), .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) { .array_type => |array_type| switch (aggregate.storage) { .bytes => |bytes| try o.builder.stringConst(try o.builder.string( @@ -3988,7 +3834,7 @@ pub const Object = struct { 0.., ) |field_ty, field_val, field_index| { if (field_val != .none) continue; - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; const field_align = Type.fromInterned(field_ty).abiAlignment(zcu); big_align = big_align.max(field_align); @@ -4034,16 +3880,8 @@ pub const Object = struct { }, .struct_type => { const struct_type = ip.loadStructType(ty.toIntern()); - assert(struct_type.haveLayout(ip)); const struct_ty = try o.lowerType(pt, ty); - if (struct_type.layout == .@"packed") { - comptime assert(Type.packed_struct_layout_version == 2); - - const bits = ty.bitSize(zcu); - const llvm_int_ty = try o.builder.intType(@intCast(bits)); - - return o.lowerValueToInt(pt, llvm_int_ty, arg_val); - } + assert(struct_type.layout != .@"packed"); const llvm_len = struct_ty.aggregateLen(&o.builder); const ExpectedContents = extern struct { @@ -4063,15 +3901,12 @@ pub const Object = struct { comptime assert(struct_layout_version == 2); var llvm_index: usize = 0; var offset: u64 = 0; - var big_align: InternPool.Alignment = .@"1"; var need_unnamed = false; var field_it = struct_type.iterateRuntimeOrder(ip); while (field_it.next()) |field_index| { const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - const field_align = ty.fieldAlignment(field_index, zcu); - big_align = big_align.max(field_align); const prev_offset = offset; - offset = field_align.forward(offset); + offset = struct_type.field_offsets.get(ip)[field_index]; const padding_len = offset - prev_offset; if (padding_len > 0) { @@ -4084,7 +3919,7 @@ pub const Object = struct { llvm_index += 1; } - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!field_ty.hasRuntimeBits(zcu)) { // This is a zero-bit field - we only needed it for the alignment. continue; } @@ -4102,7 +3937,7 @@ pub const Object = struct { } { const prev_offset = offset; - offset = big_align.forward(offset); + offset = struct_type.alignment.forward(offset); const padding_len = offset - prev_offset; if (padding_len > 0) { fields[llvm_index] = try o.builder.arrayType(padding_len, .i8); @@ -4126,19 +3961,13 @@ pub const Object = struct { if (layout.payload_size == 0) return o.lowerValue(pt, un.tag); const union_obj = zcu.typeToUnion(ty).?; - const container_layout = union_obj.flagsUnordered(ip).layout; + const container_layout = union_obj.layout; + assert(container_layout != .@"packed"); var need_unnamed = false; const payload = if (un.tag != .none) p: { const field_index = zcu.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); - if (container_layout == .@"packed") { - if (!field_ty.hasRuntimeBits(zcu)) return o.builder.intConst(union_ty, 0); - const bits = ty.bitSize(zcu); - const llvm_int_ty = try o.builder.intType(@intCast(bits)); - - return o.lowerValueToInt(pt, llvm_int_ty, arg_val); - } // Sometimes we must make an unnamed struct because LLVM does // not support bitcasting our payload struct to the true union payload type. @@ -4146,7 +3975,7 @@ pub const Object = struct { // must pointer cast to the expected type before accessing the union. need_unnamed = layout.most_aligned_field != field_index; - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!field_ty.hasRuntimeBits(zcu)) { const padding_len = layout.payload_size; break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8)); } @@ -4165,13 +3994,6 @@ pub const Object = struct { ); } else p: { assert(layout.tag_size == 0); - if (container_layout == .@"packed") { - const bits = ty.bitSize(zcu); - const llvm_int_ty = try o.builder.intType(@intCast(bits)); - - return o.lowerValueToInt(pt, llvm_int_ty, arg_val); - } - const union_val = try o.lowerValue(pt, un.val); need_unnamed = true; break :p union_val; @@ -4273,7 +4095,14 @@ pub const Object = struct { }; return o.lowerPtr(pt, field.base, offset + field_off); }, - .arr_elem, .comptime_field, .comptime_alloc => unreachable, + .arr_elem => |arr_elem| { + const base_ptr_ty = Value.fromInterned(arr_elem.base).typeOf(zcu); + assert(base_ptr_ty.ptrSize(zcu) == .many); + const elem_size = base_ptr_ty.childType(zcu).abiSize(zcu); + return o.lowerPtr(pt, arr_elem.base, offset + elem_size * arr_elem.index); + }, + .comptime_field => unreachable, + .comptime_alloc => unreachable, }; } @@ -4298,12 +4127,11 @@ pub const Object = struct { const ptr_ty = Type.fromInterned(uav.orig_ty); - const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn"; - if ((!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) or - (is_fn_body and zcu.typeToFunc(uav_ty).?.is_generic)) return o.lowerPtrToVoid(pt, ptr_ty); + if (!uav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) { + return o.lowerPtrToVoid(pt, ptr_ty); + } - if (is_fn_body) - @panic("TODO"); + assert(uav_ty.zigTypeTag(zcu) != .@"fn"); // should be using a Nav ref const llvm_addr_space = toLlvmAddressSpace(ptr_ty.ptrAddressSpace(zcu), target); const alignment = ptr_ty.ptrAlignment(zcu); @@ -4326,14 +4154,11 @@ pub const Object = struct { const nav_ty = Type.fromInterned(nav.typeOf(ip)); const ptr_ty = try pt.navPtrType(nav_index); - const is_fn_body = nav_ty.zigTypeTag(zcu) == .@"fn"; - if ((!is_fn_body and !nav_ty.hasRuntimeBits(zcu)) or - (is_fn_body and zcu.typeToFunc(nav_ty).?.is_generic)) - { + if (nav.getExtern(ip) == null and !nav_ty.isRuntimeFnOrHasRuntimeBits(zcu)) { return o.lowerPtrToVoid(pt, ptr_ty); } - const llvm_global = if (is_fn_body) + const llvm_global = if (nav_ty.zigTypeTag(zcu) == .@"fn") (try o.resolveLlvmFunction(pt, nav_index)).ptrConst(&o.builder).global else (try o.resolveGlobalNav(pt, nav_index)).ptrConst(&o.builder).global; @@ -4376,21 +4201,18 @@ pub const Object = struct { /// types to work around a LLVM deficiency when targeting ARM/AArch64. fn getAtomicAbiType(o: *Object, pt: Zcu.PerThread, ty: Type, is_rmw_xchg: bool) Allocator.Error!Builder.Type { const zcu = pt.zcu; - const ip = &zcu.intern_pool; - const int_ty = switch (ty.zigTypeTag(zcu)) { - .int => ty, - .@"enum" => ty.intTagType(zcu), - .@"struct" => Type.fromInterned(ip.loadStructType(ty.toIntern()).backingIntTypeUnordered(ip)), + switch (ty.zigTypeTag(zcu)) { + .int, .@"enum", .@"struct", .@"union" => {}, .float => { if (!is_rmw_xchg) return .none; return o.builder.intType(@intCast(ty.abiSize(zcu) * 8)); }, .bool => return .i8, else => return .none, - }; - const bit_count = int_ty.intInfo(zcu).bits; + } + const bit_count = ty.bitSize(zcu); if (!std.math.isPowerOfTwo(bit_count) or (bit_count % 8) != 0) { - return o.builder.intType(@intCast(int_ty.abiSize(zcu) * 8)); + return o.builder.intType(@intCast(ty.abiSize(zcu) * 8)); } else { return .none; } @@ -4498,7 +4320,7 @@ pub const Object = struct { const ret_ty = try o.lowerType(pt, Type.slice_const_u8_sentinel_0); const target = &zcu.root_mod.resolved_target.result; const function_index = try o.builder.addFunction( - try o.builder.fnType(ret_ty, &.{try o.lowerType(pt, Type.fromInterned(enum_type.tag_ty))}, .normal), + try o.builder.fnType(ret_ty, &.{try o.lowerType(pt, Type.fromInterned(enum_type.int_tag_type))}, .normal), try o.builder.strtabStringFmt("__zig_tag_name_{f}", .{enum_type.name.fmt(ip)}), toLlvmAddressSpace(.generic, target), ); @@ -4521,12 +4343,16 @@ pub const Object = struct { const bad_value_block = try wip.block(1, "BadValue"); const tag_int_value = wip.arg(0); - var wip_switch = - try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len), .none); + var wip_switch = try wip.@"switch"( + tag_int_value, + bad_value_block, + @intCast(enum_type.field_names.len), + .none, + ); defer wip_switch.finish(&wip); - for (0..enum_type.names.len) |field_index| { - const name = try o.builder.stringNull(enum_type.names.get(ip)[field_index].toSlice(ip)); + for (0..enum_type.field_names.len) |field_index| { + const name = try o.builder.stringNull(enum_type.field_names.get(ip)[field_index].toSlice(ip)); const name_init = try o.builder.stringConst(name); const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); @@ -4597,7 +4423,41 @@ pub const NavGen = struct { const ty = Type.fromInterned(nav.typeOf(ip)); if (linkage != .internal and ip.isFunctionType(ty.toIntern())) { - _ = try o.resolveLlvmFunction(pt, owner_nav); + const function_index = try o.resolveLlvmFunction(pt, owner_nav); + // Add parameter attributes which weren't set by `resolveLlvmFunction` + const fn_info = zcu.typeToFunc(ty).?; + var attributes = try function_index.ptrConst(&o.builder).attributes.toWip(&o.builder); + defer attributes.deinit(&o.builder); + var it = iterateParamTypes(o, pt, fn_info); + if (firstParamSRet(fn_info, zcu, zcu.getTarget())) it.llvm_index += 1; + if (fn_info.cc == .auto and zcu.comp.config.any_error_tracing) it.llvm_index += 1; + while (try it.next()) |lowering| switch (lowering) { + .byval => { + const param_index = it.zig_index - 1; + const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); + if (!isByRef(param_ty, zcu)) { + try o.addByValParamAttrs(pt, &attributes, param_ty, param_index, fn_info, it.llvm_index - 1); + } + }, + .byref => { + const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); + const param_llvm_ty = try o.lowerType(pt, param_ty); + const alignment = param_ty.abiAlignment(zcu); + try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment.toLlvm(), it.byval_attr, param_llvm_ty); + }, + .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), + // No attributes needed for these. + .no_bits, + .abi_sized_int, + .multiple_llvm_types, + .float_array, + .i32_array, + .i64_array, + => continue, + + .slice => unreachable, // extern functions do not support slice types. + }; + function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); } else { const variable_index = try o.resolveGlobalNav(pt, nav_index); variable_index.setAlignment(zcu.navAlignment(nav_index).toLlvm(), &o.builder); @@ -4626,7 +4486,7 @@ pub const NavGen = struct { debug_file, // File debug_file, // Scope line_number, - try o.lowerDebugType(pt, ty), + try o.getDebugType(pt, ty), variable_index, .{ .local = linkage == .internal }, ); @@ -4748,7 +4608,7 @@ pub const FuncGen = struct { /// Have we seen loads or stores involving `allowzero` pointers? allowzero_access: bool = false, - pub fn maybeMarkAllowZeroAccess(self: *FuncGen, info: InternPool.Key.PtrType) void { + fn maybeMarkAllowZeroAccess(self: *FuncGen, info: InternPool.Key.PtrType) void { // LLVM already considers null pointers to be valid in non-generic address spaces, so avoid // pessimizing optimization for functions with accesses to such pointers. if (info.flags.address_space == .generic and info.flags.is_allowzero) self.allowzero_access = true; @@ -5216,7 +5076,7 @@ pub const FuncGen = struct { try o.builder.metadataString(nav.fqn.toSlice(&zcu.intern_pool)), line_number, line_number + func.lbrace_line, - try o.lowerDebugType(pt, fn_ty), + try o.getDebugType(pt, fn_ty), .{ .di_flags = .{ .StaticMember = true }, .sp_flags = .{ @@ -5514,7 +5374,7 @@ pub const FuncGen = struct { return .none; } - if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBitsIgnoreComptime(zcu)) { + if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBits(zcu)) { return .none; } @@ -5633,7 +5493,7 @@ pub const FuncGen = struct { return; } const fn_info = zcu.typeToFunc(Type.fromInterned(ip.getNav(self.ng.nav_index).typeOf(ip))).?; - if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ret_ty.hasRuntimeBits(zcu)) { if (Type.fromInterned(fn_info.return_type).isError(zcu)) { // Functions with an empty error set are emitted with an error code // return type and return zero so they can be function pointers coerced @@ -5698,7 +5558,7 @@ pub const FuncGen = struct { const ptr_ty = self.typeOf(un_op); const ret_ty = ptr_ty.childType(zcu); const fn_info = zcu.typeToFunc(Type.fromInterned(ip.getNav(self.ng.nav_index).typeOf(ip))).?; - if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ret_ty.hasRuntimeBits(zcu)) { if (Type.fromInterned(fn_info.return_type).isError(zcu)) { // Functions with an empty error set are emitted with an error code // return type and return zero so they can be function pointers coerced @@ -5829,14 +5689,13 @@ pub const FuncGen = struct { const o = self.ng.object; const pt = self.ng.pt; const zcu = pt.zcu; - const ip = &zcu.intern_pool; const scalar_ty = operand_ty.scalarType(zcu); const int_ty = switch (scalar_ty.zigTypeTag(zcu)) { .@"enum" => scalar_ty.intTagType(zcu), .int, .bool, .pointer, .error_set => scalar_ty, .optional => blk: { const payload_ty = operand_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu) or + if (!payload_ty.hasRuntimeBits(zcu) or operand_ty.optionalReprIsPayload(zcu)) { break :blk operand_ty; @@ -5908,12 +5767,7 @@ pub const FuncGen = struct { return phi.toValue(); }, .float => return self.buildFloatCmp(fast, op, operand_ty, .{ lhs, rhs }), - .@"struct" => blk: { - const struct_obj = ip.loadStructType(scalar_ty.toIntern()); - assert(struct_obj.layout == .@"packed"); - const backing_index = struct_obj.backingIntTypeUnordered(ip); - break :blk Type.fromInterned(backing_index); - }, + .@"struct" => scalar_ty.bitpackBackingInt(zcu), else => unreachable, }; const is_signed = int_ty.isSignedInt(zcu); @@ -6305,7 +6159,7 @@ pub const FuncGen = struct { const pt = fg.ng.pt; const zcu = pt.zcu; const payload_ty = err_union_ty.errorUnionPayload(zcu); - const payload_has_bits = payload_ty.hasRuntimeBitsIgnoreComptime(zcu); + const payload_has_bits = payload_ty.hasRuntimeBits(zcu); const err_union_llvm_ty = try o.lowerType(pt, err_union_ty); const error_type = try o.errorIntType(pt); @@ -6641,7 +6495,7 @@ pub const FuncGen = struct { const len = try o.builder.intValue(llvm_usize, array_ty.arrayLen(zcu)); const slice_llvm_ty = try o.lowerType(pt, self.typeOfIndex(inst)); const operand = try self.resolveInst(ty_op.operand); - if (!array_ty.hasRuntimeBitsIgnoreComptime(zcu)) + if (!array_ty.hasRuntimeBits(zcu)) return self.wip.buildAggregate(slice_llvm_ty, &.{ operand, len }, ""); const ptr = try self.wip.gep(.inbounds, try o.lowerType(pt, array_ty), operand, &.{ try o.builder.intValue(llvm_usize, 0), try o.builder.intValue(llvm_usize, 0), @@ -6824,7 +6678,7 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const slice_ptr = try self.resolveInst(ty_op.operand); const slice_ptr_ty = self.typeOf(ty_op.operand); - const slice_llvm_ty = try o.lowerPtrElemTy(pt, slice_ptr_ty.childType(zcu)); + const slice_llvm_ty = try o.lowerType(pt, slice_ptr_ty.childType(zcu)); return self.wip.gepStruct(slice_llvm_ty, slice_ptr, index, ""); } @@ -6838,7 +6692,7 @@ pub const FuncGen = struct { const slice = try self.resolveInst(bin_op.lhs); const index = try self.resolveInst(bin_op.rhs); const elem_ty = slice_ty.childType(zcu); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, elem_ty); + const llvm_elem_ty = try o.lowerType(pt, elem_ty); const base_ptr = try self.wip.extractValue(slice, &.{0}, ""); const ptr = try self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{index}, ""); if (isByRef(elem_ty, zcu)) { @@ -6863,7 +6717,7 @@ pub const FuncGen = struct { const slice = try self.resolveInst(bin_op.lhs); const index = try self.resolveInst(bin_op.rhs); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, slice_ty.childType(zcu)); + const llvm_elem_ty = try o.lowerType(pt, slice_ty.childType(zcu)); const base_ptr = try self.wip.extractValue(slice, &.{0}, ""); return self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{index}, ""); } @@ -6903,7 +6757,7 @@ pub const FuncGen = struct { const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_ty = self.typeOf(bin_op.lhs); const elem_ty = ptr_ty.childType(zcu); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, elem_ty); + const llvm_elem_ty = try o.lowerType(pt, elem_ty); const base_ptr = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); // TODO: when we go fully opaque pointers in LLVM 16 we can remove this branch @@ -6930,8 +6784,8 @@ pub const FuncGen = struct { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const ptr_ty = self.typeOf(bin_op.lhs); - const elem_ty = ptr_ty.childType(zcu); - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) return self.resolveInst(bin_op.lhs); + const elem_ty = ptr_ty.indexableElem(zcu); + assert(elem_ty.hasRuntimeBits(zcu)); const base_ptr = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -6939,12 +6793,8 @@ pub const FuncGen = struct { const elem_ptr = ty_pl.ty.toType(); if (elem_ptr.ptrInfo(zcu).flags.vector_index != .none) return base_ptr; - const llvm_elem_ty = try o.lowerPtrElemTy(pt, elem_ty); - return self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, if (ptr_ty.isSinglePointer(zcu)) - // If this is a single-item pointer to an array, we need another index in the GEP. - &.{ try o.builder.intValue(try o.lowerType(pt, Type.usize), 0), rhs } - else - &.{rhs}, ""); + const llvm_elem_ty = try o.lowerType(pt, elem_ty); + return self.wip.gep(.inbounds, llvm_elem_ty, base_ptr, &.{rhs}, ""); } fn airStructFieldPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { @@ -6976,7 +6826,7 @@ pub const FuncGen = struct { const struct_llvm_val = try self.resolveInst(struct_field.struct_operand); const field_index = struct_field.field_index; const field_ty = struct_ty.fieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) return .none; + if (!field_ty.hasRuntimeBits(zcu)) return .none; if (!isByRef(struct_ty, zcu)) { assert(!isByRef(field_ty, zcu)); @@ -7037,15 +6887,17 @@ pub const FuncGen = struct { const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?; const field_ptr = try self.wip.gepStruct(struct_llvm_ty, struct_llvm_val, llvm_field_index, ""); - const alignment = struct_ty.fieldAlignment(field_index, zcu); + const explicit_alignment = struct_ty.explicitFieldAlignment(field_index, zcu); const field_ptr_ty = try pt.ptrType(.{ .child = field_ty.toIntern(), - .flags = .{ .alignment = alignment }, + .flags = .{ .alignment = explicit_alignment }, }); if (isByRef(field_ty, zcu)) { - assert(alignment != .none); - const field_alignment = alignment.toLlvm(); - return self.loadByRef(field_ptr, field_ty, field_alignment, .normal); + const alignment = switch (explicit_alignment) { + .none => field_ty.abiAlignment(zcu), + else => |a| a, + }; + return self.loadByRef(field_ptr, field_ty, alignment.toLlvm(), .normal); } else { return self.load(field_ptr, field_ptr_ty); } @@ -7146,7 +6998,7 @@ pub const FuncGen = struct { self.file, self.scope, self.prev_dbg_line, - try o.lowerDebugType(pt, ptr_ty.childType(zcu)), + try o.getDebugType(pt, ptr_ty.childType(zcu)), ); _ = try self.wip.callIntrinsic( @@ -7179,7 +7031,7 @@ pub const FuncGen = struct { self.file, self.scope, self.prev_dbg_line, - try o.lowerDebugType(pt, operand_ty), + try o.getDebugType(pt, operand_ty), arg_no: { self.arg_inline_index += 1; break :arg_no self.arg_inline_index; @@ -7189,7 +7041,7 @@ pub const FuncGen = struct { self.file, self.scope, self.prev_dbg_line, - try o.lowerDebugType(pt, operand_ty), + try o.getDebugType(pt, operand_ty), ); const zcu = pt.zcu; @@ -7280,6 +7132,7 @@ pub const FuncGen = struct { const llvm_param_attrs = try arena.alloc(Builder.Type, max_param_count); const pt = self.ng.pt; const zcu = pt.zcu; + const ip = &zcu.intern_pool; const target = zcu.getTarget(); var llvm_ret_i: usize = 0; @@ -7304,7 +7157,7 @@ pub const FuncGen = struct { const output_inst = try self.resolveInst(output.operand); const output_ty = self.typeOf(output.operand); assert(output_ty.zigTypeTag(zcu) == .pointer); - const elem_llvm_ty = try o.lowerPtrElemTy(pt, output_ty.childType(zcu)); + const elem_llvm_ty = try o.lowerType(pt, output_ty.childType(zcu)); switch (constraint[0]) { '=' => {}, @@ -7422,7 +7275,7 @@ pub const FuncGen = struct { llvm_param_attrs[llvm_param_i] = if (constraint[0] == '*') blk: { if (!is_by_ref) self.maybeMarkAllowZeroAccess(arg_ty.ptrInfo(zcu)); - break :blk try o.lowerPtrElemTy(pt, if (is_by_ref) arg_ty else arg_ty.childType(zcu)); + break :blk try o.lowerType(pt, if (is_by_ref) arg_ty else arg_ty.childType(zcu)); } else .none; llvm_param_i += 1; @@ -7436,7 +7289,7 @@ pub const FuncGen = struct { if (constraint[0] != '+') continue; const rw_ty = self.typeOf(output.operand); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, rw_ty.childType(zcu)); + const llvm_elem_ty = try o.lowerType(pt, rw_ty.childType(zcu)); if (llvm_ret_indirect[output.index]) { llvm_param_values[llvm_param_i] = llvm_rw_vals[output.index]; llvm_param_types[llvm_param_i] = llvm_rw_vals[output.index].typeOfWip(&self.wip); @@ -7463,7 +7316,7 @@ pub const FuncGen = struct { total_i += 1; } - const ip = &zcu.intern_pool; + if (total_i != 0) try llvm_constraints.append(gpa, ','); const clobbers_val: Value = .fromInterned(unwrapped_asm.clobbers); const clobbers_ty = clobbers_val.typeOf(zcu); var clobbers_bigint_buf: Value.BigIntSpace = undefined; @@ -7663,7 +7516,7 @@ pub const FuncGen = struct { comptime assert(optional_layout_version == 3); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { const loaded = if (operand_is_ptr) try self.wip.load(access_kind, optional_llvm_ty, operand, .default, "") else @@ -7706,7 +7559,7 @@ pub const FuncGen = struct { if (operand_is_ptr) self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { const loaded = if (operand_is_ptr) try self.wip.load(access_kind, try o.lowerType(pt, err_union_ty), operand, .default, "") else @@ -7733,7 +7586,7 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const optional_ty = self.typeOf(ty_op.operand).childType(zcu); const payload_ty = optional_ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { // We have a pointer to a zero-bit value and we need to return // a pointer to a zero-bit value. return operand; @@ -7761,7 +7614,7 @@ pub const FuncGen = struct { const access_kind: Builder.MemoryAccessKind = if (optional_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { self.maybeMarkAllowZeroAccess(optional_ptr_ty.ptrInfo(zcu)); // We have a pointer to a i8. We need to set it to 1 and then return the same pointer. @@ -7797,7 +7650,7 @@ pub const FuncGen = struct { const operand = try self.resolveInst(ty_op.operand); const optional_ty = self.typeOf(ty_op.operand); const payload_ty = self.typeOfIndex(inst); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return .none; + if (!payload_ty.hasRuntimeBits(zcu)) return .none; if (optional_ty.optionalReprIsPayload(zcu)) { // Payload value is the same as the optional value. @@ -7819,7 +7672,7 @@ pub const FuncGen = struct { const result_ty = self.typeOfIndex(inst); const payload_ty = if (operand_is_ptr) result_ty.childType(zcu) else result_ty; - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { return if (operand_is_ptr) operand else .none; } const offset = try errUnionPayloadOffset(payload_ty, pt); @@ -7863,7 +7716,7 @@ pub const FuncGen = struct { if (operand_is_ptr and operand_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; const payload_ty = err_union_ty.errorUnionPayload(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { if (!operand_is_ptr) return operand; self.maybeMarkAllowZeroAccess(operand_ty.ptrInfo(zcu)); @@ -7899,7 +7752,7 @@ pub const FuncGen = struct { const access_kind: Builder.MemoryAccessKind = if (err_union_ptr_ty.isVolatilePtr(zcu)) .@"volatile" else .normal; - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { self.maybeMarkAllowZeroAccess(err_union_ptr_ty.ptrInfo(zcu)); _ = try self.wip.store(access_kind, non_error_val, operand, .default); @@ -7946,9 +7799,8 @@ pub const FuncGen = struct { const struct_llvm_ty = try o.lowerType(pt, struct_ty); const llvm_field_index = o.llvmFieldIndex(struct_ty, field_index).?; assert(self.err_ret_trace != .none); - const field_ptr = - try self.wip.gepStruct(struct_llvm_ty, self.err_ret_trace, llvm_field_index, ""); - const field_alignment = struct_ty.fieldAlignment(field_index, zcu); + const field_ptr = try self.wip.gepStruct(struct_llvm_ty, self.err_ret_trace, llvm_field_index, ""); + const field_alignment = struct_ty.explicitFieldAlignment(field_index, zcu); const field_ty = struct_ty.fieldType(field_index, zcu); const field_ptr_ty = try pt.ptrType(.{ .child = field_ty.toIntern(), @@ -7989,7 +7841,7 @@ pub const FuncGen = struct { const payload_ty = self.typeOf(ty_op.operand); const non_null_bit = try o.builder.intValue(.i8, 1); comptime assert(optional_layout_version == 3); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return non_null_bit; + assert(payload_ty.hasRuntimeBits(zcu)); const operand = try self.resolveInst(ty_op.operand); const optional_ty = self.typeOfIndex(inst); if (optional_ty.optionalReprIsPayload(zcu)) return operand; @@ -8023,9 +7875,7 @@ pub const FuncGen = struct { const err_un_ty = self.typeOfIndex(inst); const operand = try self.resolveInst(ty_op.operand); const payload_ty = self.typeOf(ty_op.operand); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return operand; - } + assert(payload_ty.hasRuntimeBits(zcu)); const ok_err_code = try o.builder.intValue(try o.errorIntType(pt), 0); const err_un_llvm_ty = try o.lowerType(pt, err_un_ty); @@ -8065,7 +7915,7 @@ pub const FuncGen = struct { const err_un_ty = self.typeOfIndex(inst); const payload_ty = err_un_ty.errorUnionPayload(zcu); const operand = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) return operand; + if (!payload_ty.hasRuntimeBits(zcu)) return operand; const err_un_llvm_ty = try o.lowerType(pt, err_un_ty); const payload_offset = try errUnionPayloadOffset(payload_ty, pt); @@ -8517,7 +8367,7 @@ pub const FuncGen = struct { const ptr = try self.resolveInst(bin_op.lhs); const offset = try self.resolveInst(bin_op.rhs); const ptr_ty = self.typeOf(bin_op.lhs); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, ptr_ty.childType(zcu)); + const llvm_elem_ty = try o.lowerType(pt, ptr_ty.childType(zcu)); switch (ptr_ty.ptrSize(zcu)) { // It's a pointer to an array, so according to LLVM we need an extra GEP index. .one => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{ @@ -8541,7 +8391,7 @@ pub const FuncGen = struct { const offset = try self.resolveInst(bin_op.rhs); const negative_offset = try self.wip.neg(offset, ""); const ptr_ty = self.typeOf(bin_op.lhs); - const llvm_elem_ty = try o.lowerPtrElemTy(pt, ptr_ty.childType(zcu)); + const llvm_elem_ty = try o.lowerType(pt, ptr_ty.childType(zcu)); switch (ptr_ty.ptrSize(zcu)) { // It's a pointer to an array, so according to LLVM we need an extra GEP index. .one => return self.wip.gep(.inbounds, llvm_elem_ty, ptr, &.{ @@ -9502,7 +9352,7 @@ pub const FuncGen = struct { self.file, self.scope, lbrace_line, - try o.lowerDebugType(pt, inst_ty), + try o.getDebugType(pt, inst_ty), self.arg_index, ); @@ -9836,7 +9686,7 @@ pub const FuncGen = struct { const ptr_ty = self.typeOf(atomic_load.ptr); const info = ptr_ty.ptrInfo(zcu); const elem_ty = Type.fromInterned(info.child); - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) return .none; + if (!elem_ty.hasRuntimeBits(zcu)) return .none; const ordering = toLlvmAtomicOrdering(atomic_load.order); const llvm_abi_ty = try o.getAtomicAbiType(pt, elem_ty, false); const ptr_alignment = (if (info.flags.alignment != .none) @@ -10304,7 +10154,7 @@ pub const FuncGen = struct { const target = &zcu.root_mod.resolved_target.result; const function_index = try o.builder.addFunction( - try o.builder.fnType(.i1, &.{try o.lowerType(pt, Type.fromInterned(enum_type.tag_ty))}, .normal), + try o.builder.fnType(.i1, &.{try o.lowerType(pt, Type.fromInterned(enum_type.int_tag_type))}, .normal), try o.builder.strtabStringFmt("__zig_is_named_enum_value_{f}", .{enum_type.name.fmt(ip)}), toLlvmAddressSpace(.generic, target), ); @@ -10325,13 +10175,13 @@ pub const FuncGen = struct { defer wip.deinit(); wip.cursor = .{ .block = try wip.block(0, "Entry") }; - const named_block = try wip.block(@intCast(enum_type.names.len), "Named"); + const named_block = try wip.block(@intCast(enum_type.field_names.len), "Named"); const unnamed_block = try wip.block(1, "Unnamed"); const tag_int_value = wip.arg(0); - var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(enum_type.names.len), .none); + var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(enum_type.field_names.len), .none); defer wip_switch.finish(&wip); - for (0..enum_type.names.len) |field_index| { + for (0..enum_type.field_names.len) |field_index| { const this_tag_int_value = try o.lowerValue( pt, (try pt.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), @@ -10800,15 +10650,14 @@ pub const FuncGen = struct { }, .@"struct" => { if (zcu.typeToPackedStruct(result_ty)) |struct_type| { - const backing_int_ty = struct_type.backingIntTypeUnordered(ip); - assert(backing_int_ty != .none); - const big_bits = Type.fromInterned(backing_int_ty).bitSize(zcu); + const backing_int_ty: Type = .fromInterned(struct_type.packed_backing_int_type); + const big_bits = backing_int_ty.bitSize(zcu); const int_ty = try o.builder.intType(@intCast(big_bits)); comptime assert(Type.packed_struct_layout_version == 2); var running_int = try o.builder.intValue(int_ty, 0); var running_bits: u16 = 0; for (elements, struct_type.field_types.get(ip)) |elem, field_ty| { - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; const non_int_val = try self.resolveInst(elem); const ty_bit_size: u16 = @intCast(Type.fromInterned(field_ty).bitSize(zcu)); @@ -10840,12 +10689,12 @@ pub const FuncGen = struct { const llvm_elem = try self.resolveInst(elem); const llvm_i = o.llvmFieldIndex(result_ty, i).?; - const field_ptr = - try self.wip.gepStruct(llvm_result_ty, alloca_inst, llvm_i, ""); + const field_ptr = try self.wip.gepStruct(llvm_result_ty, alloca_inst, llvm_i, ""); + const field_ptr_ty = try pt.ptrType(.{ .child = self.typeOf(elem).toIntern(), .flags = .{ - .alignment = result_ty.fieldAlignment(i, zcu), + .alignment = result_ty.explicitFieldAlignment(i, zcu), }, }); try self.store(field_ptr, field_ptr_ty, llvm_elem, .none); @@ -10910,7 +10759,7 @@ pub const FuncGen = struct { const layout = union_ty.unionGetLayout(zcu); const union_obj = zcu.typeToUnion(union_ty).?; - if (union_obj.flagsUnordered(ip).layout == .@"packed") { + if (union_obj.layout == .@"packed") { const big_bits = union_ty.bitSize(zcu); const int_llvm_ty = try o.builder.intType(@intCast(big_bits)); const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); @@ -10925,10 +10774,8 @@ pub const FuncGen = struct { const tag_int_val = blk: { const tag_ty = union_ty.unionTagTypeHypothetical(zcu); - const union_field_name = union_obj.loadTagType(ip).names.get(ip)[extra.field_index]; - const enum_field_index = tag_ty.enumFieldIndex(union_field_name, zcu).?; - const tag_val = try pt.enumValueFieldIndex(tag_ty, enum_field_index); - break :blk try tag_val.intFromEnum(tag_ty, pt); + const tag_val = try pt.enumValueFieldIndex(tag_ty, extra.field_index); + break :blk tag_val.intFromEnum(zcu); }; if (layout.payload_size == 0) { if (layout.tag_size == 0) { @@ -10950,16 +10797,14 @@ pub const FuncGen = struct { const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); const field_llvm_ty = try o.lowerType(pt, field_ty); const field_size = field_ty.abiSize(zcu); - const field_align = union_ty.fieldAlignment(extra.field_index, zcu); + const field_align = union_ty.explicitFieldAlignment(extra.field_index, zcu); const llvm_usize = try o.lowerType(pt, Type.usize); const usize_zero = try o.builder.intValue(llvm_usize, 0); + assert(field_ty.hasRuntimeBits(zcu)); + const llvm_union_ty = t: { const payload_ty = p: { - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - const padding_len = layout.payload_size; - break :p try o.builder.arrayType(padding_len, .i8); - } if (field_size == layout.payload_size) { break :p field_llvm_ty; } @@ -10969,7 +10814,7 @@ pub const FuncGen = struct { }); }; if (layout.tag_size == 0) break :t try o.builder.structType(.normal, &.{payload_ty}); - const tag_ty = try o.lowerType(pt, Type.fromInterned(union_obj.enum_tag_ty)); + const tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); var fields: [3]Builder.Type = undefined; var fields_len: usize = 2; if (layout.tag_align.compare(.gte, layout.payload_align)) { @@ -11010,11 +10855,11 @@ pub const FuncGen = struct { const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align)); const indices: [2]Builder.Value = .{ usize_zero, try o.builder.intValue(.i32, tag_index) }; const field_ptr = try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, &indices, ""); - const tag_ty = try o.lowerType(pt, Type.fromInterned(union_obj.enum_tag_ty)); + const tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); var big_int_space: Value.BigIntSpace = undefined; const tag_big_int = tag_int_val.toBigInt(&big_int_space, zcu); const llvm_tag = try o.builder.bigIntValue(tag_ty, tag_big_int); - const tag_alignment = Type.fromInterned(union_obj.enum_tag_ty).abiAlignment(zcu).toLlvm(); + const tag_alignment = Type.fromInterned(union_obj.enum_tag_type).abiAlignment(zcu).toLlvm(); _ = try self.wip.store(.normal, llvm_tag, field_ptr, tag_alignment); } @@ -11295,8 +11140,10 @@ pub const FuncGen = struct { return self.wip.gep(.inbounds, .i8, struct_ptr, &.{llvm_index}, ""); }, else => { - const struct_llvm_ty = try o.lowerPtrElemTy(pt, struct_ty); - + if (!struct_ty.hasRuntimeBits(zcu)) { + return struct_ptr; + } + const struct_llvm_ty = try o.lowerType(pt, struct_ty); if (o.llvmFieldIndex(struct_ty, field_index)) |llvm_field_index| { return self.wip.gepStruct(struct_llvm_ty, struct_ptr, llvm_field_index, ""); } else { @@ -11306,7 +11153,7 @@ pub const FuncGen = struct { // the struct. const llvm_index = try o.builder.intValue( try o.lowerType(pt, Type.usize), - @intFromBool(struct_ty.hasRuntimeBitsIgnoreComptime(zcu)), + @intFromBool(struct_ty.hasRuntimeBits(zcu)), ); return self.wip.gep(.inbounds, struct_llvm_ty, struct_ptr, &.{llvm_index}, ""); } @@ -11393,7 +11240,7 @@ pub const FuncGen = struct { const zcu = pt.zcu; const info = ptr_ty.ptrInfo(zcu); const elem_ty = Type.fromInterned(info.child); - if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) return .none; + if (!elem_ty.hasRuntimeBits(zcu)) return .none; const ptr_alignment = (if (info.flags.alignment != .none) @as(InternPool.Alignment, info.flags.alignment) @@ -12048,7 +11895,7 @@ fn returnTypeByRef(zcu: *Zcu, target: *const std.Target, ty: Type) bool { fn firstParamSRet(fn_info: InternPool.Key.FuncType, zcu: *Zcu, target: *const std.Target) bool { const return_type = Type.fromInterned(fn_info.return_type); - if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) return false; + if (!return_type.hasRuntimeBits(zcu)) return false; return switch (fn_info.cc) { .auto => returnTypeByRef(zcu, target, return_type), @@ -12088,11 +11935,9 @@ fn firstParamSRetSystemV(ty: Type, zcu: *Zcu, target: *const std.Target) bool { fn lowerFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) Allocator.Error!Builder.Type { const zcu = pt.zcu; const return_type = Type.fromInterned(fn_info.return_type); - if (!return_type.hasRuntimeBitsIgnoreComptime(zcu)) { - // If the return type is an error set or an error union, then we make this - // anyerror return type instead, so that it can be coerced into a function - // pointer type which has anyerror as the return type. - return if (return_type.isError(zcu)) try o.errorIntType(pt) else .void; + if (!return_type.hasRuntimeBits(zcu)) { + assert(!return_type.isError(zcu)); + return .void; } const target = zcu.getTarget(); switch (fn_info.cc) { @@ -12136,7 +11981,7 @@ fn lowerFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.FuncType) var types: [8]Builder.Type = undefined; for (0..return_type.structFieldCount(zcu)) |field_index| { const field_ty = return_type.fieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; types[types_len] = try o.lowerType(pt, field_ty); types_len += 1; } @@ -12174,6 +12019,7 @@ fn lowerSystemVFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.Fu const zcu = pt.zcu; const ip = &zcu.intern_pool; const return_type = Type.fromInterned(fn_info.return_type); + return_type.assertHasLayout(zcu); if (isScalar(zcu, return_type)) { return o.lowerType(pt, return_type); } @@ -12222,9 +12068,7 @@ fn lowerSystemVFnRetTy(o: *Object, pt: Zcu.PerThread, fn_info: InternPool.Key.Fu assert(first_non_integer orelse classes.len == types_index); switch (ip.indexToKey(return_type.toIntern())) { .struct_type => { - const struct_type = ip.loadStructType(return_type.toIntern()); - assert(struct_type.haveLayout(ip)); - const size: u64 = struct_type.sizeUnordered(ip); + const size = return_type.abiSize(zcu); assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index); if (size % 8 > 0) { types_buffer[types_index - 1] = try o.builder.intType(@intCast(size % 8 * 8)); @@ -12260,7 +12104,7 @@ const ParamTypeIterator = struct { i64_array: u8, }; - pub fn next(it: *ParamTypeIterator) Allocator.Error!?Lowering { + fn next(it: *ParamTypeIterator) Allocator.Error!?Lowering { if (it.zig_index >= it.fn_info.param_types.len) return null; const ip = &it.pt.zcu.intern_pool; const ty = it.fn_info.param_types.get(ip)[it.zig_index]; @@ -12269,7 +12113,7 @@ const ParamTypeIterator = struct { } /// `airCall` uses this instead of `next` so that it can take into account variadic functions. - pub fn nextCall(it: *ParamTypeIterator, fg: *FuncGen, args: []const Air.Inst.Ref) Allocator.Error!?Lowering { + fn nextCall(it: *ParamTypeIterator, fg: *FuncGen, args: []const Air.Inst.Ref) Allocator.Error!?Lowering { assert(std.meta.eql(it.pt, fg.ng.pt)); const ip = &it.pt.zcu.intern_pool; if (it.zig_index >= it.fn_info.param_types.len) { @@ -12288,7 +12132,7 @@ const ParamTypeIterator = struct { const zcu = pt.zcu; const target = zcu.getTarget(); - if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!ty.hasRuntimeBits(zcu)) { it.zig_index += 1; return .no_bits; } @@ -12383,7 +12227,7 @@ const ParamTypeIterator = struct { it.types_len = 0; for (0..ty.structFieldCount(zcu)) |field_index| { const field_ty = ty.fieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; it.types_buffer[it.types_len] = try it.object.lowerType(pt, field_ty); it.types_len += 1; } @@ -12460,6 +12304,7 @@ const ParamTypeIterator = struct { fn nextSystemV(it: *ParamTypeIterator, ty: Type) Allocator.Error!?Lowering { const zcu = it.pt.zcu; const ip = &zcu.intern_pool; + ty.assertHasLayout(zcu); const classes = x86_64_abi.classifySystemV(ty, zcu, zcu.getTarget(), .arg); if (classes[0] == .memory) { it.zig_index += 1; @@ -12531,9 +12376,7 @@ const ParamTypeIterator = struct { } switch (ip.indexToKey(ty.toIntern())) { .struct_type => { - const struct_type = ip.loadStructType(ty.toIntern()); - assert(struct_type.haveLayout(ip)); - const size: u64 = struct_type.sizeUnordered(ip); + const size = ty.abiSize(zcu); assert((std.math.divCeil(u64, size, 8) catch unreachable) == types_index); if (size % 8 > 0) { types_buffer[types_index - 1] = @@ -12707,14 +12550,14 @@ fn isByRef(ty: Type, zcu: *Zcu) bool { }, .error_union => { const payload_ty = ty.errorUnionPayload(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { return false; } return true; }, .optional => { const payload_ty = ty.optionalChild(zcu); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + if (!payload_ty.hasRuntimeBits(zcu)) { return false; } if (ty.optionalReprIsPayload(zcu)) { diff --git a/src/codegen/mips/abi.zig b/src/codegen/mips/abi.zig @@ -13,7 +13,7 @@ pub const Context = enum { ret, arg }; pub fn classifyType(ty: Type, zcu: *Zcu, ctx: Context) Class { const target = zcu.getTarget(); - std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + std.debug.assert(ty.hasRuntimeBits(zcu)); const max_direct_size = target.ptrBitWidth() * 2; switch (ty.zigTypeTag(zcu)) { diff --git a/src/codegen/riscv64/abi.zig b/src/codegen/riscv64/abi.zig @@ -11,7 +11,7 @@ pub const Class = enum { memory, byval, integer, double_integer, fields }; pub fn classifyType(ty: Type, zcu: *Zcu) Class { const target = zcu.getTarget(); - std.debug.assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + std.debug.assert(ty.hasRuntimeBits(zcu)); const max_byval_size = target.ptrBitWidth() * 2; switch (ty.zigTypeTag(zcu)) { @@ -27,7 +27,7 @@ pub fn classifyType(ty: Type, zcu: *Zcu) Class { var field_count: usize = 0; for (0..ty.structFieldCount(zcu)) |field_index| { const field_ty = ty.fieldType(field_index, zcu); - if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; + if (!field_ty.hasRuntimeBits(zcu)) continue; if (field_ty.isRuntimeFloat()) any_fp = true else if (!field_ty.isAbiInt(zcu)) diff --git a/src/codegen/wasm/abi.zig b/src/codegen/wasm/abi.zig @@ -22,7 +22,7 @@ pub const Class = union(enum) { /// or returned as value within a wasm function. pub fn classifyType(ty: Type, zcu: *const Zcu) Class { const ip = &zcu.intern_pool; - assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); + assert(ty.hasRuntimeBits(zcu)); switch (ty.zigTypeTag(zcu)) { .int, .@"enum", .error_set => return .{ .direct = ty }, .float => return .{ .direct = ty }, @@ -47,7 +47,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class { return .indirect; } const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]); - const explicit_align = struct_type.fieldAlign(ip, 0); + const explicit_align = struct_type.field_aligns.getOrNone(ip, 0); if (explicit_align != .none) { if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu))) return .indirect; @@ -56,7 +56,7 @@ pub fn classifyType(ty: Type, zcu: *const Zcu) Class { }, .@"union" => { const union_obj = zcu.typeToUnion(ty).?; - if (union_obj.flagsUnordered(ip).layout == .@"packed") { + if (union_obj.layout == .@"packed") { return .{ .direct = ty }; } const layout = ty.unionGetLayout(zcu); diff --git a/src/link.zig b/src/link.zig @@ -29,6 +29,7 @@ const codegen = @import("codegen.zig"); pub const aarch64 = @import("link/aarch64.zig"); pub const LdScript = @import("link/LdScript.zig"); pub const Queue = @import("link/Queue.zig"); +pub const DebugConstPool = @import("link/DebugConstPool.zig"); pub const Diags = struct { /// Stored here so that function definitions can distinguish between @@ -1587,8 +1588,9 @@ pub fn doZcuTask(comp: *Compilation, tid: Zcu.PerThread.Id, task: ZcuTask) void const ty_prog_node = comp.link_prog_node.start(name, 0); defer ty_prog_node.end(); if (zcu.llvm_object) |llvm_object| { - _ = llvm_object; - @compileError("MLUGG TODO"); + llvm_object.updateContainerType(pt, container_update.ty, container_update.success) catch |err| switch (err) { + error.OutOfMemory => diags.setAllocFailure(), + }; } else { if (comp.bin_file) |lf| { lf.updateContainerType(pt, container_update.ty, container_update.success) catch |err| switch (err) { diff --git a/src/link/DebugConstPool.zig b/src/link/DebugConstPool.zig @@ -13,6 +13,9 @@ /// * forward `updateContainerType` calls to its `DebugConstPool` /// * expose some callback functions---see functions in `DebugInfo` /// * ensure that any `get` call is eventually followed by a `flushPending` call +/// +/// TODO: everything in this file should have the error set 'Allocator.Error', but right now the +/// self-hosted linkers can return all kinds of crap for some reason. This needs fixing. const DebugConstPool = @This(); values: std.AutoArrayHashMapUnmanaged(InternPool.Index, void), diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig @@ -18,7 +18,7 @@ const codegen = @import("../codegen.zig"); const dev = @import("../dev.zig"); const link = @import("../link.zig"); const target_info = @import("../target.zig"); -const DebugConstPool = @import("DebugConstPool.zig"); +const DebugConstPool = link.DebugConstPool; gpa: Allocator, bin_file: *link.File,