From e08b6149ab8fb80ad8fa4983ad3aca8ef3303a9f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 22 Nov 2021 20:30:20 -0700 Subject: [PATCH] Sema: fix alignment of type-inferred locals --- src/Sema.zig | 48 +++++++++++++++++++++++++--------- src/value.zig | 15 ++++++++++- test/behavior.zig | 1 + test/behavior/align_llvm.zig | 19 ++++++++++++++ test/behavior/align_stage1.zig | 15 ----------- 5 files changed, 70 insertions(+), 28 deletions(-) create mode 100644 test/behavior/align_llvm.zig diff --git a/src/Sema.zig b/src/Sema.zig index b873af4c7e..413c82e005 100644 --- a/src/Sema.zig +++ b/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); diff --git a/src/value.zig b/src/value.zig index d4ee5bcf0c..cc030faaae 100644 --- a/src/value.zig +++ b/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, }, }; diff --git a/test/behavior.zig b/test/behavior.zig index 7d95fc8eb9..4c768e304e 100644 --- a/test/behavior.zig +++ b/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"); diff --git a/test/behavior/align_llvm.zig b/test/behavior/align_llvm.zig new file mode 100644 index 0000000000..e87c109b4d --- /dev/null +++ b/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); +} diff --git a/test/behavior/align_stage1.zig b/test/behavior/align_stage1.zig index 0626c702e4..a4af92368e 100644 --- a/test/behavior/align_stage1.zig +++ b/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); -}