stage2 llvm: Respect container type when lowering parent pointers
We need to make sure that we bitcast our pointers correctly before we use get_element_ptr to compute the offset for the parent pointer. This also includes a small fix-up for a problem where ptrs to const i64/u64 were not using the correct type in >1-level decl chains (where we call lowerParentPtr recursively)
This commit is contained in:
committed by
Andrew Kelley
parent
5fa057053c
commit
1f76b4c6b8
@@ -1518,26 +1518,8 @@ pub const DeclGen = struct {
|
||||
const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False);
|
||||
return llvm_int.constIntToPtr(try dg.llvmType(tv.ty));
|
||||
},
|
||||
.field_ptr, .opt_payload_ptr, .eu_payload_ptr => {
|
||||
const parent = try dg.lowerParentPtr(tv.val, tv.ty);
|
||||
return parent.llvm_ptr.constBitCast(try dg.llvmType(tv.ty));
|
||||
},
|
||||
.elem_ptr => {
|
||||
const elem_ptr = tv.val.castTag(.elem_ptr).?.data;
|
||||
const parent = try dg.lowerParentPtr(elem_ptr.array_ptr, tv.ty);
|
||||
const llvm_usize = try dg.llvmType(Type.usize);
|
||||
if (parent.llvm_ptr.typeOf().getElementType().getTypeKind() == .Array) {
|
||||
const indices: [2]*const llvm.Value = .{
|
||||
llvm_usize.constInt(0, .False),
|
||||
llvm_usize.constInt(elem_ptr.index, .False),
|
||||
};
|
||||
return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
|
||||
} else {
|
||||
const indices: [1]*const llvm.Value = .{
|
||||
llvm_usize.constInt(elem_ptr.index, .False),
|
||||
};
|
||||
return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
|
||||
}
|
||||
.field_ptr, .opt_payload_ptr, .eu_payload_ptr, .elem_ptr => {
|
||||
return dg.lowerParentPtr(tv.val, tv.ty.childType());
|
||||
},
|
||||
.null_value, .zero => {
|
||||
const llvm_type = try dg.llvmType(tv.ty);
|
||||
@@ -2786,7 +2768,7 @@ pub const DeclGen = struct {
|
||||
llvm_ptr: *const llvm.Value,
|
||||
};
|
||||
|
||||
fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl) Error!ParentPtr {
|
||||
fn lowerParentPtrDecl(dg: *DeclGen, ptr_val: Value, decl: *Module.Decl, ptr_child_ty: Type) Error!*const llvm.Value {
|
||||
decl.markAlive();
|
||||
var ptr_ty_payload: Type.Payload.ElemType = .{
|
||||
.base = .{ .tag = .single_mut_pointer },
|
||||
@@ -2794,123 +2776,104 @@ pub const DeclGen = struct {
|
||||
};
|
||||
const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
|
||||
const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl);
|
||||
return ParentPtr{
|
||||
.llvm_ptr = llvm_ptr,
|
||||
.ty = decl.ty,
|
||||
};
|
||||
|
||||
if (ptr_child_ty.eql(decl.ty)) {
|
||||
return llvm_ptr;
|
||||
} else {
|
||||
return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0));
|
||||
}
|
||||
}
|
||||
|
||||
fn lowerParentPtr(dg: *DeclGen, ptr_val: Value, base_ty: Type) Error!ParentPtr {
|
||||
switch (ptr_val.tag()) {
|
||||
fn lowerParentPtr(dg: *DeclGen, ptr_val: Value, ptr_child_ty: Type) Error!*const llvm.Value {
|
||||
var bitcast_needed: bool = undefined;
|
||||
const llvm_ptr = switch (ptr_val.tag()) {
|
||||
.decl_ref_mut => {
|
||||
const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
|
||||
return dg.lowerParentPtrDecl(ptr_val, decl);
|
||||
return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty);
|
||||
},
|
||||
.decl_ref => {
|
||||
const decl = ptr_val.castTag(.decl_ref).?.data;
|
||||
return dg.lowerParentPtrDecl(ptr_val, decl);
|
||||
return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty);
|
||||
},
|
||||
.variable => {
|
||||
const decl = ptr_val.castTag(.variable).?.data.owner_decl;
|
||||
return dg.lowerParentPtrDecl(ptr_val, decl);
|
||||
return dg.lowerParentPtrDecl(ptr_val, decl, ptr_child_ty);
|
||||
},
|
||||
.int_i64 => {
|
||||
const int = ptr_val.castTag(.int_i64).?.data;
|
||||
const llvm_usize = try dg.llvmType(Type.usize);
|
||||
const llvm_int = llvm_usize.constInt(@bitCast(u64, int), .False);
|
||||
return ParentPtr{
|
||||
.llvm_ptr = llvm_int.constIntToPtr(try dg.llvmType(base_ty)),
|
||||
.ty = base_ty,
|
||||
};
|
||||
return llvm_int.constIntToPtr((try dg.llvmType(ptr_child_ty)).pointerType(0));
|
||||
},
|
||||
.int_u64 => {
|
||||
const int = ptr_val.castTag(.int_u64).?.data;
|
||||
const llvm_usize = try dg.llvmType(Type.usize);
|
||||
const llvm_int = llvm_usize.constInt(int, .False);
|
||||
return ParentPtr{
|
||||
.llvm_ptr = llvm_int.constIntToPtr(try dg.llvmType(base_ty)),
|
||||
.ty = base_ty,
|
||||
};
|
||||
return llvm_int.constIntToPtr((try dg.llvmType(ptr_child_ty)).pointerType(0));
|
||||
},
|
||||
.field_ptr => {
|
||||
.field_ptr => blk: {
|
||||
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
|
||||
const parent = try dg.lowerParentPtr(field_ptr.container_ptr, base_ty);
|
||||
const parent_llvm_ptr = try dg.lowerParentPtr(field_ptr.container_ptr, field_ptr.container_ty);
|
||||
const parent_ty = field_ptr.container_ty;
|
||||
|
||||
const field_index = @intCast(u32, field_ptr.field_index);
|
||||
const llvm_u32 = dg.context.intType(32);
|
||||
const target = dg.module.getTarget();
|
||||
switch (parent.ty.zigTypeTag()) {
|
||||
switch (parent_ty.zigTypeTag()) {
|
||||
.Union => {
|
||||
const fields = parent.ty.unionFields();
|
||||
const layout = parent.ty.unionGetLayout(target);
|
||||
const field_ty = fields.values()[field_index].ty;
|
||||
bitcast_needed = true;
|
||||
|
||||
const layout = parent_ty.unionGetLayout(target);
|
||||
if (layout.payload_size == 0) {
|
||||
// In this case a pointer to the union and a pointer to any
|
||||
// (void) payload is the same.
|
||||
return ParentPtr{
|
||||
.llvm_ptr = parent.llvm_ptr,
|
||||
.ty = field_ty,
|
||||
};
|
||||
break :blk parent_llvm_ptr;
|
||||
}
|
||||
if (layout.tag_size == 0) {
|
||||
const indices: [2]*const llvm.Value = .{
|
||||
llvm_u32.constInt(0, .False),
|
||||
llvm_u32.constInt(0, .False),
|
||||
};
|
||||
return ParentPtr{
|
||||
.llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
|
||||
.ty = field_ty,
|
||||
};
|
||||
}
|
||||
const llvm_pl_index = @boolToInt(layout.tag_align >= layout.payload_align);
|
||||
const llvm_pl_index = if (layout.tag_size == 0) 0 else @boolToInt(layout.tag_align >= layout.payload_align);
|
||||
const indices: [2]*const llvm.Value = .{
|
||||
llvm_u32.constInt(0, .False),
|
||||
llvm_u32.constInt(llvm_pl_index, .False),
|
||||
};
|
||||
return ParentPtr{
|
||||
.llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
|
||||
.ty = field_ty,
|
||||
};
|
||||
break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
|
||||
},
|
||||
.Struct => {
|
||||
const field_ty = parent_ty.structFieldType(field_index);
|
||||
bitcast_needed = !field_ty.eql(ptr_child_ty);
|
||||
|
||||
var ty_buf: Type.Payload.Pointer = undefined;
|
||||
const llvm_field_index = llvmFieldIndex(parent.ty, field_index, target, &ty_buf).?;
|
||||
const llvm_field_index = llvmFieldIndex(parent_ty, field_index, target, &ty_buf).?;
|
||||
const indices: [2]*const llvm.Value = .{
|
||||
llvm_u32.constInt(0, .False),
|
||||
llvm_u32.constInt(llvm_field_index, .False),
|
||||
};
|
||||
return ParentPtr{
|
||||
.llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
|
||||
.ty = parent.ty.structFieldType(field_index),
|
||||
};
|
||||
break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.elem_ptr => {
|
||||
.elem_ptr => blk: {
|
||||
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
|
||||
const parent = try dg.lowerParentPtr(elem_ptr.array_ptr, base_ty);
|
||||
const parent_llvm_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr, elem_ptr.elem_ty);
|
||||
bitcast_needed = !elem_ptr.elem_ty.eql(ptr_child_ty);
|
||||
|
||||
const llvm_usize = try dg.llvmType(Type.usize);
|
||||
const indices: [2]*const llvm.Value = .{
|
||||
llvm_usize.constInt(0, .False),
|
||||
const indices: [1]*const llvm.Value = .{
|
||||
llvm_usize.constInt(elem_ptr.index, .False),
|
||||
};
|
||||
return ParentPtr{
|
||||
.llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
|
||||
.ty = parent.ty.childType(),
|
||||
};
|
||||
break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
|
||||
},
|
||||
.opt_payload_ptr => {
|
||||
.opt_payload_ptr => blk: {
|
||||
const opt_payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
|
||||
const parent = try dg.lowerParentPtr(opt_payload_ptr.container_ptr, base_ty);
|
||||
const parent_llvm_ptr = try dg.lowerParentPtr(opt_payload_ptr.container_ptr, opt_payload_ptr.container_ty);
|
||||
var buf: Type.Payload.ElemType = undefined;
|
||||
const payload_ty = parent.ty.optionalChild(&buf);
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime() or parent.ty.isPtrLikeOptional()) {
|
||||
|
||||
const payload_ty = opt_payload_ptr.container_ty.optionalChild(&buf);
|
||||
bitcast_needed = !payload_ty.eql(ptr_child_ty);
|
||||
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime() or payload_ty.isPtrLikeOptional()) {
|
||||
// In this case, we represent pointer to optional the same as pointer
|
||||
// to the payload.
|
||||
return ParentPtr{
|
||||
.llvm_ptr = parent.llvm_ptr,
|
||||
.ty = payload_ty,
|
||||
};
|
||||
break :blk parent_llvm_ptr;
|
||||
}
|
||||
|
||||
const llvm_u32 = dg.context.intType(32);
|
||||
@@ -2918,22 +2881,19 @@ pub const DeclGen = struct {
|
||||
llvm_u32.constInt(0, .False),
|
||||
llvm_u32.constInt(0, .False),
|
||||
};
|
||||
return ParentPtr{
|
||||
.llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
|
||||
.ty = payload_ty,
|
||||
};
|
||||
break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
|
||||
},
|
||||
.eu_payload_ptr => {
|
||||
.eu_payload_ptr => blk: {
|
||||
const eu_payload_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
|
||||
const parent = try dg.lowerParentPtr(eu_payload_ptr.container_ptr, base_ty);
|
||||
const payload_ty = parent.ty.errorUnionPayload();
|
||||
const parent_llvm_ptr = try dg.lowerParentPtr(eu_payload_ptr.container_ptr, eu_payload_ptr.container_ty);
|
||||
|
||||
const payload_ty = eu_payload_ptr.container_ty.errorUnionPayload();
|
||||
bitcast_needed = !payload_ty.eql(ptr_child_ty);
|
||||
|
||||
if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
|
||||
// In this case, we represent pointer to error union the same as pointer
|
||||
// to the payload.
|
||||
return ParentPtr{
|
||||
.llvm_ptr = parent.llvm_ptr,
|
||||
.ty = payload_ty,
|
||||
};
|
||||
break :blk parent_llvm_ptr;
|
||||
}
|
||||
|
||||
const llvm_u32 = dg.context.intType(32);
|
||||
@@ -2941,12 +2901,14 @@ pub const DeclGen = struct {
|
||||
llvm_u32.constInt(0, .False),
|
||||
llvm_u32.constInt(1, .False),
|
||||
};
|
||||
return ParentPtr{
|
||||
.llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
|
||||
.ty = payload_ty,
|
||||
};
|
||||
break :blk parent_llvm_ptr.constInBoundsGEP(&indices, indices.len);
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
if (bitcast_needed) {
|
||||
return llvm_ptr.constBitCast((try dg.llvmType(ptr_child_ty)).pointerType(0));
|
||||
} else {
|
||||
return llvm_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -887,7 +887,9 @@ test "peer cast *[N:x]T to *[N]T" {
|
||||
}
|
||||
|
||||
test "peer cast [*:x]T to [*]T" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
fn doTheTest() !void {
|
||||
|
||||
@@ -21,8 +21,47 @@ fn testReinterpretBytesAsInteger() !void {
|
||||
try expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected);
|
||||
}
|
||||
|
||||
test "reinterpret an array over multiple elements, with no well-defined layout" {
|
||||
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
try testReinterpretWithOffsetAndNoWellDefinedLayout();
|
||||
comptime try testReinterpretWithOffsetAndNoWellDefinedLayout();
|
||||
}
|
||||
|
||||
fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {
|
||||
const bytes: ?[5]?u8 = [5]?u8{ 0x12, 0x34, 0x56, 0x78, 0x9a };
|
||||
const ptr = &bytes.?[1];
|
||||
const copy: [4]?u8 = @ptrCast(*const [4]?u8, ptr).*;
|
||||
_ = copy;
|
||||
//try expect(@ptrCast(*align(1)?u8, bytes[1..5]).* == );
|
||||
}
|
||||
|
||||
test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
|
||||
try testReinterpretStructWrappedBytesAsInteger();
|
||||
comptime try testReinterpretStructWrappedBytesAsInteger();
|
||||
}
|
||||
|
||||
fn testReinterpretStructWrappedBytesAsInteger() !void {
|
||||
const S = struct { bytes: [5:0]u8 };
|
||||
const obj = S{ .bytes = "\x12\x34\x56\x78\xab".* };
|
||||
const expected = switch (native_endian) {
|
||||
.Little => 0xab785634,
|
||||
.Big => 0x345678ab,
|
||||
};
|
||||
try expect(@ptrCast(*align(1) const u32, obj.bytes[1..5]).* == expected);
|
||||
}
|
||||
|
||||
test "reinterpret bytes of an array into an extern struct" {
|
||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
|
||||
try testReinterpretBytesAsExternStruct();
|
||||
comptime try testReinterpretBytesAsExternStruct();
|
||||
@@ -42,6 +81,57 @@ fn testReinterpretBytesAsExternStruct() !void {
|
||||
try expect(val == 5);
|
||||
}
|
||||
|
||||
test "reinterpret bytes of an extern struct into another" {
|
||||
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
|
||||
try testReinterpretExternStructAsExternStruct();
|
||||
comptime try testReinterpretExternStructAsExternStruct();
|
||||
}
|
||||
|
||||
fn testReinterpretExternStructAsExternStruct() !void {
|
||||
const S1 = extern struct {
|
||||
a: u8,
|
||||
b: u16,
|
||||
c: u8,
|
||||
};
|
||||
comptime var bytes align(2) = S1{ .a = 0, .b = 0, .c = 5 };
|
||||
|
||||
const S2 = extern struct {
|
||||
a: u32 align(2),
|
||||
c: u8,
|
||||
};
|
||||
var ptr = @ptrCast(*const S2, &bytes);
|
||||
var val = ptr.c;
|
||||
try expect(val == 5);
|
||||
}
|
||||
|
||||
test "lower reinterpreted comptime field ptr" {
|
||||
if (builtin.zig_backend == .stage1) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
|
||||
// Test lowering a field ptr
|
||||
comptime var bytes align(2) = [_]u8{ 1, 2, 3, 4, 5, 6 };
|
||||
const S = extern struct {
|
||||
a: u32 align(2),
|
||||
c: u8,
|
||||
};
|
||||
comptime var ptr = @ptrCast(*const S, &bytes);
|
||||
var val = &ptr.c;
|
||||
try expect(val.* == 5);
|
||||
|
||||
// Test lowering an elem ptr
|
||||
comptime var src_value = S{ .a = 15, .c = 5 };
|
||||
comptime var ptr2 = @ptrCast(*[@sizeOf(S)]u8, &src_value);
|
||||
var val2 = &ptr2[4];
|
||||
try expect(val2.* == 5);
|
||||
}
|
||||
|
||||
test "reinterpret struct field at comptime" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
|
||||
Reference in New Issue
Block a user