aboutsummaryrefslogtreecommitdiff
path: root/src/codegen
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-09-27 19:48:42 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-09-27 19:53:29 -0700
commitc0aa4a1a42b3e0d312bd274799be67d60a1c0238 (patch)
tree6af32089a5fd3b6f79d7eb8a5fcb26bd0a46c39b /src/codegen
parent25266d08046df6032007b46346faf01a2f40ef31 (diff)
downloadzig-c0aa4a1a42b3e0d312bd274799be67d60a1c0238.tar.gz
zig-c0aa4a1a42b3e0d312bd274799be67d60a1c0238.zip
stage2: implement basic unions
* AIR instructions struct_field_ptr and related functions now are also emitted by the frontend for unions. Backends must inspect the type of the pointer operand to lower the instructions correctly. - These will be renamed to `agg_field_ptr` (short for "aggregate") in the future. * Introduce the new `set_union_tag` AIR instruction. * Introduce `Module.EnumNumbered` and associated `Type` methods. This is for enums which have no decls, but do have the possibility of overriding the integer tag type and tag values. * Sema: Implement support for union tag types in both the auto-generated and explicitly-provided cases, as well as explicitly provided enum tag values in union declarations. * LLVM backend: implement lowering union types, union field pointer instructions, and the new `set_union_tag` instruction.
Diffstat (limited to 'src/codegen')
-rw-r--r--src/codegen/c.zig16
-rw-r--r--src/codegen/llvm.zig80
2 files changed, 90 insertions, 6 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 16b13db292..fc0c86b8f1 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -955,6 +955,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
.atomic_load => try airAtomicLoad(f, inst),
.memset => try airMemset(f, inst),
.memcpy => try airMemcpy(f, inst),
+ .set_union_tag => try airSetUnionTag(f, inst),
.int_to_float,
.float_to_int,
@@ -2080,6 +2081,21 @@ fn airMemcpy(f: *Function, inst: Air.Inst.Index) !CValue {
return CValue.none;
}
+fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
+ const bin_op = f.air.instructions.items(.data)[inst].bin_op;
+ const union_ptr = try f.resolveInst(bin_op.lhs);
+ const new_tag = try f.resolveInst(bin_op.rhs);
+ const writer = f.object.writer();
+
+ try writer.writeAll("*");
+ try f.writeCValue(writer, union_ptr);
+ try writer.writeAll(" = ");
+ try f.writeCValue(writer, new_tag);
+ try writer.writeAll(";\n");
+
+ return CValue.none;
+}
+
fn toMemoryOrder(order: std.builtin.AtomicOrder) [:0]const u8 {
return switch (order) {
.Unordered => "memory_order_relaxed",
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index b15834c963..ab164b5d91 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -735,7 +735,7 @@ pub const DeclGen = struct {
},
.Enum => {
var buffer: Type.Payload.Bits = undefined;
- const int_ty = t.enumTagType(&buffer);
+ const int_ty = t.intTagType(&buffer);
const bit_count = int_ty.intInfo(self.module.getTarget()).bits;
return self.context.intType(bit_count);
},
@@ -812,6 +812,29 @@ pub const DeclGen = struct {
.False,
);
},
+ .Union => {
+ const union_obj = t.castTag(.@"union").?.data;
+ assert(union_obj.haveFieldTypes());
+
+ const enum_tag_ty = union_obj.tag_ty;
+ const enum_tag_llvm_ty = try self.llvmType(enum_tag_ty);
+
+ if (union_obj.onlyTagHasCodegenBits()) {
+ return enum_tag_llvm_ty;
+ }
+
+ const target = self.module.getTarget();
+ const most_aligned_field_index = union_obj.mostAlignedField(target);
+ const most_aligned_field = union_obj.fields.values()[most_aligned_field_index];
+ // TODO handle when the most aligned field is different than the
+ // biggest sized field.
+
+ const llvm_fields = [_]*const llvm.Type{
+ try self.llvmType(most_aligned_field.ty),
+ enum_tag_llvm_ty,
+ };
+ return self.context.structType(&llvm_fields, llvm_fields.len, .False);
+ },
.Fn => {
const ret_ty = try self.llvmType(t.fnReturnType());
const params_len = t.fnParamLen();
@@ -840,7 +863,6 @@ pub const DeclGen = struct {
.BoundFn => @panic("TODO remove BoundFn from the language"),
- .Union,
.Opaque,
.Frame,
.AnyFrame,
@@ -1131,7 +1153,7 @@ pub const DeclGen = struct {
var buffer: Type.Payload.Bits = undefined;
const int_ty = switch (ty.zigTypeTag()) {
.Int => ty,
- .Enum => ty.enumTagType(&buffer),
+ .Enum => ty.intTagType(&buffer),
.Float => {
if (!is_rmw_xchg) return null;
return dg.context.intType(@intCast(c_uint, ty.abiSize(target) * 8));
@@ -1281,6 +1303,7 @@ pub const FuncGen = struct {
.atomic_load => try self.airAtomicLoad(inst),
.memset => try self.airMemset(inst),
.memcpy => try self.airMemcpy(inst),
+ .set_union_tag => try self.airSetUnionTag(inst),
.atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
.atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
@@ -1381,7 +1404,7 @@ pub const FuncGen = struct {
const int_ty = switch (operand_ty.zigTypeTag()) {
.Enum => blk: {
var buffer: Type.Payload.Bits = undefined;
- const int_ty = operand_ty.enumTagType(&buffer);
+ const int_ty = operand_ty.intTagType(&buffer);
break :blk int_ty;
},
.Int, .Bool, .Pointer, .ErrorSet => operand_ty,
@@ -1660,8 +1683,9 @@ pub const FuncGen = struct {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
const struct_ptr = try self.resolveInst(struct_field.struct_operand);
+ const struct_ptr_ty = self.air.typeOf(struct_field.struct_operand);
const field_index = @intCast(c_uint, struct_field.field_index);
- return self.builder.buildStructGEP(struct_ptr, field_index, "");
+ return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index);
}
fn airStructFieldPtrIndex(self: *FuncGen, inst: Air.Inst.Index, field_index: c_uint) !?*const llvm.Value {
@@ -1670,7 +1694,8 @@ pub const FuncGen = struct {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
const struct_ptr = try self.resolveInst(ty_op.operand);
- return self.builder.buildStructGEP(struct_ptr, field_index, "");
+ const struct_ptr_ty = self.air.typeOf(ty_op.operand);
+ return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index);
}
fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
@@ -2521,6 +2546,49 @@ pub const FuncGen = struct {
return null;
}
+ fn airSetUnionTag(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const union_ptr = try self.resolveInst(bin_op.lhs);
+ // TODO handle when onlyTagHasCodegenBits() == true
+ const new_tag = try self.resolveInst(bin_op.rhs);
+ const tag_field_ptr = self.builder.buildStructGEP(union_ptr, 1, "");
+
+ _ = self.builder.buildStore(new_tag, tag_field_ptr);
+ return null;
+ }
+
+ fn fieldPtr(
+ self: *FuncGen,
+ inst: Air.Inst.Index,
+ struct_ptr: *const llvm.Value,
+ struct_ptr_ty: Type,
+ field_index: c_uint,
+ ) !?*const llvm.Value {
+ const struct_ty = struct_ptr_ty.childType();
+ switch (struct_ty.zigTypeTag()) {
+ .Struct => return self.builder.buildStructGEP(struct_ptr, field_index, ""),
+ .Union => return self.unionFieldPtr(inst, struct_ptr, struct_ty, field_index),
+ else => unreachable,
+ }
+ }
+
+ fn unionFieldPtr(
+ self: *FuncGen,
+ inst: Air.Inst.Index,
+ union_ptr: *const llvm.Value,
+ union_ty: Type,
+ field_index: c_uint,
+ ) !?*const llvm.Value {
+ const union_obj = union_ty.cast(Type.Payload.Union).?.data;
+ const field = &union_obj.fields.values()[field_index];
+ const result_llvm_ty = try self.dg.llvmType(self.air.typeOfIndex(inst));
+ if (!field.ty.hasCodeGenBits()) {
+ return null;
+ }
+ const union_field_ptr = self.builder.buildStructGEP(union_ptr, 0, "");
+ return self.builder.buildBitCast(union_field_ptr, result_llvm_ty, "");
+ }
+
fn getIntrinsic(self: *FuncGen, name: []const u8) *const llvm.Value {
const id = llvm.lookupIntrinsicID(name.ptr, name.len);
assert(id != 0);