Commit e08b6149ab

Andrew Kelley <andrew@ziglang.org>
2021-11-23 04:30:20
Sema: fix alignment of type-inferred locals
1 parent e8b9942
src/Sema.zig
@@ -1415,10 +1415,6 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
     const ptr = sema.resolveInst(bin_inst.rhs);
 
     const addr_space = target_util.defaultAddressSpace(sema.mod.getTarget(), .local);
-    const ptr_ty = try Type.ptr(sema.arena, .{
-        .pointee_type = pointee_ty,
-        .@"addrspace" = addr_space,
-    });
 
     if (Air.refToIndex(ptr)) |ptr_inst| {
         if (sema.air_instructions.items(.tag)[ptr_inst] == .constant) {
@@ -1438,6 +1434,11 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
                     try inferred_alloc.stored_inst_list.append(sema.arena, operand);
 
                     try sema.requireRuntimeBlock(block, src);
+                    const ptr_ty = try Type.ptr(sema.arena, .{
+                        .pointee_type = pointee_ty,
+                        .@"align" = inferred_alloc.alignment,
+                        .@"addrspace" = addr_space,
+                    });
                     const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
                     return bitcasted_ptr;
                 },
@@ -1447,19 +1448,30 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
                     // The alloc will turn into a Decl.
                     var anon_decl = try block.startAnonDecl();
                     defer anon_decl.deinit();
-                    iac.data = try anon_decl.finish(
+                    iac.data.decl = try anon_decl.finish(
                         try pointee_ty.copy(anon_decl.arena()),
                         Value.undef,
                     );
+                    const ptr_ty = try Type.ptr(sema.arena, .{
+                        .pointee_type = pointee_ty,
+                        .@"align" = iac.data.alignment,
+                        .@"addrspace" = addr_space,
+                    });
                     return sema.addConstant(
                         ptr_ty,
                         try Value.Tag.decl_ref_mut.create(sema.arena, .{
-                            .decl = iac.data,
+                            .decl = iac.data.decl,
                             .runtime_index = block.runtime_index,
                         }),
                     );
                 },
-                .decl_ref_mut => return sema.addConstant(ptr_ty, ptr_val),
+                .decl_ref_mut => {
+                    const ptr_ty = try Type.ptr(sema.arena, .{
+                        .pointee_type = pointee_ty,
+                        .@"addrspace" = addr_space,
+                    });
+                    return sema.addConstant(ptr_ty, ptr_val);
+                },
                 else => {},
             }
         }
@@ -1491,6 +1503,11 @@ fn zirCoerceResultPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
         sema.air_instructions.len -= 1;
     }
 
+    const ptr_ty = try Type.ptr(sema.arena, .{
+        .pointee_type = pointee_ty,
+        .@"addrspace" = addr_space,
+    });
+
     var new_ptr = ptr;
 
     while (true) {
@@ -2183,7 +2200,10 @@ fn zirAllocExtended(
         } else {
             return sema.addConstant(
                 inferred_alloc_ty,
-                try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),
+                try Value.Tag.inferred_alloc_comptime.create(sema.arena, .{
+                    .decl = undefined,
+                    .alignment = alignment,
+                }),
             );
         }
     }
@@ -2208,7 +2228,7 @@ fn zirAllocExtended(
     // to the block even though it is currently a `.constant`.
     const result = try sema.addConstant(
         inferred_alloc_ty,
-        try Value.Tag.inferred_alloc.create(sema.arena, .{}),
+        try Value.Tag.inferred_alloc.create(sema.arena, .{ .alignment = alignment }),
     );
     try sema.requireFunctionBlock(block, src);
     try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
@@ -2302,7 +2322,7 @@ fn zirAllocInferred(
     // to the block even though it is currently a `.constant`.
     const result = try sema.addConstant(
         inferred_alloc_ty,
-        try Value.Tag.inferred_alloc.create(sema.arena, .{}),
+        try Value.Tag.inferred_alloc.create(sema.arena, .{ .alignment = 0 }),
     );
     try sema.requireFunctionBlock(block, src);
     try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
@@ -2331,12 +2351,13 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
     switch (ptr_val.tag()) {
         .inferred_alloc_comptime => {
             const iac = ptr_val.castTag(.inferred_alloc_comptime).?;
-            const decl = iac.data;
+            const decl = iac.data.decl;
             try sema.mod.declareDeclDependency(sema.owner_decl, decl);
 
             const final_elem_ty = try decl.ty.copy(sema.arena);
             const final_ptr_ty = try Type.ptr(sema.arena, .{
                 .pointee_type = final_elem_ty,
+                .@"align" = iac.data.alignment,
                 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
             });
             const final_ptr_ty_inst = try sema.addType(final_ptr_ty);
@@ -2365,6 +2386,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
             // Change it to a normal alloc.
             const final_ptr_ty = try Type.ptr(sema.arena, .{
                 .pointee_type = final_elem_ty,
+                .@"align" = inferred_alloc.data.alignment,
                 .@"addrspace" = target_util.defaultAddressSpace(target, .local),
             });
             sema.air_instructions.set(ptr_inst, .{
@@ -2681,10 +2703,11 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
             }
             var anon_decl = try block.startAnonDecl();
             defer anon_decl.deinit();
-            iac.data = try anon_decl.finish(
+            iac.data.decl = try anon_decl.finish(
                 try operand_ty.copy(anon_decl.arena()),
                 try operand_val.copy(anon_decl.arena()),
             );
+            // TODO set the alignment on the decl
             return;
         } else {
             return sema.failWithNeededComptime(block, src);
@@ -2698,6 +2721,7 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
         // Create a runtime bitcast instruction with exactly the type the pointer wants.
         const ptr_ty = try Type.ptr(sema.arena, .{
             .pointee_type = operand_ty,
+            .@"align" = inferred_alloc.data.alignment,
             .@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
         });
         const bitcasted_ptr = try block.addBitCast(ptr_ty, ptr);
src/value.zig
@@ -256,7 +256,6 @@ pub const Value = extern union {
 
                 .extern_fn,
                 .decl_ref,
-                .inferred_alloc_comptime,
                 => Payload.Decl,
 
                 .repeated,
@@ -291,6 +290,7 @@ pub const Value = extern union {
                 .float_128 => Payload.Float_128,
                 .@"error" => Payload.Error,
                 .inferred_alloc => Payload.InferredAlloc,
+                .inferred_alloc_comptime => Payload.InferredAllocComptime,
                 .@"struct" => Payload.Struct,
                 .@"union" => Payload.Union,
                 .bound_fn => Payload.BoundFn,
@@ -2889,6 +2889,19 @@ pub const Value = extern union {
                 /// the items are contiguous in memory and thus can be passed to
                 /// `Module.resolvePeerTypes`.
                 stored_inst_list: std.ArrayListUnmanaged(Air.Inst.Ref) = .{},
+                /// 0 means ABI-aligned.
+                alignment: u16,
+            },
+        };
+
+        pub const InferredAllocComptime = struct {
+            pub const base_tag = Tag.inferred_alloc_comptime;
+
+            base: Payload = .{ .tag = base_tag },
+            data: struct {
+                decl: *Module.Decl,
+                /// 0 means ABI-aligned.
+                alignment: u16,
             },
         };
 
test/behavior/align_llvm.zig
@@ -0,0 +1,19 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const builtin = @import("builtin");
+const native_arch = builtin.target.cpu.arch;
+
+test "page aligned array on stack" {
+    // Large alignment value to make it hard to accidentally pass.
+    var array align(0x1000) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
+    var number1: u8 align(16) = 42;
+    var number2: u8 align(16) = 43;
+
+    try expect(@ptrToInt(&array[0]) & 0xFFF == 0);
+    try expect(array[3] == 4);
+
+    try expect(@truncate(u4, @ptrToInt(&number1)) == 0);
+    try expect(@truncate(u4, @ptrToInt(&number2)) == 0);
+    try expect(number1 == 42);
+    try expect(number2 == 43);
+}
test/behavior/align_stage1.zig
@@ -223,18 +223,3 @@ test "align(N) on functions" {
 fn overaligned_fn() align(0x1000) i32 {
     return 42;
 }
-
-test "page aligned array on stack" {
-    // Large alignment value to make it hard to accidentally pass.
-    var array align(0x1000) = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
-    var number1: u8 align(16) = 42;
-    var number2: u8 align(16) = 43;
-
-    try expect(@ptrToInt(&array[0]) & 0xFFF == 0);
-    try expect(array[3] == 4);
-
-    try expect(@truncate(u4, @ptrToInt(&number1)) == 0);
-    try expect(@truncate(u4, @ptrToInt(&number2)) == 0);
-    try expect(number1 == 42);
-    try expect(number2 == 43);
-}
test/behavior.zig
@@ -44,6 +44,7 @@ test {
 
     if (builtin.object_format != .c) {
         // Tests that pass for stage1 and stage2 but not the C backend.
+        _ = @import("behavior/align_llvm.zig");
         _ = @import("behavior/array.zig");
         _ = @import("behavior/atomics.zig");
         _ = @import("behavior/basic_llvm.zig");