diff options
| author | Koakuma <koachan@protonmail.com> | 2022-05-27 19:53:16 +0700 |
|---|---|---|
| committer | Koakuma <koachan@protonmail.com> | 2022-06-06 20:34:53 +0700 |
| commit | 3d662cfaf4579cb01546c3e5123eece70fd60b9a (patch) | |
| tree | 3e955cef4d2ea1f023b667cdf786872f9d0571a9 /src | |
| parent | 093332c02ed828c6c24d6c7e113ae2804ba7e6f3 (diff) | |
| download | zig-3d662cfaf4579cb01546c3e5123eece70fd60b9a.tar.gz zig-3d662cfaf4579cb01546c3e5123eece70fd60b9a.zip | |
stage2: sparc64: Implement airAddSubOverflow
Diffstat (limited to 'src')
| -rw-r--r-- | src/arch/sparc64/CodeGen.zig | 150 | ||||
| -rw-r--r-- | src/arch/sparc64/Emit.zig | 1 | ||||
| -rw-r--r-- | src/arch/sparc64/Mir.zig | 1 |
3 files changed, 143 insertions, 9 deletions
diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 42dfc88254..088ee8dcdb 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -123,6 +123,16 @@ const MCValue = union(enum) { immediate: u64, /// The value is in a target-specific register. register: Register, + /// The value is a tuple { wrapped, overflow } where + /// wrapped is stored in the register and the overflow bit is + /// stored in the C (signed) or V (unsigned) flag of the CCR. + /// + /// This MCValue is only generated by a add_with_overflow or + /// sub_with_overflow instruction operating on 32- or 64-bit values. + register_with_overflow: struct { + reg: Register, + flag: struct { cond: Instruction.ICondition, ccr: Instruction.CCR }, + }, /// The value is in memory at a hard-coded address. /// If the type is a pointer, it means the pointer address is at this memory location. memory: u64, @@ -525,8 +535,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .trunc_float, => @panic("TODO try self.airUnaryMath(inst)"), - .add_with_overflow => @panic("TODO try self.airAddWithOverflow(inst)"), - .sub_with_overflow => @panic("TODO try self.airSubWithOverflow(inst)"), + .add_with_overflow => try self.airAddSubWithOverflow(inst), + .sub_with_overflow => try self.airAddSubWithOverflow(inst), .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"), .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"), @@ -684,6 +694,88 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { } } +fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { + const tag = self.air.instructions.items(.tag)[inst]; + const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; + const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const lhs = try self.resolveInst(extra.lhs); + const rhs = try self.resolveInst(extra.rhs); + const lhs_ty = self.air.typeOf(extra.lhs); + const rhs_ty = self.air.typeOf(extra.rhs); + + switch (lhs_ty.zigTypeTag()) { + .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), + .Int => { + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); + const int_info = lhs_ty.intInfo(self.target.*); + switch (int_info.bits) { + 32, 64 => { + // Only say yes if the operation is + // commutative, i.e. we can swap both of the + // operands + const lhs_immediate_ok = switch (tag) { + .add_with_overflow => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), + .sub_with_overflow => false, + else => unreachable, + }; + const rhs_immediate_ok = switch (tag) { + .add_with_overflow, + .sub_with_overflow, + => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), + else => unreachable, + }; + + const mir_tag: Mir.Inst.Tag = switch (tag) { + .add_with_overflow => .addcc, + .sub_with_overflow => .subcc, + else => unreachable, + }; + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + + const dest = blk: { + if (rhs_immediate_ok) { + break :blk try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, null); + } else if (lhs_immediate_ok) { + // swap lhs and rhs + break :blk try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, null); + } else { + break :blk try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, null); + } + }; + + const cond = switch (int_info.signedness) { + .unsigned => switch (tag) { + .add_with_overflow => Instruction.ICondition.cs, + .sub_with_overflow => Instruction.ICondition.cc, + else => unreachable, + }, + .signed => Instruction.ICondition.vs, + }; + + const ccr = switch (int_info.bits) { + 32 => Instruction.CCR.icc, + 64 => Instruction.CCR.xcc, + else => unreachable, + }; + + break :result MCValue{ .register_with_overflow = .{ + .reg = dest.register, + .flag = .{ .cond = cond, .ccr = ccr }, + } }; + }, + else => return self.fail("TODO overflow operations on other integer sizes", .{}), + } + }, + else => unreachable, + } + }; + return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); +} + fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { const stack_offset = try self.allocMemPtr(inst); return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); @@ -955,13 +1047,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. switch (mc_arg) { .none => continue, - .undef => unreachable, - .immediate => unreachable, - .unreach => unreachable, - .dead => unreachable, - .memory => unreachable, - .compare_flags_signed => unreachable, - .compare_flags_unsigned => unreachable, .register => |reg| { try self.register_manager.getReg(reg, null); try self.genSetReg(arg_ty, reg, arg_mcv); @@ -972,6 +1057,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .ptr_stack_offset => { return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); }, + else => unreachable, } } @@ -1894,6 +1980,7 @@ fn binOpImmediate( const mir_data: Mir.Inst.Data = switch (mir_tag) { .add, + .addcc, .mulx, .subcc, => .{ @@ -2010,6 +2097,7 @@ fn binOpRegister( const mir_data: Mir.Inst.Data = switch (mir_tag) { .add, + .addcc, .mulx, .subcc, => .{ @@ -2473,6 +2561,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, }); }, + .register_with_overflow => unreachable, .memory => |addr| { // The value is in memory at a hard-coded address. // If the type is a pointer, it means the pointer address is at this memory location. @@ -2519,6 +2608,47 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro return self.fail("TODO larger stack offsets", .{}); return self.genStore(reg, .sp, i13, simm13, abi_size); }, + .register_with_overflow => |rwo| { + const reg_lock = self.register_manager.lockReg(rwo.reg); + defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); + + const wrapped_ty = ty.structFieldType(0); + try self.genSetStack(wrapped_ty, stack_offset, .{ .register = rwo.reg }); + + const overflow_bit_ty = ty.structFieldType(1); + const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); + const cond_reg = try self.register_manager.allocReg(null, gp); + + // TODO handle floating point CCRs + assert(rwo.flag.ccr == .xcc or rwo.flag.ccr == .icc); + + _ = try self.addInst(.{ + .tag = .mov, + .data = .{ + .arithmetic_2op = .{ + .is_imm = false, + .rs1 = cond_reg, + .rs2_or_imm = .{ .rs2 = .g0 }, + }, + }, + }); + + _ = try self.addInst(.{ + .tag = .movcc, + .data = .{ + .conditional_move = .{ + .ccr = rwo.flag.ccr, + .cond = .{ .icond = rwo.flag.cond }, + .is_imm = true, + .rd = cond_reg, + .rs2_or_imm = .{ .imm = 1 }, + }, + }, + }); + try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{ + .register = cond_reg, + }); + }, .memory, .stack_offset => { switch (mcv) { .stack_offset => |off| { @@ -2760,6 +2890,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo .dead => unreachable, .compare_flags_unsigned, .compare_flags_signed, + .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), @@ -3100,6 +3231,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type .dead => unreachable, .compare_flags_unsigned, .compare_flags_signed, + .register_with_overflow, => unreachable, // cannot hold an address .immediate => |imm| { try self.setRegOrMem(value_ty, .{ .memory = imm }, value); diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 55de28d4b3..2383e6c146 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -79,6 +79,7 @@ pub fn emitMir( .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(), .add => try emit.mirArithmetic3Op(inst), + .addcc => @panic("TODO implement sparc64 addcc"), .bpr => try emit.mirConditionalBranch(inst), .bpcc => try emit.mirConditionalBranch(inst), diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index dada29ac18..36849eb48a 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -42,6 +42,7 @@ pub const Inst = struct { /// This uses the arithmetic_3op field. // TODO add other operations. add, + addcc, /// A.3 Branch on Integer Register with Prediction (BPr) /// This uses the branch_predict_reg field. |
