aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/llvm.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/codegen/llvm.zig')
-rw-r--r--src/codegen/llvm.zig157
1 files changed, 121 insertions, 36 deletions
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 23fa19d9cb..8beb4dd206 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -1329,32 +1329,25 @@ 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 => {
- const field_ptr = tv.val.castTag(.field_ptr).?.data;
- const parent_ptr = try dg.lowerParentPtr(field_ptr.container_ptr);
- const llvm_u32 = dg.context.intType(32);
- const indices: [2]*const llvm.Value = .{
- llvm_u32.constInt(0, .False),
- llvm_u32.constInt(field_ptr.field_index, .False),
- };
- const uncasted = parent_ptr.constInBoundsGEP(&indices, indices.len);
- return uncasted.constBitCast(try dg.llvmType(tv.ty));
+ .field_ptr, .opt_payload_ptr, .eu_payload_ptr => {
+ const parent = try dg.lowerParentPtr(tv.val);
+ return parent.llvm_ptr.constBitCast(try dg.llvmType(tv.ty));
},
.elem_ptr => {
const elem_ptr = tv.val.castTag(.elem_ptr).?.data;
- const parent_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr);
+ const parent = try dg.lowerParentPtr(elem_ptr.array_ptr);
const llvm_usize = try dg.llvmType(Type.usize);
- if (parent_ptr.typeOf().getElementType().getTypeKind() == .Array) {
+ 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_ptr.constInBoundsGEP(&indices, indices.len);
+ return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
} else {
const indices: [1]*const llvm.Value = .{
llvm_usize.constInt(elem_ptr.index, .False),
};
- return parent_ptr.constInBoundsGEP(&indices, indices.len);
+ return parent.llvm_ptr.constInBoundsGEP(&indices, indices.len);
}
},
.null_value, .zero => {
@@ -1800,11 +1793,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) Error!ParentPtr {
decl.markAlive();
var ptr_ty_payload: Type.Payload.ElemType = .{
.base = .{ .tag = .single_mut_pointer },
@@ -1818,42 +1807,134 @@ pub const DeclGen = struct {
};
}
- fn lowerParentPtr(dg: *DeclGen, ptr_val: Value) Error!*const llvm.Value {
+ fn lowerParentPtr(dg: *DeclGen, ptr_val: Value) Error!ParentPtr {
switch (ptr_val.tag()) {
.decl_ref_mut => {
const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
- return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr;
+ return dg.lowerParentPtrDecl(ptr_val, decl);
},
.decl_ref => {
const decl = ptr_val.castTag(.decl_ref).?.data;
- return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr;
+ return dg.lowerParentPtrDecl(ptr_val, decl);
},
.variable => {
const decl = ptr_val.castTag(.variable).?.data.owner_decl;
- return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr;
+ return dg.lowerParentPtrDecl(ptr_val, decl);
},
.field_ptr => {
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
- const parent_ptr = try dg.lowerParentPtr(field_ptr.container_ptr);
+ const parent = try dg.lowerParentPtr(field_ptr.container_ptr);
+ const field_index = @intCast(u32, field_ptr.field_index);
const llvm_u32 = dg.context.intType(32);
- const indices: [2]*const llvm.Value = .{
- llvm_u32.constInt(0, .False),
- llvm_u32.constInt(field_ptr.field_index, .False),
- };
- return parent_ptr.constInBoundsGEP(&indices, indices.len);
+ const target = dg.module.getTarget();
+ switch (parent.ty.zigTypeTag()) {
+ .Union => {
+ const fields = parent.ty.unionFields();
+ const layout = parent.ty.unionGetLayout(target);
+ const field_ty = fields.values()[field_index].ty;
+ 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,
+ };
+ }
+ const llvm_pl_index = @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,
+ };
+ },
+ .Struct => {
+ var ty_buf: Type.Payload.Pointer = undefined;
+ 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),
+ };
+ },
+ else => unreachable,
+ }
},
.elem_ptr => {
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
- const parent_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr);
+ const parent = try dg.lowerParentPtr(elem_ptr.array_ptr);
const llvm_usize = try dg.llvmType(Type.usize);
const indices: [2]*const llvm.Value = .{
llvm_usize.constInt(0, .False),
llvm_usize.constInt(elem_ptr.index, .False),
};
- return parent_ptr.constInBoundsGEP(&indices, indices.len);
+ return ParentPtr{
+ .llvm_ptr = parent.llvm_ptr.constInBoundsGEP(&indices, indices.len),
+ .ty = parent.ty.childType(),
+ };
+ },
+ .opt_payload_ptr => {
+ const opt_payload_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
+ const parent = try dg.lowerParentPtr(opt_payload_ptr);
+ var buf: Type.Payload.ElemType = undefined;
+ const payload_ty = parent.ty.optionalChild(&buf);
+ if (!payload_ty.hasRuntimeBits() or parent.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,
+ };
+ }
+
+ 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,
+ };
+ },
+ .eu_payload_ptr => {
+ const eu_payload_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
+ const parent = try dg.lowerParentPtr(eu_payload_ptr);
+ const payload_ty = parent.ty.errorUnionPayload();
+ if (!payload_ty.hasRuntimeBits()) {
+ // 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,
+ };
+ }
+
+ 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,
+ };
},
- .opt_payload_ptr => return dg.todo("implement lowerParentPtr for optional payload", .{}),
- .eu_payload_ptr => return dg.todo("implement lowerParentPtr for error union payload", .{}),
else => unreachable,
}
}
@@ -3142,7 +3223,9 @@ pub const FuncGen = struct {
const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
_ = self.builder.buildStore(non_null_bit, non_null_ptr);
}
- // Then return the payload pointer.
+ // Then return the payload pointer (only if it's used).
+ if (self.liveness.isUnused(inst))
+ return null;
const indices: [2]*const llvm.Value = .{
index_type.constNull(), // dereference the pointer
index_type.constNull(), // first field is the payload
@@ -3236,7 +3319,9 @@ pub const FuncGen = struct {
const non_null_ptr = self.builder.buildInBoundsGEP(operand, &indices, indices.len, "");
_ = self.builder.buildStore(non_error_val, non_null_ptr);
}
- // Then return the payload pointer.
+ // Then return the payload pointer (only if it is used).
+ if (self.liveness.isUnused(inst))
+ return null;
const indices: [2]*const llvm.Value = .{
index_type.constNull(), // dereference the pointer
index_type.constInt(1, .False), // second field is the payload
@@ -5257,7 +5342,7 @@ fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.Ca
}
/// Take into account 0 bit fields. Returns null if an llvm field could not be found. This only
-/// happends if you want the field index of a zero sized field at the end of the struct.
+/// happens if you want the field index of a zero sized field at the end of the struct.
fn llvmFieldIndex(
ty: Type,
field_index: u32,