diff --git a/src/Air.zig b/src/Air.zig index dc2cd1a9c3..5c8873a199 100644 --- a/src/Air.zig +++ b/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), diff --git a/src/AstGen.zig b/src/AstGen.zig index 45b074a848..b9ac0b5c43 100644 --- a/src/AstGen.zig +++ b/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, - .union_type = union_type, + const field_type = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{ + .container_type = union_type, .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); + 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, + }); + return rvalue(gz, rl, result, node); } fn asRlPtr( diff --git a/src/Liveness.zig b/src/Liveness.zig index f4ee20c49a..1fd6cd91ee 100644 --- a/src/Liveness.zig +++ b/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 }); diff --git a/src/Module.zig b/src/Module.zig index 91386405be..048895e5bf 100644 --- a/src/Module.zig +++ b/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); diff --git a/src/Sema.zig b/src/Sema.zig index cd4c526822..dc79310028 100644 --- a/src/Sema.zig +++ b/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( diff --git a/src/Zir.zig b/src/Zir.zig index 86fe44773e..b70b41e506 100644 --- a/src/Zir.zig +++ b/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 { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 0a3070e881..c9413975e3 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/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 }); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 8f1d3e8be4..f4e97fa8b1 100644 --- a/src/arch/arm/CodeGen.zig +++ b/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 }); diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index 33c5b86351..c654ea625d 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/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 }); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index c99b44e612..e2e6b00e84 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/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; diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index b30a38fc40..47d1e1f4d3 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/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 }); 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; diff --git a/src/print_air.zig b/src/print_air.zig index 06ec464f91..4609c4d148 100644 --- a/src/print_air.zig +++ b/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; diff --git a/src/print_zir.zig b/src/print_zir.zig index 9ef67e3213..f5e6f22e45 100644 --- a/src/print_zir.zig +++ b/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()); } diff --git a/test/behavior/union.zig b/test/behavior/union.zig index be288ee740..91bd783ce2 100644 --- a/test/behavior/union.zig +++ b/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 {