mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
stage2: implement aligned variables and @alignCast
* Sema: implement zirAllocExtended * Sema: implement zirAlignCast
This commit is contained in:
parent
01c1f41520
commit
d97928bf52
114
src/Sema.zig
114
src/Sema.zig
@ -1940,7 +1940,7 @@ fn zirRetPtr(
|
||||
try sema.requireFunctionBlock(block, src);
|
||||
|
||||
if (block.is_comptime) {
|
||||
return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty);
|
||||
return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty, 0);
|
||||
}
|
||||
|
||||
const ptr_type = try Type.ptr(sema.arena, .{
|
||||
@ -2067,9 +2067,7 @@ fn zirAllocExtended(
|
||||
const type_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
break :blk try sema.resolveType(block, ty_src, type_ref);
|
||||
} else {
|
||||
return sema.fail(block, src, "TODO implement Sema.zirAllocExtended inferred", .{});
|
||||
};
|
||||
} else undefined;
|
||||
|
||||
const alignment: u16 = if (small.has_align) blk: {
|
||||
const align_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
|
||||
@ -2078,22 +2076,47 @@ fn zirAllocExtended(
|
||||
break :blk alignment;
|
||||
} else 0;
|
||||
|
||||
const inferred_alloc_ty = if (small.is_const)
|
||||
Type.initTag(.inferred_alloc_const)
|
||||
else
|
||||
Type.initTag(.inferred_alloc_mut);
|
||||
|
||||
if (small.is_comptime) {
|
||||
return sema.fail(block, src, "TODO implement Sema.zirAllocExtended comptime", .{});
|
||||
if (small.has_type) {
|
||||
return sema.analyzeComptimeAlloc(block, var_ty, alignment);
|
||||
} else {
|
||||
return sema.addConstant(
|
||||
inferred_alloc_ty,
|
||||
try Value.Tag.inferred_alloc_comptime.create(sema.arena, undefined),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!small.is_const) {
|
||||
return sema.fail(block, src, "TODO implement Sema.zirAllocExtended var", .{});
|
||||
if (small.has_type) {
|
||||
if (!small.is_const) {
|
||||
try sema.validateVarType(block, ty_src, var_ty, false);
|
||||
}
|
||||
const ptr_type = try Type.ptr(sema.arena, .{
|
||||
.pointee_type = var_ty,
|
||||
.@"align" = alignment,
|
||||
.@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
|
||||
});
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
try sema.resolveTypeLayout(block, src, var_ty);
|
||||
return block.addTy(.alloc, ptr_type);
|
||||
}
|
||||
|
||||
const ptr_type = try Type.ptr(sema.arena, .{
|
||||
.pointee_type = var_ty,
|
||||
.@"align" = alignment,
|
||||
.@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .local),
|
||||
});
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
try sema.resolveTypeLayout(block, src, var_ty);
|
||||
return block.addTy(.alloc, ptr_type);
|
||||
// `Sema.addConstant` does not add the instruction to the block because it is
|
||||
// not needed in the case of constant values. However here, we plan to "downgrade"
|
||||
// to a normal instruction when we hit `resolve_inferred_alloc`. So we append
|
||||
// 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 sema.requireFunctionBlock(block, src);
|
||||
try block.instructions.append(sema.gpa, Air.refToIndex(result).?);
|
||||
return result;
|
||||
}
|
||||
|
||||
fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
@ -2103,7 +2126,7 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
|
||||
const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
|
||||
return sema.analyzeComptimeAlloc(block, var_ty);
|
||||
return sema.analyzeComptimeAlloc(block, var_ty, 0);
|
||||
}
|
||||
|
||||
fn zirAllocInferredComptime(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
@ -2125,7 +2148,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
const var_decl_src = inst_data.src();
|
||||
const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
|
||||
if (block.is_comptime) {
|
||||
return sema.analyzeComptimeAlloc(block, var_ty);
|
||||
return sema.analyzeComptimeAlloc(block, var_ty, 0);
|
||||
}
|
||||
const ptr_type = try Type.ptr(sema.arena, .{
|
||||
.pointee_type = var_ty,
|
||||
@ -2145,7 +2168,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
||||
const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
|
||||
const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
|
||||
if (block.is_comptime) {
|
||||
return sema.analyzeComptimeAlloc(block, var_ty);
|
||||
return sema.analyzeComptimeAlloc(block, var_ty, 0);
|
||||
}
|
||||
try sema.validateVarType(block, ty_src, var_ty, false);
|
||||
const ptr_type = try Type.ptr(sema.arena, .{
|
||||
@ -9436,8 +9459,10 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
const ty = try sema.resolveType(block, operand_src, inst_data.operand);
|
||||
const resolved_ty = try sema.resolveTypeFields(block, operand_src, ty);
|
||||
try sema.resolveTypeLayout(block, operand_src, resolved_ty);
|
||||
const target = sema.mod.getTarget();
|
||||
const abi_align = ty.abiAlignment(target);
|
||||
const abi_align = resolved_ty.abiAlignment(target);
|
||||
return sema.addIntUnsigned(Type.comptime_int, abi_align);
|
||||
}
|
||||
|
||||
@ -9735,8 +9760,33 @@ fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
|
||||
|
||||
fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
return sema.fail(block, src, "TODO: Sema.zirAlignCast", .{});
|
||||
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const align_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
|
||||
const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
|
||||
const dest_align = try sema.resolveAlign(block, align_src, extra.lhs);
|
||||
const ptr = sema.resolveInst(extra.rhs);
|
||||
const ptr_ty = sema.typeOf(ptr);
|
||||
|
||||
// TODO in addition to pointers, this instruction is supposed to work for
|
||||
// pointer-like optionals and slices.
|
||||
try sema.checkPtrType(block, ptr_src, ptr_ty);
|
||||
|
||||
// TODO compile error if the result pointer is comptime known and would have an
|
||||
// alignment that disagrees with the Decl's alignment.
|
||||
|
||||
// TODO insert safety check that the alignment is correct
|
||||
|
||||
const ptr_info = ptr_ty.ptrInfo().data;
|
||||
const dest_ty = try Type.ptr(sema.arena, .{
|
||||
.pointee_type = ptr_info.pointee_type,
|
||||
.@"align" = dest_align,
|
||||
.@"addrspace" = ptr_info.@"addrspace",
|
||||
.mutable = ptr_info.mutable,
|
||||
.@"allowzero" = ptr_info.@"allowzero",
|
||||
.@"volatile" = ptr_info.@"volatile",
|
||||
.size = ptr_info.size,
|
||||
});
|
||||
return sema.coerceCompatiblePtrs(block, dest_ty, ptr, ptr_src);
|
||||
}
|
||||
|
||||
fn zirClz(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
@ -9838,6 +9888,18 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr
|
||||
}
|
||||
}
|
||||
|
||||
fn checkPtrType(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
ty_src: LazySrcLoc,
|
||||
ty: Type,
|
||||
) CompileError!void {
|
||||
switch (ty.zigTypeTag()) {
|
||||
.Pointer => {},
|
||||
else => return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty}),
|
||||
}
|
||||
}
|
||||
|
||||
fn checkFloatType(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@ -14630,14 +14692,22 @@ fn analyzeComptimeAlloc(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
var_type: Type,
|
||||
alignment: u32,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const ptr_type = try Type.ptr(sema.arena, .{
|
||||
.pointee_type = var_type,
|
||||
.@"addrspace" = target_util.defaultAddressSpace(sema.mod.getTarget(), .global_constant),
|
||||
.@"align" = alignment,
|
||||
});
|
||||
|
||||
var anon_decl = try block.startAnonDecl();
|
||||
defer anon_decl.deinit();
|
||||
|
||||
const align_val = if (alignment == 0)
|
||||
Value.@"null"
|
||||
else
|
||||
try Value.Tag.int_u64.create(anon_decl.arena(), alignment);
|
||||
|
||||
const decl = try anon_decl.finish(
|
||||
try var_type.copy(anon_decl.arena()),
|
||||
// There will be stores before the first load, but they may be to sub-elements or
|
||||
@ -14645,6 +14715,8 @@ fn analyzeComptimeAlloc(
|
||||
// into fields/elements and have those overridden with stored values.
|
||||
Value.undef,
|
||||
);
|
||||
decl.align_val = align_val;
|
||||
|
||||
try sema.mod.declareDeclDependency(sema.owner_decl, decl);
|
||||
return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{
|
||||
.runtime_index = block.runtime_index,
|
||||
|
||||
@ -41,3 +41,82 @@ test "implicitly decreasing slice alignment" {
|
||||
fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) u32 {
|
||||
return a[0] + b[0];
|
||||
}
|
||||
|
||||
test "@alignCast pointers" {
|
||||
var x: u32 align(4) = 1;
|
||||
expectsOnly1(&x);
|
||||
try expect(x == 2);
|
||||
}
|
||||
fn expectsOnly1(x: *align(1) u32) void {
|
||||
expects4(@alignCast(4, x));
|
||||
}
|
||||
fn expects4(x: *align(4) u32) void {
|
||||
x.* += 1;
|
||||
}
|
||||
|
||||
test "specifying alignment allows pointer cast" {
|
||||
try testBytesAlign(0x33);
|
||||
}
|
||||
fn testBytesAlign(b: u8) !void {
|
||||
var bytes align(4) = [_]u8{ b, b, b, b };
|
||||
const ptr = @ptrCast(*u32, &bytes[0]);
|
||||
try expect(ptr.* == 0x33333333);
|
||||
}
|
||||
|
||||
test "@alignCast slices" {
|
||||
var array align(4) = [_]u32{ 1, 1 };
|
||||
const slice = array[0..];
|
||||
sliceExpectsOnly1(slice);
|
||||
try expect(slice[0] == 2);
|
||||
}
|
||||
fn sliceExpectsOnly1(slice: []align(1) u32) void {
|
||||
sliceExpects4(@alignCast(4, slice));
|
||||
}
|
||||
fn sliceExpects4(slice: []align(4) u32) void {
|
||||
slice[0] += 1;
|
||||
}
|
||||
|
||||
test "alignment of structs" {
|
||||
try expect(@alignOf(struct {
|
||||
a: i32,
|
||||
b: *i32,
|
||||
}) == @alignOf(usize));
|
||||
}
|
||||
|
||||
test "return error union with 128-bit integer" {
|
||||
try expect(3 == try give());
|
||||
}
|
||||
fn give() anyerror!u128 {
|
||||
return 3;
|
||||
}
|
||||
|
||||
test "alignment of >= 128-bit integer type" {
|
||||
try expect(@alignOf(u128) == 16);
|
||||
try expect(@alignOf(u129) == 16);
|
||||
}
|
||||
|
||||
test "alignment of struct with 128-bit field" {
|
||||
try expect(@alignOf(struct {
|
||||
x: u128,
|
||||
}) == 16);
|
||||
|
||||
comptime {
|
||||
try expect(@alignOf(struct {
|
||||
x: u128,
|
||||
}) == 16);
|
||||
}
|
||||
}
|
||||
|
||||
test "size of extern struct with 128-bit field" {
|
||||
try expect(@sizeOf(extern struct {
|
||||
x: u128,
|
||||
y: u8,
|
||||
}) == 32);
|
||||
|
||||
comptime {
|
||||
try expect(@sizeOf(extern struct {
|
||||
x: u128,
|
||||
y: u8,
|
||||
}) == 32);
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,43 +39,6 @@ test "bit field alignment" {
|
||||
try expect(@TypeOf(&blah.b) == *align(1:3:1) const u3);
|
||||
}
|
||||
|
||||
test "specifying alignment allows pointer cast" {
|
||||
try testBytesAlign(0x33);
|
||||
}
|
||||
fn testBytesAlign(b: u8) !void {
|
||||
var bytes align(4) = [_]u8{ b, b, b, b };
|
||||
const ptr = @ptrCast(*u32, &bytes[0]);
|
||||
try expect(ptr.* == 0x33333333);
|
||||
}
|
||||
|
||||
test "@alignCast pointers" {
|
||||
var x: u32 align(4) = 1;
|
||||
expectsOnly1(&x);
|
||||
try expect(x == 2);
|
||||
}
|
||||
fn expectsOnly1(x: *align(1) u32) void {
|
||||
expects4(@alignCast(4, x));
|
||||
}
|
||||
fn expects4(x: *align(4) u32) void {
|
||||
x.* += 1;
|
||||
}
|
||||
|
||||
test "@alignCast slices" {
|
||||
var array align(4) = [_]u32{
|
||||
1,
|
||||
1,
|
||||
};
|
||||
const slice = array[0..];
|
||||
sliceExpectsOnly1(slice);
|
||||
try expect(slice[0] == 2);
|
||||
}
|
||||
fn sliceExpectsOnly1(slice: []align(1) u32) void {
|
||||
sliceExpects4(@alignCast(4, slice));
|
||||
}
|
||||
fn sliceExpects4(slice: []align(4) u32) void {
|
||||
slice[0] += 1;
|
||||
}
|
||||
|
||||
test "implicitly decreasing fn alignment" {
|
||||
// function alignment is a compile error on wasm32/wasm64
|
||||
if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest;
|
||||
@ -180,13 +143,6 @@ fn fnWithAlignedStack() i32 {
|
||||
return 1234;
|
||||
}
|
||||
|
||||
test "alignment of structs" {
|
||||
try expect(@alignOf(struct {
|
||||
a: i32,
|
||||
b: *i32,
|
||||
}) == @alignOf(usize));
|
||||
}
|
||||
|
||||
test "alignment of function with c calling convention" {
|
||||
var runtime_nothing = nothing;
|
||||
const casted1 = @ptrCast(*const u8, runtime_nothing);
|
||||
@ -196,44 +152,6 @@ test "alignment of function with c calling convention" {
|
||||
|
||||
fn nothing() callconv(.C) void {}
|
||||
|
||||
test "return error union with 128-bit integer" {
|
||||
try expect(3 == try give());
|
||||
}
|
||||
fn give() anyerror!u128 {
|
||||
return 3;
|
||||
}
|
||||
|
||||
test "alignment of >= 128-bit integer type" {
|
||||
try expect(@alignOf(u128) == 16);
|
||||
try expect(@alignOf(u129) == 16);
|
||||
}
|
||||
|
||||
test "alignment of struct with 128-bit field" {
|
||||
try expect(@alignOf(struct {
|
||||
x: u128,
|
||||
}) == 16);
|
||||
|
||||
comptime {
|
||||
try expect(@alignOf(struct {
|
||||
x: u128,
|
||||
}) == 16);
|
||||
}
|
||||
}
|
||||
|
||||
test "size of extern struct with 128-bit field" {
|
||||
try expect(@sizeOf(extern struct {
|
||||
x: u128,
|
||||
y: u8,
|
||||
}) == 32);
|
||||
|
||||
comptime {
|
||||
try expect(@sizeOf(extern struct {
|
||||
x: u128,
|
||||
y: u8,
|
||||
}) == 32);
|
||||
}
|
||||
}
|
||||
|
||||
const DefaultAligned = struct {
|
||||
nevermind: u32,
|
||||
badguy: i128,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user