diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2022-02-28 17:42:59 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-02-28 17:42:59 +0100 |
| commit | 331cc810ded2058f2e2767b0485eb18d888a45e5 (patch) | |
| tree | 9e5ba37e5b06eaccf5ff36976955a32cda5ddfc4 /src | |
| parent | 90059a12e0ffe433132450f9f43221a198a22106 (diff) | |
| parent | 16f9774d2d6f358c97637e35609dfe0fc14cb501 (diff) | |
| download | zig-331cc810ded2058f2e2767b0485eb18d888a45e5.tar.gz zig-331cc810ded2058f2e2767b0485eb18d888a45e5.zip | |
Merge pull request #11012 from ziglang/x64-union-tag
stage2,x64: basic (un)tagged unions
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/x86_64/CodeGen.zig | 142 | ||||
| -rw-r--r-- | src/arch/x86_64/Emit.zig | 6 | ||||
| -rw-r--r-- | src/codegen.zig | 70 |
3 files changed, 169 insertions, 49 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 47d1e1f4d3..45d9767320 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2098,17 +2098,72 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { const bin_op = self.air.instructions.items(.data)[inst].bin_op; - _ = bin_op; - return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}); + const ptr_ty = self.air.typeOf(bin_op.lhs); + const union_ty = ptr_ty.childType(); + const tag_ty = self.air.typeOf(bin_op.rhs); + const layout = union_ty.unionGetLayout(self.target.*); + + if (layout.tag_size == 0) { + return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); + } + + const ptr = try self.resolveInst(bin_op.lhs); + ptr.freezeIfRegister(&self.register_manager); + defer ptr.unfreezeIfRegister(&self.register_manager); + + const tag = try self.resolveInst(bin_op.rhs); + tag.freezeIfRegister(&self.register_manager); + defer tag.unfreezeIfRegister(&self.register_manager); + + const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: { + // TODO reusing the operand + const reg = try self.copyToTmpRegister(ptr_ty, ptr); + try self.genBinMathOpMir(.add, ptr_ty, .{ .register = reg }, .{ .immediate = layout.payload_size }); + break :blk MCValue{ .register = reg }; + } else ptr; + + try self.store(adjusted_ptr, tag, ptr_ty, tag_ty); + + return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); } fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) - .dead - else - return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); + if (self.liveness.isUnused(inst)) { + return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); + } + + const tag_ty = self.air.typeOfIndex(inst); + const union_ty = self.air.typeOf(ty_op.operand); + const layout = union_ty.unionGetLayout(self.target.*); + + if (layout.tag_size == 0) { + return self.finishAir(inst, .none, .{ ty_op.operand, .none, .none }); + } + + // TODO reusing the operand + const operand = try self.resolveInst(ty_op.operand); + operand.freezeIfRegister(&self.register_manager); + defer operand.unfreezeIfRegister(&self.register_manager); + + const tag_abi_size = tag_ty.abiSize(self.target.*); + const offset: i32 = if (layout.tag_align < layout.payload_align) @intCast(i32, layout.payload_size) else 0; + const dst_mcv: MCValue = blk: { + switch (operand) { + .stack_offset => |off| { + if (tag_abi_size <= 8) { + break :blk try self.copyToRegisterWithInstTracking(inst, tag_ty, .{ + .stack_offset = off - offset, + }); + } + + return self.fail("TODO implement get_union_tag for ABI larger than 8 bytes and operand {}", .{operand}); + }, + else => return self.fail("TODO implement get_union_tag for {}", .{operand}), + } + }; + + return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); } fn airClz(self: *Self, inst: Air.Inst.Index) !void { @@ -2429,8 +2484,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type }); }, .stack_offset => { - const tmp_reg = try self.copyToTmpRegister(value_ty, value); - return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); + if (abi_size <= 8) { + const tmp_reg = try self.copyToTmpRegister(value_ty, value); + return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); + } + + try self.genInlineMemcpy(0, value_ty, value, .{ + .source_stack_base = .rbp, + .dest_stack_base = reg.to64(), + }); }, else => |other| { return self.fail("TODO implement set pointee with {}", .{other}); @@ -3905,36 +3967,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u .dead, .unreach => unreachable, .immediate => |imm| { _ = try self.addInst(.{ - .tag = .@"test", + .tag = .xor, .ops = (Mir.Ops{ .reg1 = registerAlias(cond_reg, abi_size), }).encode(), .data = .{ .imm = @intCast(u32, imm) }, }); - return self.addInst(.{ - .tag = .cond_jmp_eq_ne, - .ops = (Mir.Ops{ - .flags = 0b00, - }).encode(), - .data = .{ .inst = undefined }, - }); }, .register => |reg| { _ = try self.addInst(.{ - .tag = .@"test", + .tag = .xor, .ops = (Mir.Ops{ .reg1 = registerAlias(cond_reg, abi_size), .reg2 = registerAlias(reg, abi_size), }).encode(), .data = undefined, }); - return self.addInst(.{ - .tag = .cond_jmp_eq_ne, - .ops = (Mir.Ops{ - .flags = 0b00, - }).encode(), - .data = .{ .inst = undefined }, - }); }, .stack_offset => { if (abi_size <= 8) { @@ -3948,6 +3996,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u return self.fail("TODO implement switch mir when case is {}", .{case}); }, } + + _ = try self.addInst(.{ + .tag = .@"test", + .ops = (Mir.Ops{ + .reg1 = registerAlias(cond_reg, abi_size), + .reg2 = registerAlias(cond_reg, abi_size), + }).encode(), + .data = undefined, + }); + return self.addInst(.{ + .tag = .cond_jmp_eq_ne, + .ops = (Mir.Ops{ + .flags = 0b00, + }).encode(), + .data = .{ .inst = undefined }, + }); }, .stack_offset => { try self.spillCompareFlagsIfOccupied(); @@ -5408,24 +5472,14 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } switch (typed_value.ty.zigTypeTag()) { - .Array => { - return self.lowerUnnamedConst(typed_value); - }, .Pointer => switch (typed_value.ty.ptrSize()) { - .Slice => { - return self.lowerUnnamedConst(typed_value); - }, + .Slice => {}, else => { switch (typed_value.val.tag()) { .int_u64 => { return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; }, - .slice => { - return self.lowerUnnamedConst(typed_value); - }, - else => { - return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()}); - }, + else => {}, } }, }, @@ -5434,10 +5488,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { if (info.bits <= ptr_bits and info.signedness == .signed) { return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt()) }; } - if (info.bits > ptr_bits or info.signedness == .signed) { - return self.fail("TODO const int bigger than ptr and signed int", .{}); + if (!(info.bits > ptr_bits or info.signedness == .signed)) { + return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; } - return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; }, .Bool => { return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; @@ -5457,7 +5510,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } else if (typed_value.ty.abiSize(self.target.*) == 1) { return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; } - return self.fail("TODO non pointer optionals", .{}); }, .Enum => { if (typed_value.val.castTag(.enum_field_index)) |field_index| { @@ -5504,13 +5556,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); } } - return self.lowerUnnamedConst(typed_value); - }, - .Struct => { - return self.lowerUnnamedConst(typed_value); }, - else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), + else => {}, } + + return self.lowerUnnamedConst(typed_value); } const CallMCValues = struct { diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index fc43e61c17..f4d365dc4f 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1859,6 +1859,9 @@ fn lowerToRmEnc( switch (reg_or_mem) { .register => |src_reg| { const encoder = try Encoder.init(code, 4); + if (reg.size() == 16) { + encoder.prefix16BitMode(); + } encoder.rex(.{ .w = setRexWRegister(reg) or setRexWRegister(src_reg), .r = reg.isExtended(), @@ -1902,6 +1905,9 @@ fn lowerToMrEnc( switch (reg_or_mem) { .register => |dst_reg| { const encoder = try Encoder.init(code, 3); + if (dst_reg.size() == 16) { + encoder.prefix16BitMode(); + } encoder.rex(.{ .w = setRexWRegister(dst_reg) or setRexWRegister(reg), .r = reg.isExtended(), diff --git a/src/codegen.zig b/src/codegen.zig index 26a4478fb2..2484cb0e59 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -466,10 +466,74 @@ pub fn generateSymbol( return Result{ .appended = {} }; }, .Union => { - // TODO generateSymbol for unions + // TODO generate debug info for unions const target = bin_file.options.target; - const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); - try code.writer().writeByteNTimes(0xaa, abi_size); + const union_obj = typed_value.val.castTag(.@"union").?.data; + const layout = typed_value.ty.unionGetLayout(target); + + if (layout.payload_size == 0) { + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ + .ty = typed_value.ty.unionTagType().?, + .val = union_obj.tag, + }, code, debug_output)) { + .appended => {}, + .externally_managed => |external_slice| { + code.appendSliceAssumeCapacity(external_slice); + }, + .fail => |em| return Result{ .fail = em }, + } + } + + // Check if we should store the tag first. + if (layout.tag_align >= layout.payload_align) { + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ + .ty = typed_value.ty.unionTagType().?, + .val = union_obj.tag, + }, code, debug_output)) { + .appended => {}, + .externally_managed => |external_slice| { + code.appendSliceAssumeCapacity(external_slice); + }, + .fail => |em| return Result{ .fail = em }, + } + } + + const union_ty = typed_value.ty.cast(Type.Payload.Union).?.data; + const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag).?; + assert(union_ty.haveFieldTypes()); + const field_ty = union_ty.fields.values()[field_index].ty; + if (!field_ty.hasRuntimeBits()) { + try code.writer().writeByteNTimes(0xaa, try math.cast(usize, layout.payload_size)); + } else { + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ + .ty = field_ty, + .val = union_obj.val, + }, code, debug_output)) { + .appended => {}, + .externally_managed => |external_slice| { + code.appendSliceAssumeCapacity(external_slice); + }, + .fail => |em| return Result{ .fail = em }, + } + + const padding = try math.cast(usize, layout.payload_size - field_ty.abiSize(target)); + if (padding > 0) { + try code.writer().writeByteNTimes(0, padding); + } + } + + if (layout.tag_size > 0) { + switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{ + .ty = union_ty.tag_ty, + .val = union_obj.tag, + }, code, debug_output)) { + .appended => {}, + .externally_managed => |external_slice| { + code.appendSliceAssumeCapacity(external_slice); + }, + .fail => |em| return Result{ .fail = em }, + } + } return Result{ .appended = {} }; }, |
