aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-02-26 20:59:36 -0700
committerAndrew Kelley <andrew@ziglang.org>2022-02-26 20:59:36 -0700
commit2687b8f7f4825c9018af6998e1de140e6185f9cd (patch)
tree4bbd5f609153618e883ad4cce09c7a8c920a22b0 /src/codegen
parent32e89a98d82c0f4505a3f3d4cd72e7db2eadfbb9 (diff)
downloadzig-2687b8f7f4825c9018af6998e1de140e6185f9cd.tar.gz
zig-2687b8f7f4825c9018af6998e1de140e6185f9cd.zip
stage2: implement `@unionInit`
The ZIR instruction `union_init_ptr` is renamed to `union_init`. I made it always use by-value semantics for now, not taking the time to invest in result location semantics, in case we decide to change the rules for unions. This way is much simpler. There is a new AIR instruction: union_init. This is for a comptime known tag, runtime-known field value. vector_init is renamed to aggregate_init, which solves a TODO comment.
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/c.zig22
-rw-r--r--src/codegen/llvm.zig108
2 files changed, 125 insertions, 5 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 2a0751a202..f09acaa47c 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -1713,7 +1713,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.tag_name => try airTagName(f, inst),
.error_name => try airErrorName(f, inst),
.splat => try airSplat(f, inst),
- .vector_init => try airVectorInit(f, inst),
+ .aggregate_init => try airAggregateInit(f, inst),
+ .union_init => try airUnionInit(f, inst),
.prefetch => try airPrefetch(f, inst),
.int_to_float,
@@ -3526,7 +3527,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue {
return f.fail("TODO: C backend: implement airSplat", .{});
}
-fn airVectorInit(f: *Function, inst: Air.Inst.Index) !CValue {
+fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue {
if (f.liveness.isUnused(inst)) return CValue.none;
const inst_ty = f.air.typeOfIndex(inst);
@@ -3541,7 +3542,22 @@ fn airVectorInit(f: *Function, inst: Air.Inst.Index) !CValue {
_ = elements;
_ = local;
- return f.fail("TODO: C backend: implement airVectorInit", .{});
+ return f.fail("TODO: C backend: implement airAggregateInit", .{});
+}
+
+fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue {
+ if (f.liveness.isUnused(inst)) return CValue.none;
+
+ const inst_ty = f.air.typeOfIndex(inst);
+ const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
+
+ const writer = f.object.writer();
+ const local = try f.allocLocal(inst_ty, .Const);
+ try writer.writeAll(" = ");
+
+ _ = local;
+ _ = ty_pl;
+ return f.fail("TODO: C backend: implement airUnionInit", .{});
}
fn airPrefetch(f: *Function, inst: Air.Inst.Index) !CValue {
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index bc5f5ca7c7..c0642a59de 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -2175,7 +2175,8 @@ pub const FuncGen = struct {
.tag_name => try self.airTagName(inst),
.error_name => try self.airErrorName(inst),
.splat => try self.airSplat(inst),
- .vector_init => try self.airVectorInit(inst),
+ .aggregate_init => try self.airAggregateInit(inst),
+ .union_init => try self.airUnionInit(inst),
.prefetch => try self.airPrefetch(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
@@ -4608,7 +4609,7 @@ pub const FuncGen = struct {
return self.builder.buildShuffleVector(op_vector, undef_vector, mask_llvm_ty.constNull(), "");
}
- fn airVectorInit(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ fn airAggregateInit(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
if (self.liveness.isUnused(inst)) return null;
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
@@ -4693,6 +4694,109 @@ pub const FuncGen = struct {
}
}
+ fn airUnionInit(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ if (self.liveness.isUnused(inst)) return null;
+
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
+ const union_ty = self.air.typeOfIndex(inst);
+ const union_llvm_ty = try self.dg.llvmType(union_ty);
+ const target = self.dg.module.getTarget();
+ const layout = union_ty.unionGetLayout(target);
+ if (layout.payload_size == 0) {
+ if (layout.tag_size == 0) {
+ return null;
+ }
+ assert(!isByRef(union_ty));
+ return union_llvm_ty.constInt(extra.field_index, .False);
+ }
+ assert(isByRef(union_ty));
+ // The llvm type of the alloca will the the named LLVM union type, which will not
+ // necessarily match the format that we need, depending on which tag is active. We
+ // must construct the correct unnamed struct type here and bitcast, in order to
+ // then set the fields appropriately.
+ const result_ptr = self.buildAlloca(union_llvm_ty);
+ const llvm_payload = try self.resolveInst(extra.init);
+ const union_obj = union_ty.cast(Type.Payload.Union).?.data;
+ assert(union_obj.haveFieldTypes());
+ const field = union_obj.fields.values()[extra.field_index];
+ const field_llvm_ty = try self.dg.llvmType(field.ty);
+ const tag_llvm_ty = try self.dg.llvmType(union_obj.tag_ty);
+ const field_size = field.ty.abiSize(target);
+ const field_align = field.normalAlignment(target);
+
+ const llvm_union_ty = t: {
+ const payload = p: {
+ if (!field.ty.hasRuntimeBits()) {
+ const padding_len = @intCast(c_uint, layout.payload_size);
+ break :p self.context.intType(8).arrayType(padding_len);
+ }
+ if (field_size == layout.payload_size) {
+ break :p field_llvm_ty;
+ }
+ const padding_len = @intCast(c_uint, layout.payload_size - field_size);
+ const fields: [2]*const llvm.Type = .{
+ field_llvm_ty, self.context.intType(8).arrayType(padding_len),
+ };
+ break :p self.context.structType(&fields, fields.len, .False);
+ };
+ if (layout.tag_size == 0) {
+ const fields: [1]*const llvm.Type = .{payload};
+ break :t self.context.structType(&fields, fields.len, .False);
+ }
+ var fields: [2]*const llvm.Type = undefined;
+ if (layout.tag_align >= layout.payload_align) {
+ fields = .{ tag_llvm_ty, payload };
+ } else {
+ fields = .{ payload, tag_llvm_ty };
+ }
+ break :t self.context.structType(&fields, fields.len, .False);
+ };
+
+ const casted_ptr = self.builder.buildBitCast(result_ptr, llvm_union_ty.pointerType(0), "");
+
+ // Now we follow the layout as expressed above with GEP instructions to set the
+ // tag and the payload.
+ const index_type = self.context.intType(32);
+
+ if (layout.tag_size == 0) {
+ const indices: [3]*const llvm.Value = .{
+ index_type.constNull(),
+ index_type.constNull(),
+ index_type.constNull(),
+ };
+ const len: c_uint = if (field_size == layout.payload_size) 2 else 3;
+ const field_ptr = self.builder.buildInBoundsGEP(casted_ptr, &indices, len, "");
+ const store_inst = self.builder.buildStore(llvm_payload, field_ptr);
+ store_inst.setAlignment(field_align);
+ return result_ptr;
+ }
+
+ {
+ const indices: [3]*const llvm.Value = .{
+ index_type.constNull(),
+ index_type.constInt(@boolToInt(layout.tag_align >= layout.payload_align), .False),
+ index_type.constNull(),
+ };
+ const len: c_uint = if (field_size == layout.payload_size) 2 else 3;
+ const field_ptr = self.builder.buildInBoundsGEP(casted_ptr, &indices, len, "");
+ const store_inst = self.builder.buildStore(llvm_payload, field_ptr);
+ store_inst.setAlignment(field_align);
+ }
+ {
+ const indices: [2]*const llvm.Value = .{
+ index_type.constNull(),
+ index_type.constInt(@boolToInt(layout.tag_align < layout.payload_align), .False),
+ };
+ const field_ptr = self.builder.buildInBoundsGEP(casted_ptr, &indices, indices.len, "");
+ const llvm_tag = union_llvm_ty.constInt(extra.field_index, .False);
+ const store_inst = self.builder.buildStore(llvm_tag, field_ptr);
+ store_inst.setAlignment(union_obj.tag_ty.abiAlignment(target));
+ }
+
+ return result_ptr;
+ }
+
fn airPrefetch(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
const prefetch = self.air.instructions.items(.data)[inst].prefetch;