Commit 2687b8f7f4
Changed files (16)
src
arch
test
behavior
src/arch/aarch64/CodeGen.zig
@@ -625,7 +625,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.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),
@@ -3472,14 +3473,14 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
-fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
+fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
const vector_ty = self.air.typeOfIndex(inst);
const len = vector_ty.vectorLen();
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
const result: MCValue = res: {
if (self.liveness.isUnused(inst)) break :res MCValue.dead;
- return self.fail("TODO implement airVectorInit for {}", .{self.target.cpu.arch});
+ return self.fail("TODO implement airAggregateInit for {}", .{self.target.cpu.arch});
};
if (elements.len <= Liveness.bpi - 1) {
@@ -3494,6 +3495,13 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
return bt.finishAir(result);
}
+fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
+ _ = extra;
+ return self.fail("TODO implement airUnionInit for aarch64", .{});
+}
+
fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
const prefetch = self.air.instructions.items(.data)[inst].prefetch;
return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
src/arch/arm/CodeGen.zig
@@ -609,7 +609,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.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),
@@ -3937,14 +3938,14 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
-fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
+fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
const vector_ty = self.air.typeOfIndex(inst);
const len = vector_ty.vectorLen();
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
const result: MCValue = res: {
if (self.liveness.isUnused(inst)) break :res MCValue.dead;
- return self.fail("TODO implement airVectorInit for arm", .{});
+ return self.fail("TODO implement airAggregateInit for arm", .{});
};
if (elements.len <= Liveness.bpi - 1) {
@@ -3959,6 +3960,14 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
return bt.finishAir(result);
}
+fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
+ _ = extra;
+
+ return self.fail("TODO implement airUnionInit for arm", .{});
+}
+
fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
const prefetch = self.air.instructions.items(.data)[inst].prefetch;
return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
src/arch/riscv64/CodeGen.zig
@@ -596,7 +596,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.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),
@@ -2157,14 +2158,14 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
-fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
+fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
const vector_ty = self.air.typeOfIndex(inst);
const len = vector_ty.vectorLen();
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
const result: MCValue = res: {
if (self.liveness.isUnused(inst)) break :res MCValue.dead;
- return self.fail("TODO implement airVectorInit for riscv64", .{});
+ return self.fail("TODO implement airAggregateInit for riscv64", .{});
};
if (elements.len <= Liveness.bpi - 1) {
@@ -2179,6 +2180,14 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
return bt.finishAir(result);
}
+fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
+ _ = extra;
+ return self.fail("TODO implement airUnionInit for riscv64", .{});
+ // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
+}
+
fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
const prefetch = self.air.instructions.items(.data)[inst].prefetch;
return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
src/arch/wasm/CodeGen.zig
@@ -1642,7 +1642,8 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.ret_ptr => self.airRetPtr(inst),
.ret_load => self.airRetLoad(inst),
.splat => self.airSplat(inst),
- .vector_init => self.airVectorInit(inst),
+ .aggregate_init => self.airAggregateInit(inst),
+ .union_init => self.airUnionInit(inst),
.prefetch => self.airPrefetch(inst),
.slice => self.airSlice(inst),
@@ -3350,7 +3351,7 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
return self.fail("TODO: Implement wasm airSplat", .{});
}
-fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+fn airAggregateInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
const vector_ty = self.air.typeOfIndex(inst);
@@ -3359,7 +3360,7 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
switch (vector_ty.zigTypeTag()) {
- .Vector => return self.fail("TODO: Wasm backend: implement airVectorInit for vectors", .{}),
+ .Vector => return self.fail("TODO: Wasm backend: implement airAggregateInit for vectors", .{}),
.Array => {
const result = try self.allocStack(vector_ty);
const elem_ty = vector_ty.childType();
@@ -3413,6 +3414,11 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
}
}
+fn airUnionInit(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
+ if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
+ return self.fail("TODO: Wasm backend: implement airUnionInit", .{});
+}
+
fn airPrefetch(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
const prefetch = self.air.instructions.items(.data)[inst].prefetch;
_ = prefetch;
src/arch/x86_64/CodeGen.zig
@@ -708,7 +708,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.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),
@@ -5232,14 +5233,14 @@ fn airSplat(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
-fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
+fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
const vector_ty = self.air.typeOfIndex(inst);
const len = vector_ty.vectorLen();
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
const result: MCValue = res: {
if (self.liveness.isUnused(inst)) break :res MCValue.dead;
- return self.fail("TODO implement airVectorInit for x86_64", .{});
+ return self.fail("TODO implement airAggregateInit for x86_64", .{});
};
if (elements.len <= Liveness.bpi - 1) {
@@ -5254,6 +5255,16 @@ fn airVectorInit(self: *Self, inst: Air.Inst.Index) !void {
return bt.finishAir(result);
}
+fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
+ const result: MCValue = res: {
+ if (self.liveness.isUnused(inst)) break :res MCValue.dead;
+ return self.fail("TODO implement airAggregateInit for x86_64", .{});
+ };
+ return self.finishAir(inst, result, .{ extra.init, .none, .none });
+}
+
fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
const prefetch = self.air.instructions.items(.data)[inst].prefetch;
return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
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 {
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;
src/Air.zig
@@ -561,13 +561,15 @@ pub const Inst = struct {
/// Uses the `un_op` field.
error_name,
- /// Constructs a vector, tuple, or array value out of runtime-known elements.
+ /// Constructs a vector, tuple, struct, or array value out of runtime-known elements.
/// Some of the elements may be comptime-known.
/// Uses the `ty_pl` field, payload is index of an array of elements, each of which
/// is a `Ref`. Length of the array is given by the vector type.
- /// TODO rename this to `aggregate_init` and make it support array values and
- /// struct values too.
- vector_init,
+ aggregate_init,
+
+ /// Constructs a union from a field index and a runtime-known init value.
+ /// Uses the `ty_pl` field with payload `UnionInit`.
+ union_init,
/// Communicates an intent to load memory.
/// Result is always unused.
@@ -768,6 +770,11 @@ pub const AtomicRmw = struct {
}
};
+pub const UnionInit = struct {
+ field_index: u32,
+ init: Inst.Ref,
+};
+
pub fn getMainBody(air: Air) []const Air.Inst.Index {
const body_index = air.extra[@enumToInt(ExtraIndex.main_block)];
const extra = air.extraData(Block, body_index);
@@ -864,7 +871,8 @@ pub fn typeOfIndex(air: Air, inst: Air.Inst.Index) Type {
.cmpxchg_weak,
.cmpxchg_strong,
.slice,
- .vector_init,
+ .aggregate_init,
+ .union_init,
.field_parent_ptr,
=> return air.getRefType(datas[inst].ty_pl.ty),
src/AstGen.zig
@@ -2052,8 +2052,8 @@ fn unusedResultDeferExpr(gz: *GenZir, defer_scope: *Scope.Defer, expr_scope: *Sc
_ = try unusedResultExpr(gz, expr_scope, expr_node);
}
-/// Returns AST source node of the thing that is noreturn if the statement is definitely `noreturn`.
-/// Otherwise returns 0.
+/// Returns AST source node of the thing that is noreturn if the statement is
+/// definitely `noreturn`. Otherwise returns 0.
fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
try emitDbgNode(gz, statement);
// We need to emit an error if the result is not `noreturn` or `void`, but
@@ -2201,7 +2201,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.array_init_anon,
.array_init_ref,
.array_init_anon_ref,
- .union_init_ptr,
+ .union_init,
.field_type,
.field_type_ref,
.error_set_decl,
@@ -6802,40 +6802,17 @@ fn unionInit(
) InnerError!Zir.Inst.Ref {
const union_type = try typeExpr(gz, scope, params[0]);
const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
- switch (rl) {
- .none, .discard, .ref, .ty, .coerced_ty, .inferred_ptr => {
- _ = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{
- .container_type = union_type,
- .field_name = field_name,
- });
- const result = try expr(gz, scope, .{ .ty = union_type }, params[2]);
- return rvalue(gz, rl, result, node);
- },
- .ptr => |result_ptr| {
- return unionInitRlPtr(gz, scope, node, result_ptr, params[2], union_type, field_name);
- },
- .block_ptr => |block_scope| {
- return unionInitRlPtr(gz, scope, node, block_scope.rl_ptr, params[2], union_type, field_name);
- },
- }
-}
-
-fn unionInitRlPtr(
- parent_gz: *GenZir,
- scope: *Scope,
- node: Ast.Node.Index,
- result_ptr: Zir.Inst.Ref,
- expr_node: Ast.Node.Index,
- union_type: Zir.Inst.Ref,
- field_name: Zir.Inst.Ref,
-) InnerError!Zir.Inst.Ref {
- const union_init_ptr = try parent_gz.addPlNode(.union_init_ptr, node, Zir.Inst.UnionInitPtr{
- .result_ptr = result_ptr,
+ const field_type = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{
+ .container_type = union_type,
+ .field_name = field_name,
+ });
+ const init = try reachableExpr(gz, scope, .{ .ty = field_type }, params[2], node);
+ const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{
.union_type = union_type,
+ .init = init,
.field_name = field_name,
});
- // TODO check if we need to do the elision like below in asRlPtr
- return expr(parent_gz, scope, .{ .ptr = union_init_ptr }, expr_node);
+ return rvalue(gz, rl, result, node);
}
fn asRlPtr(
src/Liveness.zig
@@ -25,7 +25,7 @@ tomb_bits: []usize,
/// array. The meaning of the data depends on the AIR tag.
/// * `cond_br` - points to a `CondBr` in `extra` at this index.
/// * `switch_br` - points to a `SwitchBr` in `extra` at this index.
-/// * `asm`, `call`, `vector_init` - the value is a set of bits which are the extra tomb
+/// * `asm`, `call`, `aggregate_init` - the value is a set of bits which are the extra tomb
/// bits of operands.
/// The main tomb bits are still used and the extra ones are starting with the lsb of the
/// value here.
@@ -420,10 +420,10 @@ fn analyzeInst(
}
return extra_tombs.finish();
},
- .vector_init => {
+ .aggregate_init => {
const ty_pl = inst_datas[inst].ty_pl;
- const vector_ty = a.air.getRefType(ty_pl.ty);
- const len = @intCast(usize, vector_ty.arrayLen());
+ const aggregate_ty = a.air.getRefType(ty_pl.ty);
+ const len = @intCast(usize, aggregate_ty.arrayLen());
const elements = @bitCast([]const Air.Inst.Ref, a.air.extra[ty_pl.payload..][0..len]);
if (elements.len <= bpi - 1) {
@@ -442,6 +442,10 @@ fn analyzeInst(
}
return extra_tombs.finish();
},
+ .union_init => {
+ const extra = a.air.extraData(Air.UnionInit, inst_datas[inst].ty_pl.payload).data;
+ return trackOperands(a, new_set, inst, main_tomb, .{ extra.init, .none, .none });
+ },
.struct_field_ptr, .struct_field_val => {
const extra = a.air.extraData(Air.StructField, inst_datas[inst].ty_pl.payload).data;
return trackOperands(a, new_set, inst, main_tomb, .{ extra.struct_operand, .none, .none });
src/Module.zig
@@ -1123,6 +1123,15 @@ pub const Union = struct {
/// undefined until `status` is `have_field_types` or `have_layout`.
ty: Type,
abi_align: Value,
+
+ /// Returns the field alignment, assuming the union is not packed.
+ pub fn normalAlignment(field: Field, target: Target) u32 {
+ if (field.abi_align.tag() == .abi_align_default) {
+ return field.ty.abiAlignment(target);
+ } else {
+ return @intCast(u32, field.abi_align.toUnsignedInt());
+ }
+ }
};
pub const Fields = std.StringArrayHashMapUnmanaged(Field);
src/print_air.zig
@@ -232,7 +232,8 @@ const Writer = struct {
.assembly => try w.writeAssembly(s, inst),
.dbg_stmt => try w.writeDbgStmt(s, inst),
.call => try w.writeCall(s, inst),
- .vector_init => try w.writeVectorInit(s, inst),
+ .aggregate_init => try w.writeAggregateInit(s, inst),
+ .union_init => try w.writeUnionInit(s, inst),
.br => try w.writeBr(s, inst),
.cond_br => try w.writeCondBr(s, inst),
.switch_br => try w.writeSwitchBr(s, inst),
@@ -301,7 +302,7 @@ const Writer = struct {
try s.writeAll("}");
}
- fn writeVectorInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
+ fn writeAggregateInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
const vector_ty = w.air.getRefType(ty_pl.ty);
const len = @intCast(usize, vector_ty.arrayLen());
@@ -315,6 +316,14 @@ const Writer = struct {
try s.writeAll("]");
}
+ fn writeUnionInit(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
+ const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
+ const extra = w.air.extraData(Air.UnionInit, ty_pl.payload).data;
+
+ try s.print("{d}, ", .{extra.field_index});
+ try w.writeOperand(s, inst, 0, extra.init);
+ }
+
fn writeStructField(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const ty_pl = w.air.instructions.items(.data)[inst].ty_pl;
const extra = w.air.extraData(Air.StructField, ty_pl.payload).data;
src/print_zir.zig
@@ -273,7 +273,7 @@ const Writer = struct {
.slice_end => try self.writeSliceEnd(stream, inst),
.slice_sentinel => try self.writeSliceSentinel(stream, inst),
- .union_init_ptr => try self.writeUnionInitPtr(stream, inst),
+ .union_init => try self.writeUnionInit(stream, inst),
.struct_init,
.struct_init_ref,
@@ -692,14 +692,14 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
- fn writeUnionInitPtr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
+ fn writeUnionInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
- const extra = self.code.extraData(Zir.Inst.UnionInitPtr, inst_data.payload_index).data;
- try self.writeInstRef(stream, extra.result_ptr);
- try stream.writeAll(", ");
+ const extra = self.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
try self.writeInstRef(stream, extra.union_type);
try stream.writeAll(", ");
try self.writeInstRef(stream, extra.field_name);
+ try stream.writeAll(", ");
+ try self.writeInstRef(stream, extra.init);
try stream.writeAll(") ");
try self.writeSrc(stream, inst_data.src());
}
src/Sema.zig
@@ -204,7 +204,7 @@ pub const Block = struct {
return block.namespace.file_scope;
}
- pub fn addTy(
+ fn addTy(
block: *Block,
tag: Air.Inst.Tag,
ty: Type,
@@ -215,7 +215,7 @@ pub const Block = struct {
});
}
- pub fn addTyOp(
+ fn addTyOp(
block: *Block,
tag: Air.Inst.Tag,
ty: Type,
@@ -230,7 +230,7 @@ pub const Block = struct {
});
}
- pub fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref {
+ fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref {
return block.addInst(.{
.tag = .bitcast,
.data = .{ .ty_op = .{
@@ -240,14 +240,14 @@ pub const Block = struct {
});
}
- pub fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref {
+ fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref {
return block.addInst(.{
.tag = tag,
.data = .{ .no_op = {} },
});
}
- pub fn addUnOp(
+ fn addUnOp(
block: *Block,
tag: Air.Inst.Tag,
operand: Air.Inst.Ref,
@@ -258,7 +258,7 @@ pub const Block = struct {
});
}
- pub fn addBr(
+ fn addBr(
block: *Block,
target_block: Air.Inst.Index,
operand: Air.Inst.Ref,
@@ -328,7 +328,7 @@ pub const Block = struct {
});
}
- pub fn addStructFieldVal(
+ fn addStructFieldVal(
block: *Block,
struct_val: Air.Inst.Ref,
field_index: u32,
@@ -346,7 +346,7 @@ pub const Block = struct {
});
}
- pub fn addSliceElemPtr(
+ fn addSliceElemPtr(
block: *Block,
slice: Air.Inst.Ref,
elem_index: Air.Inst.Ref,
@@ -364,7 +364,7 @@ pub const Block = struct {
});
}
- pub fn addPtrElemPtr(
+ fn addPtrElemPtr(
block: *Block,
array_ptr: Air.Inst.Ref,
elem_index: Air.Inst.Ref,
@@ -374,7 +374,7 @@ pub const Block = struct {
return block.addPtrElemPtrTypeRef(array_ptr, elem_index, ty_ref);
}
- pub fn addPtrElemPtrTypeRef(
+ fn addPtrElemPtrTypeRef(
block: *Block,
array_ptr: Air.Inst.Ref,
elem_index: Air.Inst.Ref,
@@ -392,7 +392,7 @@ pub const Block = struct {
});
}
- pub fn addVectorInit(
+ fn addAggregateInit(
block: *Block,
vector_ty: Type,
elements: []const Air.Inst.Ref,
@@ -404,7 +404,7 @@ pub const Block = struct {
sema.appendRefsAssumeCapacity(elements);
return block.addInst(.{
- .tag = .vector_init,
+ .tag = .aggregate_init,
.data = .{ .ty_pl = .{
.ty = ty_ref,
.payload = extra_index,
@@ -412,6 +412,24 @@ pub const Block = struct {
});
}
+ fn addUnionInit(
+ block: *Block,
+ union_ty: Type,
+ field_index: u32,
+ init: Air.Inst.Ref,
+ ) !Air.Inst.Ref {
+ return block.addInst(.{
+ .tag = .union_init,
+ .data = .{ .ty_pl = .{
+ .ty = try block.sema.addType(union_ty),
+ .payload = try block.sema.addExtra(Air.UnionInit{
+ .field_index = field_index,
+ .init = init,
+ }),
+ } },
+ });
+ }
+
pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
return Air.indexToRef(try block.addInstAsIndex(inst));
}
@@ -697,7 +715,7 @@ fn analyzeBodyInner(
.array_init_ref => try sema.zirArrayInit(block, inst, true),
.array_init_anon => try sema.zirArrayInitAnon(block, inst, false),
.array_init_anon_ref => try sema.zirArrayInitAnon(block, inst, true),
- .union_init_ptr => try sema.zirUnionInitPtr(block, inst),
+ .union_init => try sema.zirUnionInit(block, inst),
.field_type => try sema.zirFieldType(block, inst),
.field_type_ref => try sema.zirFieldTypeRef(block, inst),
.ptr_to_int => try sema.zirPtrToInt(block, inst),
@@ -11273,10 +11291,31 @@ fn arrayInitEmpty(sema: *Sema, obj_ty: Type) CompileError!Air.Inst.Ref {
}
}
-fn zirUnionInitPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
+fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirUnionInitPtr", .{});
+ const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
+ const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
+ const union_ty = try sema.resolveType(block, ty_src, extra.union_type);
+ const field_name = try sema.resolveConstString(block, field_src, extra.field_name);
+ const init = sema.resolveInst(extra.init);
+ const union_obj = union_ty.cast(Type.Payload.Union).?.data;
+ const field_index_usize = union_obj.fields.getIndex(field_name) orelse
+ return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
+ const field_index = @intCast(u32, field_index_usize);
+
+ if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| {
+ const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
+ return sema.addConstant(
+ union_ty,
+ try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, .val = init_val }),
+ );
+ }
+
+ try sema.requireRuntimeBlock(block, init_src);
+ try sema.resolveTypeLayout(block, ty_src, union_ty);
+ return block.addUnionInit(union_ty, field_index, init);
}
fn zirStructInit(
@@ -11447,7 +11486,7 @@ fn finishStructInit(
}
try sema.requireRuntimeBlock(block, src);
- return block.addVectorInit(struct_ty, field_inits);
+ return block.addAggregateInit(struct_ty, field_inits);
}
fn zirStructInitAnon(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
@@ -11527,7 +11566,7 @@ fn zirArrayInit(
return alloc;
}
- return block.addVectorInit(array_ty, resolved_args);
+ return block.addAggregateInit(array_ty, resolved_args);
}
fn zirArrayInitAnon(
@@ -11593,7 +11632,7 @@ fn zirArrayInitAnon(
element_refs[i] = sema.resolveInst(operand);
}
- return block.addVectorInit(tuple_ty, element_refs);
+ return block.addAggregateInit(tuple_ty, element_refs);
}
fn addConstantMaybeRef(
@@ -11617,31 +11656,47 @@ fn addConstantMaybeRef(
fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- return sema.fail(block, src, "TODO: Sema.zirFieldTypeRef", .{});
+ const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
+ const ty_src = inst_data.src();
+ const field_src = inst_data.src();
+ const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
+ const field_name = try sema.resolveConstString(block, field_src, extra.field_name);
+ return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
}
fn zirFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
- const src = inst_data.src();
+ const ty_src = inst_data.src();
+ const field_src = inst_data.src();
+ const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
const field_name = sema.code.nullTerminatedString(extra.name_start);
- const unresolved_ty = try sema.resolveType(block, src, extra.container_type);
- const resolved_ty = try sema.resolveTypeFields(block, src, unresolved_ty);
+ return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
+}
+
+fn fieldType(
+ sema: *Sema,
+ block: *Block,
+ aggregate_ty: Type,
+ field_name: []const u8,
+ field_src: LazySrcLoc,
+ ty_src: LazySrcLoc,
+) CompileError!Air.Inst.Ref {
+ const resolved_ty = try sema.resolveTypeFields(block, ty_src, aggregate_ty);
switch (resolved_ty.zigTypeTag()) {
.Struct => {
const struct_obj = resolved_ty.castTag(.@"struct").?.data;
const field = struct_obj.fields.get(field_name) orelse
- return sema.failWithBadStructFieldAccess(block, struct_obj, src, field_name);
+ return sema.failWithBadStructFieldAccess(block, struct_obj, field_src, field_name);
return sema.addType(field.ty);
},
.Union => {
const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
const field = union_obj.fields.get(field_name) orelse
- return sema.failWithBadUnionFieldAccess(block, union_obj, src, field_name);
+ return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
return sema.addType(field.ty);
},
- else => return sema.fail(block, src, "expected struct or union; found '{}'", .{
+ else => return sema.fail(block, ty_src, "expected struct or union; found '{}'", .{
resolved_ty,
}),
}
@@ -16396,7 +16451,7 @@ fn coerceArrayLike(
if (runtime_src) |rs| {
try sema.requireRuntimeBlock(block, rs);
- return block.addVectorInit(dest_ty, element_refs);
+ return block.addAggregateInit(dest_ty, element_refs);
}
return sema.addConstant(
@@ -16453,7 +16508,7 @@ fn coerceTupleToArray(
if (runtime_src) |rs| {
try sema.requireRuntimeBlock(block, rs);
- return block.addVectorInit(dest_ty, element_refs);
+ return block.addAggregateInit(dest_ty, element_refs);
}
return sema.addConstant(
src/Zir.zig
@@ -721,10 +721,9 @@ pub const Inst = struct {
/// Anonymous array initialization syntax, make the result a pointer.
/// Uses the `pl_node` field. Payload is `MultiOp`.
array_init_anon_ref,
- /// Given a pointer to a union and a comptime known field name, activates that field
- /// and returns a pointer to it.
- /// Uses the `pl_node` field. Payload is `UnionInitPtr`.
- union_init_ptr,
+ /// Implements the `@unionInit` builtin.
+ /// Uses the `pl_node` field. Payload is `UnionInit`.
+ union_init,
/// Implements the `@typeInfo` builtin. Uses `un_node`.
type_info,
/// Implements the `@sizeOf` builtin. Uses `un_node`.
@@ -1125,7 +1124,7 @@ pub const Inst = struct {
.array_init_anon,
.array_init_ref,
.array_init_anon_ref,
- .union_init_ptr,
+ .union_init,
.field_type,
.field_type_ref,
.int_to_enum,
@@ -1383,7 +1382,7 @@ pub const Inst = struct {
.array_init_anon = .pl_node,
.array_init_ref = .pl_node,
.array_init_anon_ref = .pl_node,
- .union_init_ptr = .pl_node,
+ .union_init = .pl_node,
.type_info = .un_node,
.size_of = .un_node,
.bit_size_of = .un_node,
@@ -2896,10 +2895,10 @@ pub const Inst = struct {
ordering: Ref,
};
- pub const UnionInitPtr = struct {
- result_ptr: Ref,
+ pub const UnionInit = struct {
union_type: Ref,
field_name: Ref,
+ init: Ref,
};
pub const AtomicStore = struct {
test/behavior/union.zig
@@ -785,8 +785,40 @@ test "return union init with void payload" {
comptime try S.entry();
}
+test "@unionInit stored to a const" {
+ if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+ const S = struct {
+ const U = union(enum) {
+ boolean: bool,
+ byte: u8,
+ };
+ fn doTheTest() !void {
+ {
+ var t = true;
+ const u = @unionInit(U, "boolean", t);
+ try expect(u.boolean);
+ }
+ {
+ var byte: u8 = 69;
+ const u = @unionInit(U, "byte", byte);
+ try expect(u.byte == 69);
+ }
+ }
+ };
+
+ comptime try S.doTheTest();
+ try S.doTheTest();
+}
+
test "@unionInit can modify a union type" {
- if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const UnionInitEnum = union(enum) {
Boolean: bool,
@@ -807,7 +839,9 @@ test "@unionInit can modify a union type" {
}
test "@unionInit can modify a pointer value" {
- if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const UnionInitEnum = union(enum) {
Boolean: bool,
@@ -825,7 +859,9 @@ test "@unionInit can modify a pointer value" {
}
test "union no tag with struct member" {
- if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
const Struct = struct {};
const Union = union {