Commit 7bab406c79

Andrew Kelley <andrew@ziglang.org>
2023-10-20 00:10:35
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.
1 parent 5aa82ed
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 };
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;
 
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);
         }
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);
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(
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| {
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();
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,
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);
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);
+}
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,
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),
                         });
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,