Skip to content

Commit

Permalink
Merge pull request #11128 from topolarity/comptime-memory-reinterp
Browse files Browse the repository at this point in the history
stage2: Track parent type for `.elem_ptr`, `.field_ptr`, and `.*_payload_ptr`
  • Loading branch information
andrewrk authored Mar 15, 2022
2 parents a2a5d3c + 2f92d1a commit 9eceba2
Show file tree
Hide file tree
Showing 9 changed files with 670 additions and 326 deletions.
8 changes: 5 additions & 3 deletions src/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ pub const ErrorSet = struct {
}
};

pub const RequiresComptime = enum { no, yes, unknown, wip };
pub const PropertyBoolean = enum { no, yes, unknown, wip };

/// Represents the data that a struct declaration provides.
pub const Struct = struct {
Expand Down Expand Up @@ -884,7 +884,7 @@ pub const Struct = struct {
/// If false, resolving the fields is necessary to determine whether the type has only
/// one possible value.
known_non_opv: bool,
requires_comptime: RequiresComptime = .unknown,
requires_comptime: PropertyBoolean = .unknown,

pub const Fields = std.StringArrayHashMapUnmanaged(Field);

Expand Down Expand Up @@ -1089,6 +1089,8 @@ pub const EnumFull = struct {
namespace: Namespace,
/// Offset from `owner_decl`, points to the enum decl AST node.
node_offset: i32,
/// true if zig inferred this tag type, false if user specified it
tag_ty_inferred: bool,

pub const NameMap = std.StringArrayHashMapUnmanaged(void);
pub const ValueMap = std.ArrayHashMapUnmanaged(Value, void, Value.ArrayHashContext, false);
Expand Down Expand Up @@ -1132,7 +1134,7 @@ pub const Union = struct {
// which `have_layout` does not ensure.
fully_resolved,
},
requires_comptime: RequiresComptime = .unknown,
requires_comptime: PropertyBoolean = .unknown,

pub const Field = struct {
/// undefined until `status` is `have_field_types` or `have_layout`.
Expand Down
436 changes: 261 additions & 175 deletions src/Sema.zig

Large diffs are not rendered by default.

163 changes: 64 additions & 99 deletions src/codegen/llvm.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -2786,167 +2768,150 @@ 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 },
.data = decl.ty,
};
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,
};
}
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,
};
break :blk parent_llvm_ptr;
}
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, 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);
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 = 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, 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);
const indices: [2]*const llvm.Value = .{
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;
}
}

Expand Down
Loading

0 comments on commit 9eceba2

Please sign in to comment.