From ecfb18286a5270943382effdf383bae4546b184f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 15 Oct 2023 16:49:59 -0700 Subject: [PATCH 1/7] migrate make_ptr_const to new anonymous decl mechanism Instead of creating Module.Decl objects, directly create InternPool pointer values using the anon_decl Addr encoding. The LLVM backend needed code to notice the alignment of the pointer and lower accordingly. The other backends likely need a similar change. --- src/Sema.zig | 7 ++++--- src/codegen/llvm.zig | 6 +++++- test/behavior.zig | 1 - test/behavior/align.zig | 9 +++++++++ test/behavior/bugs/1741.zig | 11 ----------- 5 files changed, 18 insertions(+), 16 deletions(-) delete mode 100644 test/behavior/bugs/1741.zig diff --git a/src/Sema.zig b/src/Sema.zig index 2f861a87e660fc8f4783b136de0855311e2e3ab0..3e8eeeb88681e7fa0ba62600ccb5f6a4ce8b5349 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3657,9 +3657,10 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro const elem_ty = ptr_info.child.toType(); if (try sema.resolveComptimeKnownAllocValue(block, alloc, null)) |val| { - var anon_decl = try block.startAnonDecl(); - defer anon_decl.deinit(); - const new_mut_ptr = try sema.analyzeDeclRef(try anon_decl.finish(elem_ty, val.toValue(), ptr_info.flags.alignment)); + const new_mut_ptr = Air.internedToRef((try mod.intern(.{ .ptr = .{ + .ty = alloc_ty.toIntern(), + .addr = .{ .anon_decl = val }, + } }))); return sema.makePtrConst(block, new_mut_ptr); } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index cddd4340d70643825b32fe8f818d38c5ae85edce..9bb012deb1ef8585127ca0e164827df3866b02e4 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3049,6 +3049,7 @@ pub const Object = struct { o: *Object, decl_val: InternPool.Index, llvm_addr_space: Builder.AddrSpace, + alignment: InternPool.Alignment, ) Error!Builder.Variable.Index { // TODO: Add address space to the anon_decl_map const gop = try o.anon_decl_map.getOrPut(o.gpa, decl_val); @@ -3068,6 +3069,8 @@ pub const Object = struct { try variable_index.setInitializer(try o.lowerValue(decl_val), &o.builder); variable_index.setLinkage(.internal, &o.builder); variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); + if (alignment != .none) + variable_index.setAlignment(alignment.toLlvm(), &o.builder); return variable_index; } @@ -4415,7 +4418,8 @@ pub const Object = struct { const addr_space = target_util.defaultAddressSpace(target, .global_constant); const llvm_addr_space = toLlvmAddressSpace(addr_space, target); - const llvm_global = (try o.resolveGlobalAnonDecl(decl_val, llvm_addr_space)).ptrConst(&o.builder).global; + const alignment = ptr_ty.ptrAlignment(mod); + const llvm_global = (try o.resolveGlobalAnonDecl(decl_val, llvm_addr_space, alignment)).ptrConst(&o.builder).global; const llvm_val = try o.builder.convConst( .unneeded, diff --git a/test/behavior.zig b/test/behavior.zig index 7523c60ae8a731e382a18b23106aa917c077312a..9e03a8f000d13c9996d67ddeb316ad1922fd9804 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -37,7 +37,6 @@ test { _ = @import("behavior/bugs/1500.zig"); _ = @import("behavior/bugs/1607.zig"); _ = @import("behavior/bugs/1735.zig"); - _ = @import("behavior/bugs/1741.zig"); _ = @import("behavior/bugs/1851.zig"); _ = @import("behavior/bugs/1914.zig"); _ = @import("behavior/bugs/2006.zig"); diff --git a/test/behavior/align.zig b/test/behavior/align.zig index 00fbfc2cb5b6057f2862716d24da0011b999b4c8..217d0b7c15287eab5146fd344fa6518eb1aff609 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -15,6 +15,15 @@ test "global variable alignment" { } } +test "large alignment of local constant" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky + + const x: f32 align(128) = 12.34; + try std.testing.expect(@intFromPtr(&x) % 128 == 0); +} + test "slicing array of length 1 can not assume runtime index is always zero" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/bugs/1741.zig b/test/behavior/bugs/1741.zig deleted file mode 100644 index f4da8e5c2e495c5f61655f5914283d226ca89913..0000000000000000000000000000000000000000 --- a/test/behavior/bugs/1741.zig +++ /dev/null @@ -1,11 +0,0 @@ -const std = @import("std"); -const builtin = @import("builtin"); - -test "fixed" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // flaky - - const x: f32 align(128) = 12.34; - try std.testing.expect(@intFromPtr(&x) % 128 == 0); -} -- 2.54.0 From 5aa82ed477b85cf8681bc0dc65f97e813990a2ed Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Oct 2023 15:08:41 -0700 Subject: [PATCH 2/7] std.elf: remove some unneeded `@as` --- lib/std/elf.zig | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/std/elf.zig b/lib/std/elf.zig index 4c314fadc19436d84517a57af356e53e231b4dbd..4bb30b854df90d4d0437306c6b6810b19e66d8b3 100644 --- a/lib/std/elf.zig +++ b/lib/std/elf.zig @@ -782,10 +782,10 @@ pub const Elf32_Sym = extern struct { st_shndx: Elf32_Section, pub inline fn st_type(self: @This()) u4 { - return @as(u4, @truncate(self.st_info)); + return @truncate(self.st_info); } pub inline fn st_bind(self: @This()) u4 { - return @as(u4, @truncate(self.st_info >> 4)); + return @truncate(self.st_info >> 4); } }; pub const Elf64_Sym = extern struct { @@ -797,10 +797,10 @@ pub const Elf64_Sym = extern struct { st_size: Elf64_Xword, pub inline fn st_type(self: @This()) u4 { - return @as(u4, @truncate(self.st_info)); + return @truncate(self.st_info); } pub inline fn st_bind(self: @This()) u4 { - return @as(u4, @truncate(self.st_info >> 4)); + return @truncate(self.st_info >> 4); } }; pub const Elf32_Syminfo = extern struct { @@ -816,10 +816,10 @@ pub const Elf32_Rel = extern struct { r_info: Elf32_Word, pub inline fn r_sym(self: @This()) u24 { - return @as(u24, @truncate(self.r_info >> 8)); + return @truncate(self.r_info >> 8); } pub inline fn r_type(self: @This()) u8 { - return @as(u8, @truncate(self.r_info)); + return @truncate(self.r_info); } }; pub const Elf64_Rel = extern struct { @@ -827,10 +827,10 @@ pub const Elf64_Rel = extern struct { r_info: Elf64_Xword, pub inline fn r_sym(self: @This()) u32 { - return @as(u32, @truncate(self.r_info >> 32)); + return @truncate(self.r_info >> 32); } pub inline fn r_type(self: @This()) u32 { - return @as(u32, @truncate(self.r_info)); + return @truncate(self.r_info); } }; pub const Elf32_Rela = extern struct { @@ -839,10 +839,10 @@ pub const Elf32_Rela = extern struct { r_addend: Elf32_Sword, pub inline fn r_sym(self: @This()) u24 { - return @as(u24, @truncate(self.r_info >> 8)); + return @truncate(self.r_info >> 8); } pub inline fn r_type(self: @This()) u8 { - return @as(u8, @truncate(self.r_info)); + return @truncate(self.r_info); } }; pub const Elf64_Rela = extern struct { @@ -851,10 +851,10 @@ pub const Elf64_Rela = extern struct { r_addend: Elf64_Sxword, pub inline fn r_sym(self: @This()) u32 { - return @as(u32, @truncate(self.r_info >> 32)); + return @truncate(self.r_info >> 32); } pub inline fn r_type(self: @This()) u32 { - return @as(u32, @truncate(self.r_info)); + return @truncate(self.r_info); } }; pub const Elf32_Dyn = extern struct { -- 2.54.0 From 7bab406c790566781406a7968be22961ed7c305d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Oct 2023 15:10:35 -0700 Subject: [PATCH 3/7] InternPool: store alignment of anon decls Commit 5393e56500d499753dbc39704c0161b47d1e4d5c has a flaw pointed out by @mlugg: the `ty` field of pointer values changes when comptime values are pointer-casted. This commit introduces a new encoding which additionally stores the "original pointer type" which is used to store the alignment of the anonymous decl, and potentially other information in the future such as section and pointer address space. However, this new encoding is only used when the original pointer type differs from the casted pointer type in a meaningful way. I was able to make the LLVM backend and the C backend lower anonymous decls with the appropriate alignment, however I will need some help figuring out how to do this for the backends that lower anonymous decls via src/codegen.zig and the wasm backend. --- src/Compilation.zig | 1 + src/InternPool.zig | 100 +++++++++++++++++++++++++++++----- src/Sema.zig | 14 ++++- src/TypedValue.zig | 3 +- src/arch/wasm/CodeGen.zig | 17 ++++-- src/codegen.zig | 7 ++- src/codegen/c.zig | 21 ++++++- src/codegen/llvm.zig | 31 ++++++----- src/codegen/llvm/Builder.zig | 6 ++ src/codegen/llvm/bindings.zig | 3 + src/codegen/spirv.zig | 7 ++- src/link/C.zig | 19 ++++++- src/value.zig | 4 +- 13 files changed, 189 insertions(+), 44 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 800c8288d00cfb25afbc3796bab72576c97610ee..70d0e5ff82e15043299f77c84f4ba02f82263b00 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3545,6 +3545,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = .{}, .anon_decl_deps = .{}, + .aligned_anon_decls = .{}, }; defer { dg.ctypes.deinit(gpa); diff --git a/src/InternPool.zig b/src/InternPool.zig index 8cf121896d4a4ec20955681e29471a86ae7a8243..707e2d95b9461d83a0fff3ce8a5247512831a0c5 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -1074,7 +1074,7 @@ pub const Key = union(enum) { decl: Module.Decl.Index, mut_decl: MutDecl, - anon_decl: Index, + anon_decl: AnonDecl, comptime_field: Index, int: Index, eu_payload: Index, @@ -1090,6 +1090,14 @@ pub const Key = union(enum) { base: Index, index: u64, }; + pub const AnonDecl = extern struct { + val: Index, + /// Contains the canonical pointer type of the anonymous + /// declaration. This may equal `ty` of the `Ptr` or it may be + /// different. Importantly, when lowering the anonymous decl, + /// the original pointer type alignment must be used. + orig_ty: Index, + }; }; }; @@ -1231,7 +1239,8 @@ pub const Key = union(enum) { common ++ asBytes(&x.decl) ++ asBytes(&x.runtime_index), ), - .anon_decl, + .anon_decl => |x| Hash.hash(seed2, common ++ asBytes(&x)), + .int, .eu_payload, .opt_payload, @@ -1500,7 +1509,8 @@ pub const Key = union(enum) { return switch (a_info.addr) { .decl => |a_decl| a_decl == b_info.addr.decl, .mut_decl => |a_mut_decl| std.meta.eql(a_mut_decl, b_info.addr.mut_decl), - .anon_decl => |a_decl| a_decl == b_info.addr.anon_decl, + .anon_decl => |ad| ad.val == b_info.addr.anon_decl.val and + ad.orig_ty == b_info.addr.anon_decl.orig_ty, .int => |a_int| a_int == b_info.addr.int, .eu_payload => |a_eu_payload| a_eu_payload == b_info.addr.eu_payload, .opt_payload => |a_opt_payload| a_opt_payload == b_info.addr.opt_payload, @@ -2133,6 +2143,7 @@ pub const Index = enum(u32) { ptr_decl: struct { data: *PtrDecl }, ptr_mut_decl: struct { data: *PtrMutDecl }, ptr_anon_decl: struct { data: *PtrAnonDecl }, + ptr_anon_decl_aligned: struct { data: *PtrAnonDeclAligned }, ptr_comptime_field: struct { data: *PtrComptimeField }, ptr_int: struct { data: *PtrBase }, ptr_eu_payload: struct { data: *PtrBase }, @@ -2583,8 +2594,16 @@ pub const Tag = enum(u8) { /// data is extra index of `PtrMutDecl`, which contains the type and address. ptr_mut_decl, /// A pointer to an anonymous decl. - /// data is extra index of `PtrAnonDecl`, which contains the type and decl value. + /// data is extra index of `PtrAnonDecl`, which contains the pointer type and decl value. + /// The alignment of the anonymous decl is communicated via the pointer type. ptr_anon_decl, + /// A pointer to an anonymous decl. + /// data is extra index of `PtrAnonDeclAligned`, which contains the pointer + /// type and decl value. + /// The original pointer type is also provided, which will be different than `ty`. + /// This encoding is only used when a pointer to an anonymous decl is + /// coerced to a different pointer type with a different alignment. + ptr_anon_decl_aligned, /// data is extra index of `PtrComptimeField`, which contains the pointer type and field value. ptr_comptime_field, /// A pointer with an integer value. @@ -2781,6 +2800,7 @@ pub const Tag = enum(u8) { .ptr_decl => PtrDecl, .ptr_mut_decl => PtrMutDecl, .ptr_anon_decl => PtrAnonDecl, + .ptr_anon_decl_aligned => PtrAnonDeclAligned, .ptr_comptime_field => PtrComptimeField, .ptr_int => PtrBase, .ptr_eu_payload => PtrBase, @@ -3383,6 +3403,13 @@ pub const PtrAnonDecl = struct { val: Index, }; +pub const PtrAnonDeclAligned = struct { + ty: Index, + val: Index, + /// Must be nonequal to `ty`. Only the alignment from this value is important. + orig_ty: Index, +}; + pub const PtrMutDecl = struct { ty: Index, decl: Module.Decl.Index, @@ -3736,7 +3763,20 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { const info = ip.extraData(PtrAnonDecl, data); return .{ .ptr = .{ .ty = info.ty, - .addr = .{ .anon_decl = info.val }, + .addr = .{ .anon_decl = .{ + .val = info.val, + .orig_ty = info.ty, + } }, + } }; + }, + .ptr_anon_decl_aligned => { + const info = ip.extraData(PtrAnonDeclAligned, data); + return .{ .ptr = .{ + .ty = info.ty, + .addr = .{ .anon_decl = .{ + .val = info.val, + .orig_ty = info.orig_ty, + } }, } }; }, .ptr_comptime_field => { @@ -3817,7 +3857,17 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { } }; }, .ptr_anon_decl => .{ - .anon_decl = ip.extraData(PtrAnonDecl, ptr_item.data).val, + .anon_decl = .{ + .val = ip.extraData(PtrAnonDecl, ptr_item.data).val, + .orig_ty = info.ty, + }, + }, + .ptr_anon_decl_aligned => b: { + const sub_info = ip.extraData(PtrAnonDeclAligned, ptr_item.data); + break :b .{ .anon_decl = .{ + .val = sub_info.val, + .orig_ty = sub_info.orig_ty, + } }; }, .ptr_comptime_field => .{ .comptime_field = ip.extraData(PtrComptimeField, ptr_item.data).field_val, @@ -4571,13 +4621,22 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { .runtime_index = mut_decl.runtime_index, }), }), - .anon_decl => |anon_decl| ip.items.appendAssumeCapacity(.{ - .tag = .ptr_anon_decl, - .data = try ip.addExtra(gpa, PtrAnonDecl{ - .ty = ptr.ty, - .val = anon_decl, - }), - }), + .anon_decl => |anon_decl| ip.items.appendAssumeCapacity( + if (ptrsHaveSameAlignment(ip, ptr.ty, ptr_type, anon_decl.orig_ty)) .{ + .tag = .ptr_anon_decl, + .data = try ip.addExtra(gpa, PtrAnonDecl{ + .ty = ptr.ty, + .val = anon_decl.val, + }), + } else .{ + .tag = .ptr_anon_decl_aligned, + .data = try ip.addExtra(gpa, PtrAnonDeclAligned{ + .ty = ptr.ty, + .val = anon_decl.val, + .orig_ty = anon_decl.orig_ty, + }), + }, + ), .comptime_field => |field_val| { assert(field_val != .none); ip.items.appendAssumeCapacity(.{ @@ -7184,6 +7243,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { .ptr_decl => @sizeOf(PtrDecl), .ptr_mut_decl => @sizeOf(PtrMutDecl), .ptr_anon_decl => @sizeOf(PtrAnonDecl), + .ptr_anon_decl_aligned => @sizeOf(PtrAnonDeclAligned), .ptr_comptime_field => @sizeOf(PtrComptimeField), .ptr_int => @sizeOf(PtrBase), .ptr_eu_payload => @sizeOf(PtrBase), @@ -7314,6 +7374,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void { .ptr_decl, .ptr_mut_decl, .ptr_anon_decl, + .ptr_anon_decl_aligned, .ptr_comptime_field, .ptr_int, .ptr_eu_payload, @@ -7695,6 +7756,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index { inline .ptr_decl, .ptr_mut_decl, .ptr_anon_decl, + .ptr_anon_decl_aligned, .ptr_comptime_field, .ptr_int, .ptr_eu_payload, @@ -7855,7 +7917,7 @@ pub fn getBackingAddrTag(ip: *const InternPool, val: Index) ?Key.Ptr.Addr.Tag { switch (ip.items.items(.tag)[base]) { .ptr_decl => return .decl, .ptr_mut_decl => return .mut_decl, - .ptr_anon_decl => return .anon_decl, + .ptr_anon_decl, .ptr_anon_decl_aligned => return .anon_decl, .ptr_comptime_field => return .comptime_field, .ptr_int => return .int, inline .ptr_eu_payload, @@ -8032,6 +8094,7 @@ pub fn zigTypeTagOrPoison(ip: *const InternPool, index: Index) error{GenericPois .ptr_decl, .ptr_mut_decl, .ptr_anon_decl, + .ptr_anon_decl_aligned, .ptr_comptime_field, .ptr_int, .ptr_eu_payload, @@ -8281,3 +8344,12 @@ pub fn addFieldName( ip.extra.items[names_start + field_index] = @intFromEnum(name); return null; } + +/// Used only by `get` for pointer values, and mainly intended to use `Tag.ptr_anon_decl` +/// encoding instead of `Tag.ptr_anon_decl_aligned` when possible. +fn ptrsHaveSameAlignment(ip: *InternPool, a_ty: Index, a_info: Key.PtrType, b_ty: Index) bool { + if (a_ty == b_ty) return true; + const b_info = ip.indexToKey(b_ty).ptr_type; + return a_info.flags.alignment == b_info.flags.alignment and + (a_info.child == b_info.child or a_info.flags.alignment != .none); +} diff --git a/src/Sema.zig b/src/Sema.zig index 3e8eeeb88681e7fa0ba62600ccb5f6a4ce8b5349..54e744e6e7ce9b67c8012aab9d64e46006e06eba 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3659,7 +3659,10 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro if (try sema.resolveComptimeKnownAllocValue(block, alloc, null)) |val| { const new_mut_ptr = Air.internedToRef((try mod.intern(.{ .ptr = .{ .ty = alloc_ty.toIntern(), - .addr = .{ .anon_decl = val }, + .addr = .{ .anon_decl = .{ + .val = val, + .orig_ty = alloc_ty.toIntern(), + } }, } }))); return sema.makePtrConst(block, new_mut_ptr); } @@ -5540,7 +5543,10 @@ fn addStrLitNoAlias(sema: *Sema, bytes: []const u8) CompileError!Air.Inst.Ref { }); return Air.internedToRef((try mod.intern(.{ .ptr = .{ .ty = ptr_ty.toIntern(), - .addr = .{ .anon_decl = val }, + .addr = .{ .anon_decl = .{ + .val = val, + .orig_ty = ptr_ty.toIntern(), + } }, } }))); } @@ -30546,7 +30552,8 @@ fn beginComptimePtrLoad( .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null, }; }, - .anon_decl => |decl_val| blk: { + .anon_decl => |anon_decl| blk: { + const decl_val = anon_decl.val; if (decl_val.toValue().getVariable(mod) != null) return error.RuntimeLoad; const decl_ty = ip.typeOf(decl_val).toType(); const decl_tv: TypedValue = .{ .ty = decl_ty, .val = decl_val.toValue() }; @@ -36650,6 +36657,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value { .simple_value, .ptr_decl, .ptr_anon_decl, + .ptr_anon_decl_aligned, .ptr_mut_decl, .ptr_comptime_field, .ptr_int, diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 3d77d6b8a06b48e84300e95888425309bb960021..345535a06ebf4fd725e97e5ef6bb7ed717cf8f00 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -321,7 +321,8 @@ pub fn print( .val = decl.val, }, writer, level - 1, mod); }, - .anon_decl => |decl_val| { + .anon_decl => |anon_decl| { + const decl_val = anon_decl.val; if (level == 0) return writer.print("(anon decl '{d}')", .{ @intFromEnum(decl_val), }); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 3eac273164e17c8481e11a96926a655415ea139c..639af3d8c482d4c562168f1b5f1b51a2e33c9985 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3139,16 +3139,25 @@ fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: Module.Decl.In return func.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl_index, offset); } -fn lowerAnonDeclRef(func: *CodeGen, anon_decl: InternPool.Index, offset: u32) InnerError!WValue { +fn lowerAnonDeclRef( + func: *CodeGen, + anon_decl: InternPool.Key.Ptr.Addr.AnonDecl, + offset: u32, +) InnerError!WValue { const mod = func.bin_file.base.options.module.?; - const ty = mod.intern_pool.typeOf(anon_decl).toType(); + const decl_val = anon_decl.val; + const ty = mod.intern_pool.typeOf(decl_val).toType(); const is_fn_body = ty.zigTypeTag(mod) == .Fn; if (!is_fn_body and !ty.hasRuntimeBitsIgnoreComptime(mod)) { return WValue{ .imm32 = 0xaaaaaaaa }; } - const res = try func.bin_file.lowerAnonDecl(anon_decl, func.decl.srcLoc(mod)); + const alignment = mod.intern_pool.indexToKey(anon_decl.orig_ty).ptr_type.flags.alignment; + if (alignment != .none) { + @panic("TODO how to make this anon decl be aligned?"); + } + const res = try func.bin_file.lowerAnonDecl(decl_val, func.decl.srcLoc(mod)); switch (res) { .ok => {}, .fail => |em| { @@ -3156,7 +3165,7 @@ fn lowerAnonDeclRef(func: *CodeGen, anon_decl: InternPool.Index, offset: u32) In return error.CodegenFail; }, } - const target_atom_index = func.bin_file.anon_decls.get(anon_decl).?; + const target_atom_index = func.bin_file.anon_decls.get(decl_val).?; const target_sym_index = func.bin_file.getAtom(target_atom_index).getSymbolIndex().?; if (is_fn_body) { return WValue{ .function_index = target_sym_index }; diff --git a/src/codegen.zig b/src/codegen.zig index 24269f38baef8416958849b04466b3a81b5c90a6..d7a5666986e9d555e514e651f7688018f9dfd1f3 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -713,7 +713,7 @@ const RelocInfo = struct { fn lowerAnonDeclRef( bin_file: *link.File, src_loc: Module.SrcLoc, - decl_val: InternPool.Index, + anon_decl: InternPool.Key.Ptr.Addr.AnonDecl, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, reloc_info: RelocInfo, @@ -723,6 +723,7 @@ fn lowerAnonDeclRef( const mod = bin_file.options.module.?; const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); + const decl_val = anon_decl.val; const decl_ty = mod.intern_pool.typeOf(decl_val).toType(); const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn; if (!is_fn_body and !decl_ty.hasRuntimeBits(mod)) { @@ -736,6 +737,10 @@ fn lowerAnonDeclRef( .fail => |em| return .{ .fail = em }, } + const alignment = mod.intern_pool.indexToKey(anon_decl.orig_ty).ptr_type.flags.alignment; + if (alignment != .none) { + @panic("TODO how to make this anon decl be aligned?"); + } const vaddr = try bin_file.getAnonDeclVAddr(decl_val, .{ .parent_atom_index = reloc_info.parent_atom_index, .offset = code.items.len, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 94598985c0601d0566a2d0e81c7e64cacb04e370..897d0ea047f1bc0cb3141377362cc7b257748b09 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -531,6 +531,7 @@ pub const DeclGen = struct { /// Keeps track of anonymous decls that need to be rendered before this /// (named) Decl in the output C code. anon_decl_deps: std.AutoArrayHashMapUnmanaged(InternPool.Index, C.DeclBlock), + aligned_anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment), fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { @setCold(true); @@ -548,11 +549,12 @@ pub const DeclGen = struct { writer: anytype, ty: Type, ptr_val: Value, - decl_val: InternPool.Index, + anon_decl: InternPool.Key.Ptr.Addr.AnonDecl, location: ValueRenderLocation, ) error{ OutOfMemory, AnalysisFail }!void { const mod = dg.module; const ip = &mod.intern_pool; + const decl_val = anon_decl.val; const decl_ty = ip.typeOf(decl_val).toType(); // Render an undefined pointer if we have a pointer to a zero-bit or comptime type. @@ -592,8 +594,23 @@ pub const DeclGen = struct { // Indicate that the anon decl should be rendered to the output so that // our reference above is not undefined. + const ptr_type = ip.indexToKey(anon_decl.orig_ty).ptr_type; const gop = try dg.anon_decl_deps.getOrPut(dg.gpa, decl_val); if (!gop.found_existing) gop.value_ptr.* = .{}; + + // Only insert an alignment entry if the alignment is greater than ABI + // alignment. If there is already an entry, keep the greater alignment. + const explicit_alignment = ptr_type.flags.alignment; + if (explicit_alignment != .none) { + const abi_alignment = ptr_type.child.toType().abiAlignment(mod); + if (explicit_alignment.compareStrict(.gt, abi_alignment)) { + const aligned_gop = try dg.aligned_anon_decls.getOrPut(dg.gpa, decl_val); + aligned_gop.value_ptr.* = if (aligned_gop.found_existing) + aligned_gop.value_ptr.maxStrict(explicit_alignment) + else + explicit_alignment; + } + } } fn renderDeclValue( @@ -651,7 +668,7 @@ pub const DeclGen = struct { switch (ptr.addr) { .decl => |d| try dg.renderDeclValue(writer, ptr_ty, ptr_val.toValue(), d, location), .mut_decl => |md| try dg.renderDeclValue(writer, ptr_ty, ptr_val.toValue(), md.decl, location), - .anon_decl => |decl_val| try dg.renderAnonDeclValue(writer, ptr_ty, ptr_val.toValue(), decl_val, location), + .anon_decl => |anon_decl| try dg.renderAnonDeclValue(writer, ptr_ty, ptr_val.toValue(), anon_decl, location), .int => |int| { try writer.writeByte('('); try dg.renderCType(writer, ptr_cty); diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 9bb012deb1ef8585127ca0e164827df3866b02e4..ed4f1bdf6c3bc153835d2f6bb1bb4c12ca5bd0ca 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3051,9 +3051,17 @@ pub const Object = struct { llvm_addr_space: Builder.AddrSpace, alignment: InternPool.Alignment, ) Error!Builder.Variable.Index { + assert(alignment != .none); // TODO: Add address space to the anon_decl_map const gop = try o.anon_decl_map.getOrPut(o.gpa, decl_val); - if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.variable; + if (gop.found_existing) { + // Keep the greater of the two alignments. + const variable_index = gop.value_ptr.ptr(&o.builder).kind.variable; + const old_alignment = InternPool.Alignment.fromLlvm(variable_index.getAlignment(&o.builder)); + const max_alignment = old_alignment.maxStrict(alignment); + variable_index.setAlignment(max_alignment.toLlvm(), &o.builder); + return variable_index; + } errdefer assert(o.anon_decl_map.remove(decl_val)); const mod = o.module; @@ -3069,8 +3077,7 @@ pub const Object = struct { try variable_index.setInitializer(try o.lowerValue(decl_val), &o.builder); variable_index.setLinkage(.internal, &o.builder); variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); - if (alignment != .none) - variable_index.setAlignment(alignment.toLlvm(), &o.builder); + variable_index.setAlignment(alignment.toLlvm(), &o.builder); return variable_index; } @@ -4253,13 +4260,6 @@ pub const Object = struct { return o.builder.bigIntConst(try o.builder.intType(ty.intInfo(mod).bits), bigint); } - fn lowerParentPtrAnonDecl(o: *Object, decl_val: InternPool.Index) Error!Builder.Constant { - const mod = o.module; - const decl_ty = mod.intern_pool.typeOf(decl_val).toType(); - const ptr_ty = try mod.singleMutPtrType(decl_ty); - return o.lowerAnonDeclRef(ptr_ty, decl_val); - } - fn lowerParentPtrDecl(o: *Object, decl_index: Module.Decl.Index) Allocator.Error!Builder.Constant { const mod = o.module; const decl = mod.declPtr(decl_index); @@ -4275,7 +4275,7 @@ pub const Object = struct { return switch (ptr.addr) { .decl => |decl| try o.lowerParentPtrDecl(decl), .mut_decl => |mut_decl| try o.lowerParentPtrDecl(mut_decl.decl), - .anon_decl => |anon_decl| try o.lowerParentPtrAnonDecl(anon_decl), + .anon_decl => |ad| try o.lowerAnonDeclRef(ad.orig_ty.toType(), ad), .int => |int| try o.lowerIntAsPtr(int), .eu_payload => |eu_ptr| { const parent_ptr = try o.lowerParentPtr(eu_ptr.toValue()); @@ -4394,10 +4394,11 @@ pub const Object = struct { fn lowerAnonDeclRef( o: *Object, ptr_ty: Type, - decl_val: InternPool.Index, + anon_decl: InternPool.Key.Ptr.Addr.AnonDecl, ) Error!Builder.Constant { const mod = o.module; const ip = &mod.intern_pool; + const decl_val = anon_decl.val; const decl_ty = ip.typeOf(decl_val).toType(); const target = mod.getTarget(); @@ -4416,9 +4417,9 @@ pub const Object = struct { if (is_fn_body) @panic("TODO"); - const addr_space = target_util.defaultAddressSpace(target, .global_constant); - const llvm_addr_space = toLlvmAddressSpace(addr_space, target); - const alignment = ptr_ty.ptrAlignment(mod); + const orig_ty = anon_decl.orig_ty.toType(); + const llvm_addr_space = toLlvmAddressSpace(orig_ty.ptrAddressSpace(mod), target); + const alignment = orig_ty.ptrAlignment(mod); const llvm_global = (try o.resolveGlobalAnonDecl(decl_val, llvm_addr_space, alignment)).ptrConst(&o.builder).global; const llvm_val = try o.builder.convConst( diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig index 5a9703af3d294bc8acccc61ac9d7b2b6c2f18a4c..4f971db1de14e64378553cc8302c08ea799c822a 100644 --- a/src/codegen/llvm/Builder.zig +++ b/src/codegen/llvm/Builder.zig @@ -2477,6 +2477,12 @@ pub const Variable = struct { self.ptr(builder).alignment = alignment; } + pub fn getAlignment(self: Index, builder: *Builder) Alignment { + if (builder.useLibLlvm()) + return Alignment.fromByteUnits(self.toLlvm(builder).getAlignment()); + return self.ptr(builder).alignment; + } + pub fn toLlvm(self: Index, builder: *const Builder) *llvm.Value { return self.ptrConst(builder).global.toLlvm(builder); } diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index bb68e28bf23f129cfcc9f7af91fcc4acc0cda199..d7144b36ce0eaa546d0ff45d57e5cb1894ac4532 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -273,6 +273,9 @@ pub const Value = opaque { pub const setAlignment = LLVMSetAlignment; extern fn LLVMSetAlignment(V: *Value, Bytes: c_uint) void; + pub const getAlignment = LLVMGetAlignment; + extern fn LLVMGetAlignment(V: *Value) c_uint; + pub const setFunctionCallConv = LLVMSetFunctionCallConv; extern fn LLVMSetFunctionCallConv(Fn: *Value, CC: CallConv) void; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index ded73d6afd68b98faba094e4fa006b60d7d69e89..e7e6ebdef340bd94d162b7fb3954ad7ecbc6915d 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -959,12 +959,17 @@ const DeclGen = struct { } } - fn constantAnonDeclRef(self: *DeclGen, ty: Type, decl_val: InternPool.Index) !IdRef { + fn constantAnonDeclRef( + self: *DeclGen, + ty: Type, + anon_decl: InternPool.Key.Ptr.Addr.AnonDecl, + ) !IdRef { // TODO: Merge this function with constantDeclRef. const mod = self.module; const ip = &mod.intern_pool; const ty_ref = try self.resolveType(ty, .direct); + const decl_val = anon_decl.val; const decl_ty = ip.typeOf(decl_val).toType(); if (decl_val.toValue().getFunction(mod)) |func| { diff --git a/src/link/C.zig b/src/link/C.zig index 7a8cc47f71d0517c63588c8df50588f00499baff..1d5672cf11c90a2c8e355a40bcc2c6114f9caa45 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -7,6 +7,7 @@ const fs = std.fs; const C = @This(); const Module = @import("../Module.zig"); const InternPool = @import("../InternPool.zig"); +const Alignment = InternPool.Alignment; const Compilation = @import("../Compilation.zig"); const codegen = @import("../codegen/c.zig"); const link = @import("../link.zig"); @@ -30,6 +31,10 @@ string_bytes: std.ArrayListUnmanaged(u8) = .{}, /// Tracks all the anonymous decls that are used by all the decls so they can /// be rendered during flush(). anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, DeclBlock) = .{}, +/// Sparse set of anon decls that are overaligned. Underaligned anon decls are +/// lowered the same as ABI-aligned anon decls. The keys here are a subset of +/// the keys of `anon_decls`. +aligned_anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment) = .{}, /// Optimization, `updateDecl` reuses this buffer rather than creating a new /// one with every call. @@ -125,6 +130,7 @@ pub fn deinit(self: *C) void { db.deinit(gpa); } self.anon_decls.deinit(gpa); + self.aligned_anon_decls.deinit(gpa); self.string_bytes.deinit(gpa); self.fwd_decl_buf.deinit(gpa); @@ -179,6 +185,7 @@ pub fn updateFunc( .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, .anon_decl_deps = self.anon_decls, + .aligned_anon_decls = self.aligned_anon_decls, }, .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code @@ -189,6 +196,7 @@ pub fn updateFunc( function.object.indent_writer = .{ .underlying_writer = function.object.code.writer() }; defer { self.anon_decls = function.object.dg.anon_decl_deps; + self.aligned_anon_decls = function.object.dg.aligned_anon_decls; fwd_decl.* = function.object.dg.fwd_decl.moveToUnmanaged(); code.* = function.object.code.moveToUnmanaged(); function.deinit(); @@ -232,6 +240,7 @@ fn updateAnonDecl(self: *C, module: *Module, i: usize) !void { .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = .{}, .anon_decl_deps = self.anon_decls, + .aligned_anon_decls = self.aligned_anon_decls, }, .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code @@ -240,6 +249,7 @@ fn updateAnonDecl(self: *C, module: *Module, i: usize) !void { defer { self.anon_decls = object.dg.anon_decl_deps; + self.aligned_anon_decls = object.dg.aligned_anon_decls; object.dg.ctypes.deinit(object.dg.gpa); fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); code.* = object.code.moveToUnmanaged(); @@ -250,7 +260,8 @@ fn updateAnonDecl(self: *C, module: *Module, i: usize) !void { .val = anon_decl.toValue(), }; const c_value: codegen.CValue = .{ .constant = anon_decl }; - codegen.genDeclValue(&object, tv, false, c_value, .none, .none) catch |err| switch (err) { + const alignment: Alignment = self.aligned_anon_decls.get(anon_decl) orelse .none; + codegen.genDeclValue(&object, tv, false, c_value, alignment, .none) catch |err| switch (err) { error.AnalysisFail => { @panic("TODO: C backend AnalysisFail on anonymous decl"); //try module.failed_decls.put(gpa, decl_index, object.dg.error_msg.?); @@ -296,6 +307,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, .anon_decl_deps = self.anon_decls, + .aligned_anon_decls = self.aligned_anon_decls, }, .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code @@ -303,6 +315,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi object.indent_writer = .{ .underlying_writer = object.code.writer() }; defer { self.anon_decls = object.dg.anon_decl_deps; + self.aligned_anon_decls = object.dg.aligned_anon_decls; object.dg.ctypes.deinit(object.dg.gpa); fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); code.* = object.code.moveToUnmanaged(); @@ -602,6 +615,7 @@ fn flushErrDecls(self: *C, ctypes: *codegen.CType.Store) FlushDeclError!void { .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, .anon_decl_deps = self.anon_decls, + .aligned_anon_decls = self.aligned_anon_decls, }, .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code @@ -609,6 +623,7 @@ fn flushErrDecls(self: *C, ctypes: *codegen.CType.Store) FlushDeclError!void { object.indent_writer = .{ .underlying_writer = object.code.writer() }; defer { self.anon_decls = object.dg.anon_decl_deps; + self.aligned_anon_decls = object.dg.aligned_anon_decls; object.dg.ctypes.deinit(gpa); fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); code.* = object.code.moveToUnmanaged(); @@ -642,6 +657,7 @@ fn flushLazyFn( .fwd_decl = fwd_decl.toManaged(gpa), .ctypes = ctypes.*, .anon_decl_deps = .{}, + .aligned_anon_decls = .{}, }, .code = code.toManaged(gpa), .indent_writer = undefined, // set later so we can get a pointer to object.code @@ -651,6 +667,7 @@ fn flushLazyFn( // If this assert trips just handle the anon_decl_deps the same as // `updateFunc()` does. assert(object.dg.anon_decl_deps.count() == 0); + assert(object.dg.aligned_anon_decls.count() == 0); object.dg.ctypes.deinit(gpa); fwd_decl.* = object.dg.fwd_decl.moveToUnmanaged(); code.* = object.code.moveToUnmanaged(); diff --git a/src/value.zig b/src/value.zig index 2c4a88da17d145b6493ada0edfa04d9aff739b62..c7560cb419bd9f33b59a541e1713fac211e6af98 100644 --- a/src/value.zig +++ b/src/value.zig @@ -1571,7 +1571,7 @@ pub const Value = struct { .none => switch (ip.indexToKey(switch (ptr.addr) { .decl => |decl| mod.declPtr(decl).ty.toIntern(), .mut_decl => |mut_decl| mod.declPtr(mut_decl.decl).ty.toIntern(), - .anon_decl => |anon_decl| ip.typeOf(anon_decl), + .anon_decl => |anon_decl| ip.typeOf(anon_decl.val), .comptime_field => |comptime_field| ip.typeOf(comptime_field), else => unreachable, })) { @@ -1604,7 +1604,7 @@ pub const Value = struct { })).toValue(), .ptr => |ptr| switch (ptr.addr) { .decl => |decl| mod.declPtr(decl).val.maybeElemValue(mod, index), - .anon_decl => |anon_decl| anon_decl.toValue().maybeElemValue(mod, index), + .anon_decl => |anon_decl| anon_decl.val.toValue().maybeElemValue(mod, index), .mut_decl => |mut_decl| (try mod.declPtr(mut_decl.decl).internValue(mod)) .toValue().maybeElemValue(mod, index), .int, .eu_payload => null, -- 2.54.0 From 5e1db5e47800a8e9bb1d8d11f07b0023bd8e49ef Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 19 Oct 2023 16:49:34 -0700 Subject: [PATCH 4/7] Sema: migrate zirResolveInferredAlloc to new anon decl mechanism This required a bug fix in zirMakePtrConst. --- src/Sema.zig | 47 ++++++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 54e744e6e7ce9b67c8012aab9d64e46006e06eba..450b75363782ce83c277a987d4c2cfda7f517bef 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3672,10 +3672,18 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro implicit_ct: { const ptr_val = try sema.resolveMaybeUndefVal(alloc) orelse break :implicit_ct; if (!ptr_val.isComptimeMutablePtr(mod)) { - // It could still be a constant pointer to a decl - const decl_index = ptr_val.pointerDecl(mod) orelse break :implicit_ct; - const decl_val = mod.declPtr(decl_index).val.toIntern(); - if (mod.intern_pool.isRuntimeValue(decl_val)) break :implicit_ct; + // It could still be a constant pointer to a decl. + switch (mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr) { + .anon_decl => |anon_decl| { + if (mod.intern_pool.isVariable(anon_decl.val)) + break :implicit_ct; + }, + else => { + const decl_index = ptr_val.pointerDecl(mod) orelse break :implicit_ct; + const decl_val = mod.declPtr(decl_index).val.toIntern(); + if (mod.intern_pool.isRuntimeValue(decl_val)) break :implicit_ct; + }, + } } return sema.makePtrConst(block, alloc); } @@ -3915,17 +3923,19 @@ fn finishResolveComptimeKnownAllocValue(sema: *Sema, result_val: InternPool.Inde return result_val; } -fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { - const mod = sema.mod; - const alloc_ty = sema.typeOf(alloc); - - var ptr_info = alloc_ty.ptrInfo(mod); +fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type { + var ptr_info = ptr_ty.ptrInfo(sema.mod); ptr_info.flags.is_const = true; - const const_ptr_ty = try sema.ptrType(ptr_info); + return sema.ptrType(ptr_info); +} + +fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { + const alloc_ty = sema.typeOf(alloc); + const const_ptr_ty = try sema.makePtrTyConst(alloc_ty); // Detect if a comptime value simply needs to have its type changed. if (try sema.resolveMaybeUndefVal(alloc)) |val| { - return Air.internedToRef((try mod.getCoerced(val, const_ptr_ty)).toIntern()); + return Air.internedToRef((try sema.mod.getCoerced(val, const_ptr_ty)).toIntern()); } return block.addBitCast(const_ptr_ty, alloc); @@ -4039,6 +4049,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com defer tracy.end(); const mod = sema.mod; + const gpa = sema.gpa; const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; @@ -4104,11 +4115,14 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com if (!ia1.is_const) { try sema.validateVarType(block, ty_src, final_elem_ty, false); } else if (try sema.resolveComptimeKnownAllocValue(block, ptr, final_ptr_ty)) |val| { - var anon_decl = try block.startAnonDecl(); - defer anon_decl.deinit(); - const new_decl_index = try anon_decl.finish(final_elem_ty, val.toValue(), ia1.alignment); - const new_mut_ptr = Air.refToInterned(try sema.analyzeDeclRef(new_decl_index)).?.toValue(); - const new_const_ptr = (try mod.getCoerced(new_mut_ptr, final_ptr_ty)).toIntern(); + const const_ptr_ty = (try sema.makePtrTyConst(final_ptr_ty)).toIntern(); + const new_const_ptr = try mod.intern(.{ .ptr = .{ + .ty = const_ptr_ty, + .addr = .{ .anon_decl = .{ + .val = val, + .orig_ty = const_ptr_ty, + } }, + } }); // Remap the ZIR oeprand to the resolved pointer value sema.inst_map.putAssumeCapacity(Zir.refToIndex(inst_data.operand).?, Air.internedToRef(new_const_ptr)); @@ -4131,7 +4145,6 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com // Now we need to go back over all the store instructions, and do the logic as if // the new result ptr type was available. - const gpa = sema.gpa; for (ia2.prongs.items) |placeholder_inst| { var replacement_block = block.makeSubBlock(); -- 2.54.0 From dd402f6d83071535fffac055fabe72ebcb0db994 Mon Sep 17 00:00:00 2001 From: mlugg Date: Thu, 19 Oct 2023 16:56:20 -0700 Subject: [PATCH 5/7] AstGen: omit make_ptr_const for resolve_inferred_alloc After the previous commit, these make_ptr_const ZIR instructions are redundant. --- src/AstGen.zig | 10 ++++++---- src/Zir.zig | 5 +++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index de512e5bdbd714756d9e8436390a7155872942e6..cbfe1a3adbe07856f592655ae28988d5275b4d0d 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -3123,10 +3123,10 @@ fn varDecl( if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node)) _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst }); - if (resolve_inferred_alloc != .none) { + const const_ptr = if (resolve_inferred_alloc != .none) p: { _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node); - } - const const_ptr = try gz.addUnNode(.make_ptr_const, var_ptr, node); + break :p var_ptr; + } else try gz.addUnNode(.make_ptr_const, var_ptr, node); try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr); @@ -3533,7 +3533,9 @@ fn assignDestructureMaybeDecls( else => unreachable, }; // If the alloc was const, make it const. - const var_ptr = if (is_const) make_const: { + const var_ptr = if (is_const and full.ast.type_node != 0) make_const: { + // Note that we don't do this if type_node == 0 since `resolve_inferred_alloc` + // handles it for us. break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node); } else raw_ptr; const name_token = full.ast.mut_token + 1; diff --git a/src/Zir.zig b/src/Zir.zig index 973177c1b009001f38feda920809f4b09aee8cc4..c72228a42fd3e148edfde55dcf13d72b2840e43b 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -997,8 +997,9 @@ pub const Inst = struct { /// is the allocation that needs to have its type inferred. /// Uses the `un_node` field. The AST node is the var decl. resolve_inferred_alloc, - /// Turns a pointer coming from an `alloc`, `alloc_inferred`, `alloc_inferred_comptime` or - /// `Extended.alloc` into a constant version of the same pointer. + /// Turns a pointer coming from an `alloc` or `Extended.alloc` into a constant + /// version of the same pointer. For inferred allocations this is instead implicitly + /// handled by the `resolve_inferred_alloc` instruction. /// Uses the `un_node` union field. make_ptr_const, -- 2.54.0 From c4fcf0e22a0a0bab19ac9d1335b10decbfb2f137 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Thu, 19 Oct 2023 21:23:09 -0400 Subject: [PATCH 6/7] codegen: implement lowering aligned anon decls --- src/arch/wasm/CodeGen.zig | 7 ++----- src/codegen.zig | 7 ++----- src/link.zig | 10 +++++----- src/link/Coff.zig | 28 ++++++++++++++++++---------- src/link/Elf.zig | 21 +++++++++++++-------- src/link/MachO.zig | 22 +++++++++++++--------- src/link/Wasm.zig | 35 ++++++++++++++++++++--------------- 7 files changed, 73 insertions(+), 57 deletions(-) diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 639af3d8c482d4c562168f1b5f1b51a2e33c9985..83ab118ac5c021932d665eb4e8a43756a1f235a6 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -3153,11 +3153,8 @@ fn lowerAnonDeclRef( return WValue{ .imm32 = 0xaaaaaaaa }; } - const alignment = mod.intern_pool.indexToKey(anon_decl.orig_ty).ptr_type.flags.alignment; - if (alignment != .none) { - @panic("TODO how to make this anon decl be aligned?"); - } - const res = try func.bin_file.lowerAnonDecl(decl_val, func.decl.srcLoc(mod)); + const decl_align = mod.intern_pool.indexToKey(anon_decl.orig_ty).ptr_type.flags.alignment; + const res = try func.bin_file.lowerAnonDecl(decl_val, decl_align, func.decl.srcLoc(mod)); switch (res) { .ok => {}, .fail => |em| { diff --git a/src/codegen.zig b/src/codegen.zig index d7a5666986e9d555e514e651f7688018f9dfd1f3..fe948d18bdf9697b677be9c2844a5c60d2c14aa4 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -731,16 +731,13 @@ fn lowerAnonDeclRef( return Result.ok; } - const res = try bin_file.lowerAnonDecl(decl_val, src_loc); + const decl_align = mod.intern_pool.indexToKey(anon_decl.orig_ty).ptr_type.flags.alignment; + const res = try bin_file.lowerAnonDecl(decl_val, decl_align, src_loc); switch (res) { .ok => {}, .fail => |em| return .{ .fail = em }, } - const alignment = mod.intern_pool.indexToKey(anon_decl.orig_ty).ptr_type.flags.alignment; - if (alignment != .none) { - @panic("TODO how to make this anon decl be aligned?"); - } const vaddr = try bin_file.getAnonDeclVAddr(decl_val, .{ .parent_atom_index = reloc_info.parent_atom_index, .offset = code.items.len, diff --git a/src/link.zig b/src/link.zig index 9c79de290df478ffbc415958929c4239d7375df3..6e5c809f6216026562887d7261c8310a5073d454 100644 --- a/src/link.zig +++ b/src/link.zig @@ -940,15 +940,15 @@ pub const File = struct { pub const LowerResult = @import("codegen.zig").Result; - pub fn lowerAnonDecl(base: *File, decl_val: InternPool.Index, src_loc: Module.SrcLoc) !LowerResult { + pub fn lowerAnonDecl(base: *File, decl_val: InternPool.Index, decl_align: InternPool.Alignment, src_loc: Module.SrcLoc) !LowerResult { if (build_options.only_c) unreachable; switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).lowerAnonDecl(decl_val, src_loc), - .elf => return @fieldParentPtr(Elf, "base", base).lowerAnonDecl(decl_val, src_loc), - .macho => return @fieldParentPtr(MachO, "base", base).lowerAnonDecl(decl_val, src_loc), + .coff => return @fieldParentPtr(Coff, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), + .elf => return @fieldParentPtr(Elf, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), + .macho => return @fieldParentPtr(MachO, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerAnonDecl(decl_val, src_loc), .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).lowerAnonDecl(decl_val, src_loc), + .wasm => return @fieldParentPtr(Wasm, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), .spirv => unreachable, .nvptx => unreachable, } diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 94b3c637417496f9379b6091547b3e2761c7cf31..35f0b8441130805dcc9743e9e0bcaab4ce47e44e 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1091,7 +1091,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In const index = unnamed_consts.items.len; const sym_name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); defer gpa.free(sym_name); - const atom_index = switch (try self.lowerConst(sym_name, tv, self.rdata_section_index.?, decl.srcLoc(mod))) { + const atom_index = switch (try self.lowerConst(sym_name, tv, tv.ty.abiAlignment(mod), self.rdata_section_index.?, decl.srcLoc(mod))) { .ok => |atom_index| atom_index, .fail => |em| { decl.analysis = .codegen_failure; @@ -1109,13 +1109,12 @@ const LowerConstResult = union(enum) { fail: *Module.ErrorMsg, }; -fn lowerConst(self: *Coff, name: []const u8, tv: TypedValue, sect_id: u16, src_loc: Module.SrcLoc) !LowerConstResult { +fn lowerConst(self: *Coff, name: []const u8, tv: TypedValue, required_alignment: InternPool.Alignment, sect_id: u16, src_loc: Module.SrcLoc) !LowerConstResult { const gpa = self.base.allocator; var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - const mod = self.base.options.module.?; const atom_index = try self.createAtom(); const sym = self.getAtom(atom_index).getSymbolPtr(self); try self.setSymbolName(sym, name); @@ -1129,10 +1128,13 @@ fn lowerConst(self: *Coff, name: []const u8, tv: TypedValue, sect_id: u16, src_l .fail => |em| return .{ .fail = em }, }; - const required_alignment: u32 = @intCast(tv.ty.abiAlignment(mod).toByteUnits(0)); const atom = self.getAtomPtr(atom_index); atom.size = @as(u32, @intCast(code.len)); - atom.getSymbolPtr(self).value = try self.allocateAtom(atom_index, atom.size, required_alignment); + atom.getSymbolPtr(self).value = try self.allocateAtom( + atom_index, + atom.size, + @intCast(required_alignment.toByteUnitsOptional().?), + ); errdefer self.freeAtom(atom_index); log.debug("allocated atom for {s} at 0x{x}", .{ name, atom.getSymbol(self).value }); @@ -1736,7 +1738,7 @@ pub fn getDeclVAddr(self: *Coff, decl_index: Module.Decl.Index, reloc_info: link return 0; } -pub fn lowerAnonDecl(self: *Coff, decl_val: InternPool.Index, src_loc: Module.SrcLoc) !codegen.Result { +pub fn lowerAnonDecl(self: *Coff, decl_val: InternPool.Index, decl_align: InternPool.Alignment, src_loc: Module.SrcLoc) !codegen.Result { // This is basically the same as lowerUnnamedConst. // example: // const ty = mod.intern_pool.typeOf(decl_val).toType(); @@ -1747,15 +1749,21 @@ pub fn lowerAnonDecl(self: *Coff, decl_val: InternPool.Index, src_loc: Module.Sr // to put it in some location. // ... const gpa = self.base.allocator; + const mod = self.base.options.module.?; + const ty = mod.intern_pool.typeOf(decl_val).toType(); const gop = try self.anon_decls.getOrPut(gpa, decl_val); - if (!gop.found_existing) { - const mod = self.base.options.module.?; - const ty = mod.intern_pool.typeOf(decl_val).toType(); + const required_alignment = switch (decl_align) { + .none => ty.abiAlignment(mod), + else => decl_align, + }; + if (!gop.found_existing or + !required_alignment.check(self.getAtom(gop.value_ptr.*).getSymbol(self).value)) + { const val = decl_val.toValue(); const tv = TypedValue{ .ty = ty, .val = val }; const name = try std.fmt.allocPrint(gpa, "__anon_{d}", .{@intFromEnum(decl_val)}); defer gpa.free(name); - const res = self.lowerConst(name, tv, self.rdata_section_index.?, src_loc) catch |err| switch (err) { + const res = self.lowerConst(name, tv, required_alignment, self.rdata_section_index.?, src_loc) catch |err| switch (err) { else => { // TODO improve error message const em = try Module.ErrorMsg.create(gpa, src_loc, "lowerAnonDecl failed with error: {s}", .{ diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 2d16cd03e9bb77c39d30051e7ad48ddd35bb88a7..738d648ebec49a251d7ecea501fdd687c788ca02 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -473,7 +473,7 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link. return vaddr; } -pub fn lowerAnonDecl(self: *Elf, decl_val: InternPool.Index, src_loc: Module.SrcLoc) !codegen.Result { +pub fn lowerAnonDecl(self: *Elf, decl_val: InternPool.Index, decl_align: InternPool.Alignment, src_loc: Module.SrcLoc) !codegen.Result { // This is basically the same as lowerUnnamedConst. // example: // const ty = mod.intern_pool.typeOf(decl_val).toType(); @@ -484,15 +484,21 @@ pub fn lowerAnonDecl(self: *Elf, decl_val: InternPool.Index, src_loc: Module.Src // to put it in some location. // ... const gpa = self.base.allocator; + const mod = self.base.options.module.?; + const ty = mod.intern_pool.typeOf(decl_val).toType(); const gop = try self.anon_decls.getOrPut(gpa, decl_val); - if (!gop.found_existing) { - const mod = self.base.options.module.?; - const ty = mod.intern_pool.typeOf(decl_val).toType(); + const required_alignment = switch (decl_align) { + .none => ty.abiAlignment(mod), + else => decl_align, + }; + if (!gop.found_existing or + required_alignment.order(self.symbol(gop.value_ptr.*).atom(self).?.alignment).compare(.gt)) + { const val = decl_val.toValue(); const tv = TypedValue{ .ty = ty, .val = val }; const name = try std.fmt.allocPrint(gpa, "__anon_{d}", .{@intFromEnum(decl_val)}); defer gpa.free(name); - const res = self.lowerConst(name, tv, self.zig_rodata_section_index.?, src_loc) catch |err| switch (err) { + const res = self.lowerConst(name, tv, required_alignment, self.zig_rodata_section_index.?, src_loc) catch |err| switch (err) { else => { // TODO improve error message const em = try Module.ErrorMsg.create(gpa, src_loc, "lowerAnonDecl failed with error: {s}", .{ @@ -3479,7 +3485,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module const index = unnamed_consts.items.len; const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); defer gpa.free(name); - const sym_index = switch (try self.lowerConst(name, typed_value, self.zig_rodata_section_index.?, decl.srcLoc(mod))) { + const sym_index = switch (try self.lowerConst(name, typed_value, typed_value.ty.abiAlignment(mod), self.zig_rodata_section_index.?, decl.srcLoc(mod))) { .ok => |sym_index| sym_index, .fail => |em| { decl.analysis = .codegen_failure; @@ -3502,6 +3508,7 @@ fn lowerConst( self: *Elf, name: []const u8, tv: TypedValue, + required_alignment: InternPool.Alignment, output_section_index: u16, src_loc: Module.SrcLoc, ) !LowerConstResult { @@ -3510,7 +3517,6 @@ fn lowerConst( var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - const mod = self.base.options.module.?; const zig_module = self.file(self.zig_module_index.?).?.zig_module; const sym_index = try zig_module.addAtom(self); @@ -3524,7 +3530,6 @@ fn lowerConst( .fail => |em| return .{ .fail = em }, }; - const required_alignment = tv.ty.abiAlignment(mod); const phdr_index = self.phdr_to_shdr_table.get(output_section_index).?; const local_sym = self.symbol(sym_index); const name_str_index = try self.strtab.insert(gpa, name); diff --git a/src/link/MachO.zig b/src/link/MachO.zig index cc537cdd72154cbc78319eeff07069142c24c827..eb82de737402bb34489193dbb44b44caaf3af3a0 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2196,7 +2196,7 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu const index = unnamed_consts.items.len; const name = try std.fmt.allocPrint(gpa, "___unnamed_{s}_{d}", .{ decl_name, index }); defer gpa.free(name); - const atom_index = switch (try self.lowerConst(name, typed_value, self.data_const_section_index.?, decl.srcLoc(mod))) { + const atom_index = switch (try self.lowerConst(name, typed_value, typed_value.ty.abiAlignment(mod), self.data_const_section_index.?, decl.srcLoc(mod))) { .ok => |atom_index| atom_index, .fail => |em| { decl.analysis = .codegen_failure; @@ -2219,6 +2219,7 @@ fn lowerConst( self: *MachO, name: []const u8, tv: TypedValue, + required_alignment: InternPool.Alignment, sect_id: u8, src_loc: Module.SrcLoc, ) !LowerConstResult { @@ -2227,8 +2228,6 @@ fn lowerConst( var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - const mod = self.base.options.module.?; - log.debug("allocating symbol indexes for {s}", .{name}); const sym_index = try self.allocateSymbol(); @@ -2243,7 +2242,6 @@ fn lowerConst( .fail => |em| return .{ .fail = em }, }; - const required_alignment = tv.ty.abiAlignment(mod); const atom = self.getAtomPtr(atom_index); atom.size = code.len; // TODO: work out logic for disambiguating functions from function pointers @@ -2868,7 +2866,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil return 0; } -pub fn lowerAnonDecl(self: *MachO, decl_val: InternPool.Index, src_loc: Module.SrcLoc) !codegen.Result { +pub fn lowerAnonDecl(self: *MachO, decl_val: InternPool.Index, decl_align: InternPool.Alignment, src_loc: Module.SrcLoc) !codegen.Result { // This is basically the same as lowerUnnamedConst. // example: // const ty = mod.intern_pool.typeOf(decl_val).toType(); @@ -2879,15 +2877,21 @@ pub fn lowerAnonDecl(self: *MachO, decl_val: InternPool.Index, src_loc: Module.S // to put it in some location. // ... const gpa = self.base.allocator; + const mod = self.base.options.module.?; + const ty = mod.intern_pool.typeOf(decl_val).toType(); const gop = try self.anon_decls.getOrPut(gpa, decl_val); - if (!gop.found_existing) { - const mod = self.base.options.module.?; - const ty = mod.intern_pool.typeOf(decl_val).toType(); + const required_alignment = switch (decl_align) { + .none => ty.abiAlignment(mod), + else => decl_align, + }; + if (!gop.found_existing or + !required_alignment.check(self.getAtom(gop.value_ptr.*).getSymbol(self).n_value)) + { const val = decl_val.toValue(); const tv = TypedValue{ .ty = ty, .val = val }; const name = try std.fmt.allocPrint(gpa, "__anon_{d}", .{@intFromEnum(decl_val)}); defer gpa.free(name); - const res = self.lowerConst(name, tv, self.data_const_section_index.?, src_loc) catch |err| switch (err) { + const res = self.lowerConst(name, tv, required_alignment, self.data_const_section_index.?, src_loc) catch |err| switch (err) { else => { // TODO improve error message const em = try Module.ErrorMsg.create(gpa, src_loc, "lowerAnonDecl failed with error: {s}", .{ diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index fc827b23ebe963853daae102855834406860373d..f138cdc3e2a024ed96037201111ef6845babeb1d 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -1702,25 +1702,30 @@ pub fn getDeclVAddr( return target_symbol_index; } -pub fn lowerAnonDecl(wasm: *Wasm, decl_val: InternPool.Index, src_loc: Module.SrcLoc) !codegen.Result { +pub fn lowerAnonDecl(wasm: *Wasm, decl_val: InternPool.Index, decl_align: Alignment, src_loc: Module.SrcLoc) !codegen.Result { const gop = try wasm.anon_decls.getOrPut(wasm.base.allocator, decl_val); - if (gop.found_existing) { - return .ok; + if (!gop.found_existing) { + const mod = wasm.base.options.module.?; + const ty = mod.intern_pool.typeOf(decl_val).toType(); + const tv: TypedValue = .{ .ty = ty, .val = decl_val.toValue() }; + const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__anon_{d}", .{@intFromEnum(decl_val)}); + defer wasm.base.allocator.free(name); + + switch (try wasm.lowerConst(name, tv, src_loc)) { + .ok => |atom_index| gop.value_ptr.* = atom_index, + .fail => |em| return .{ .fail = em }, + } } - const mod = wasm.base.options.module.?; - const ty = mod.intern_pool.typeOf(decl_val).toType(); - const tv: TypedValue = .{ .ty = ty, .val = decl_val.toValue() }; - const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__anon_{d}", .{@intFromEnum(decl_val)}); - defer wasm.base.allocator.free(name); - - switch (try wasm.lowerConst(name, tv, src_loc)) { - .ok => |atom_index| { - gop.value_ptr.* = atom_index; - return .ok; + const atom = wasm.getAtomPtr(gop.value_ptr.*); + atom.alignment = switch (atom.alignment) { + .none => decl_align, + else => switch (decl_align) { + .none => atom.alignment, + else => atom.alignment.maxStrict(decl_align), }, - .fail => |em| return .{ .fail = em }, - } + }; + return .ok; } pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { -- 2.54.0 From 3d7c6c803b788138aa08f51cf4ee8cf7892e315d Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 20 Oct 2023 17:29:04 +0200 Subject: [PATCH 7/7] dwarf: fix false assumption that ptr-deref requires GOT-indirection --- src/link/Dwarf.zig | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index b78243de74b567f43e3d5148653d11d6ba933a40..ae08c059fd6735353d7de6dc07ab6d3002f6eb83 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -87,15 +87,6 @@ pub const DeclState = struct { self.exprloc_relocs.deinit(self.gpa); } - fn addExprlocReloc(self: *DeclState, target: u32, offset: u32, is_ptr: bool) !void { - log.debug("{x}: target sym %{d}, via GOT {}", .{ offset, target, is_ptr }); - try self.exprloc_relocs.append(self.gpa, .{ - .type = if (is_ptr) .got_load else .direct_load, - .target = target, - .offset = offset, - }); - } - /// Adds local type relocation of the form: @offset => @this + addend /// @this signifies the offset within the .debug_abbrev section of the containing atom. fn addTypeRelocLocal(self: *DeclState, atom_index: Atom.Index, offset: u32, addend: u32) !void { @@ -807,11 +798,25 @@ pub const DeclState = struct { try dbg_info.append(DW.OP.deref); } switch (loc) { - .linker_load => |load_struct| try self.addExprlocReloc( - load_struct.sym_index, - offset, - is_ptr, - ), + .linker_load => |load_struct| switch (load_struct.type) { + .direct => { + log.debug("{x}: target sym %{d}", .{ offset, load_struct.sym_index }); + try self.exprloc_relocs.append(self.gpa, .{ + .type = .direct_load, + .target = load_struct.sym_index, + .offset = offset, + }); + }, + .got => { + log.debug("{x}: target sym %{d} via GOT", .{ offset, load_struct.sym_index }); + try self.exprloc_relocs.append(self.gpa, .{ + .type = .got_load, + .target = load_struct.sym_index, + .offset = offset, + }); + }, + else => {}, // TODO + }, else => {}, } }, -- 2.54.0