Sema: fix alignment of type-inferred locals

This commit is contained in:
Andrew Kelley 2021-11-22 20:30:20 -07:00
parent e8b9942873
commit e08b6149ab
5 changed files with 70 additions and 28 deletions

View File

@ -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);

View File

@ -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,
},
};

View File

@ -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");

View File

@ -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);
}

View File

@ -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);
}