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 2f861a87e6..3e8eeeb886 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 cddd4340d7..9bb012deb1 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 7523c60ae8..9e03a8f000 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 00fbfc2cb5..217d0b7c15 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 f4da8e5c2e..0000000000 --- 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); -} 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 4c314fadc1..4bb30b854d 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 { 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 800c8288d0..70d0e5ff82 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 8cf121896d..707e2d95b9 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 3e8eeeb886..54e744e6e7 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 3d77d6b8a0..345535a06e 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 3eac273164..639af3d8c4 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 24269f38ba..d7a5666986 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 94598985c0..897d0ea047 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 9bb012deb1..ed4f1bdf6c 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 5a9703af3d..4f971db1de 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 bb68e28bf2..d7144b36ce 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 ded73d6afd..e7e6ebdef3 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 7a8cc47f71..1d5672cf11 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 2c4a88da17..c7560cb419 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, 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 54e744e6e7..450b753637 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(); 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 de512e5bdb..cbfe1a3adb 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 973177c1b0..c72228a42f 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, 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 639af3d8c4..83ab118ac5 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 d7a5666986..fe948d18bd 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 9c79de290d..6e5c809f62 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 94b3c63741..35f0b84411 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 2d16cd03e9..738d648ebe 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 cc537cdd72..eb82de7374 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 fc827b23eb..f138cdc3e2 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 { 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 b78243de74..ae08c059fd 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 => {}, } },