authorgravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-05-27 19:53:16+07:00
committergravatar for koachan@protonmail.comKoakuma <koachan@protonmail.com> 2022-06-06 20:34:53+07:00
log3d662cfaf4579cb01546c3e5123eece70fd60b9a
tree3e955cef4d2ea1f023b667cdf786872f9d0571a9
parent093332c02ed828c6c24d6c7e113ae2804ba7e6f3

stage2: sparc64: Implement airAddSubOverflow


3 files changed, 143 insertions(+), 9 deletions(-)

src/arch/sparc64/CodeGen.zig+141-9
...@@ -123,6 +123,16 @@ const MCValue = union(enum) {...@@ -123,6 +123,16 @@ const MCValue = union(enum) {
123 immediate: u64,123 immediate: u64,
124 /// The value is in a target-specific register.124 /// The value is in a target-specific register.
125 register: Register,125 register: Register,
126 /// The value is a tuple { wrapped, overflow } where
127 /// wrapped is stored in the register and the overflow bit is
128 /// stored in the C (signed) or V (unsigned) flag of the CCR.
129 ///
130 /// This MCValue is only generated by a add_with_overflow or
131 /// sub_with_overflow instruction operating on 32- or 64-bit values.
132 register_with_overflow: struct {
133 reg: Register,
134 flag: struct { cond: Instruction.ICondition, ccr: Instruction.CCR },
135 },
126 /// The value is in memory at a hard-coded address.136 /// The value is in memory at a hard-coded address.
127 /// If the type is a pointer, it means the pointer address is at this memory location.137 /// If the type is a pointer, it means the pointer address is at this memory location.
128 memory: u64,138 memory: u64,
...@@ -525,8 +535,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -525,8 +535,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
525 .trunc_float,535 .trunc_float,
526 => @panic("TODO try self.airUnaryMath(inst)"),536 => @panic("TODO try self.airUnaryMath(inst)"),
527537
528 .add_with_overflow => @panic("TODO try self.airAddWithOverflow(inst)"),538 .add_with_overflow => try self.airAddSubWithOverflow(inst),
529 .sub_with_overflow => @panic("TODO try self.airSubWithOverflow(inst)"),539 .sub_with_overflow => try self.airAddSubWithOverflow(inst),
530 .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"),540 .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"),
531 .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"),541 .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"),
532542
...@@ -684,6 +694,88 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {...@@ -684,6 +694,88 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
684 }694 }
685}695}
686696
697fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
698 const tag = self.air.instructions.items(.tag)[inst];
699 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
700 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
701 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
702 const lhs = try self.resolveInst(extra.lhs);
703 const rhs = try self.resolveInst(extra.rhs);
704 const lhs_ty = self.air.typeOf(extra.lhs);
705 const rhs_ty = self.air.typeOf(extra.rhs);
706
707 switch (lhs_ty.zigTypeTag()) {
708 .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}),
709 .Int => {
710 const mod = self.bin_file.options.module.?;
711 assert(lhs_ty.eql(rhs_ty, mod));
712 const int_info = lhs_ty.intInfo(self.target.*);
713 switch (int_info.bits) {
714 32, 64 => {
715 // Only say yes if the operation is
716 // commutative, i.e. we can swap both of the
717 // operands
718 const lhs_immediate_ok = switch (tag) {
719 .add_with_overflow => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
720 .sub_with_overflow => false,
721 else => unreachable,
722 };
723 const rhs_immediate_ok = switch (tag) {
724 .add_with_overflow,
725 .sub_with_overflow,
726 => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
727 else => unreachable,
728 };
729
730 const mir_tag: Mir.Inst.Tag = switch (tag) {
731 .add_with_overflow => .addcc,
732 .sub_with_overflow => .subcc,
733 else => unreachable,
734 };
735
736 try self.spillCompareFlagsIfOccupied();
737 self.compare_flags_inst = inst;
738
739 const dest = blk: {
740 if (rhs_immediate_ok) {
741 break :blk try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, null);
742 } else if (lhs_immediate_ok) {
743 // swap lhs and rhs
744 break :blk try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, null);
745 } else {
746 break :blk try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, null);
747 }
748 };
749
750 const cond = switch (int_info.signedness) {
751 .unsigned => switch (tag) {
752 .add_with_overflow => Instruction.ICondition.cs,
753 .sub_with_overflow => Instruction.ICondition.cc,
754 else => unreachable,
755 },
756 .signed => Instruction.ICondition.vs,
757 };
758
759 const ccr = switch (int_info.bits) {
760 32 => Instruction.CCR.icc,
761 64 => Instruction.CCR.xcc,
762 else => unreachable,
763 };
764
765 break :result MCValue{ .register_with_overflow = .{
766 .reg = dest.register,
767 .flag = .{ .cond = cond, .ccr = ccr },
768 } };
769 },
770 else => return self.fail("TODO overflow operations on other integer sizes", .{}),
771 }
772 },
773 else => unreachable,
774 }
775 };
776 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
777}
778
687fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {779fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
688 const stack_offset = try self.allocMemPtr(inst);780 const stack_offset = try self.allocMemPtr(inst);
689 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });781 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....@@ -955,13 +1047,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
9551047
956 switch (mc_arg) {1048 switch (mc_arg) {
957 .none => continue,1049 .none => continue,
958 .undef => unreachable,
959 .immediate => unreachable,
960 .unreach => unreachable,
961 .dead => unreachable,
962 .memory => unreachable,
963 .compare_flags_signed => unreachable,
964 .compare_flags_unsigned => unreachable,
965 .register => |reg| {1050 .register => |reg| {
966 try self.register_manager.getReg(reg, null);1051 try self.register_manager.getReg(reg, null);
967 try self.genSetReg(arg_ty, reg, arg_mcv);1052 try self.genSetReg(arg_ty, reg, arg_mcv);
...@@ -972,6 +1057,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -972,6 +1057,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
972 .ptr_stack_offset => {1057 .ptr_stack_offset => {
973 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});1058 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
974 },1059 },
1060 else => unreachable,
975 }1061 }
976 }1062 }
9771063
...@@ -1894,6 +1980,7 @@ fn binOpImmediate(...@@ -1894,6 +1980,7 @@ fn binOpImmediate(
18941980
1895 const mir_data: Mir.Inst.Data = switch (mir_tag) {1981 const mir_data: Mir.Inst.Data = switch (mir_tag) {
1896 .add,1982 .add,
1983 .addcc,
1897 .mulx,1984 .mulx,
1898 .subcc,1985 .subcc,
1899 => .{1986 => .{
...@@ -2010,6 +2097,7 @@ fn binOpRegister(...@@ -2010,6 +2097,7 @@ fn binOpRegister(
20102097
2011 const mir_data: Mir.Inst.Data = switch (mir_tag) {2098 const mir_data: Mir.Inst.Data = switch (mir_tag) {
2012 .add,2099 .add,
2100 .addcc,
2013 .mulx,2101 .mulx,
2014 .subcc,2102 .subcc,
2015 => .{2103 => .{
...@@ -2473,6 +2561,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2473,6 +2561,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2473 },2561 },
2474 });2562 });
2475 },2563 },
2564 .register_with_overflow => unreachable,
2476 .memory => |addr| {2565 .memory => |addr| {
2477 // The value is in memory at a hard-coded address.2566 // The value is in memory at a hard-coded address.
2478 // If the type is a pointer, it means the pointer address is at this memory location.2567 // 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...@@ -2519,6 +2608,47 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
2519 return self.fail("TODO larger stack offsets", .{});2608 return self.fail("TODO larger stack offsets", .{});
2520 return self.genStore(reg, .sp, i13, simm13, abi_size);2609 return self.genStore(reg, .sp, i13, simm13, abi_size);
2521 },2610 },
2611 .register_with_overflow => |rwo| {
2612 const reg_lock = self.register_manager.lockReg(rwo.reg);
2613 defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
2614
2615 const wrapped_ty = ty.structFieldType(0);
2616 try self.genSetStack(wrapped_ty, stack_offset, .{ .register = rwo.reg });
2617
2618 const overflow_bit_ty = ty.structFieldType(1);
2619 const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*));
2620 const cond_reg = try self.register_manager.allocReg(null, gp);
2621
2622 // TODO handle floating point CCRs
2623 assert(rwo.flag.ccr == .xcc or rwo.flag.ccr == .icc);
2624
2625 _ = try self.addInst(.{
2626 .tag = .mov,
2627 .data = .{
2628 .arithmetic_2op = .{
2629 .is_imm = false,
2630 .rs1 = cond_reg,
2631 .rs2_or_imm = .{ .rs2 = .g0 },
2632 },
2633 },
2634 });
2635
2636 _ = try self.addInst(.{
2637 .tag = .movcc,
2638 .data = .{
2639 .conditional_move = .{
2640 .ccr = rwo.flag.ccr,
2641 .cond = .{ .icond = rwo.flag.cond },
2642 .is_imm = true,
2643 .rd = cond_reg,
2644 .rs2_or_imm = .{ .imm = 1 },
2645 },
2646 },
2647 });
2648 try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{
2649 .register = cond_reg,
2650 });
2651 },
2522 .memory, .stack_offset => {2652 .memory, .stack_offset => {
2523 switch (mcv) {2653 switch (mcv) {
2524 .stack_offset => |off| {2654 .stack_offset => |off| {
...@@ -2760,6 +2890,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -2760,6 +2890,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
2760 .dead => unreachable,2890 .dead => unreachable,
2761 .compare_flags_unsigned,2891 .compare_flags_unsigned,
2762 .compare_flags_signed,2892 .compare_flags_signed,
2893 .register_with_overflow,
2763 => unreachable, // cannot hold an address2894 => unreachable, // cannot hold an address
2764 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),2895 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
2765 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),2896 .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...@@ -3100,6 +3231,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
3100 .dead => unreachable,3231 .dead => unreachable,
3101 .compare_flags_unsigned,3232 .compare_flags_unsigned,
3102 .compare_flags_signed,3233 .compare_flags_signed,
3234 .register_with_overflow,
3103 => unreachable, // cannot hold an address3235 => unreachable, // cannot hold an address
3104 .immediate => |imm| {3236 .immediate => |imm| {
3105 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);3237 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
src/arch/sparc64/Emit.zig+1
...@@ -79,6 +79,7 @@ pub fn emitMir(...@@ -79,6 +79,7 @@ pub fn emitMir(
79 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),79 .dbg_epilogue_begin => try emit.mirDebugEpilogueBegin(),
8080
81 .add => try emit.mirArithmetic3Op(inst),81 .add => try emit.mirArithmetic3Op(inst),
82 .addcc => @panic("TODO implement sparc64 addcc"),
8283
83 .bpr => try emit.mirConditionalBranch(inst),84 .bpr => try emit.mirConditionalBranch(inst),
84 .bpcc => try emit.mirConditionalBranch(inst),85 .bpcc => try emit.mirConditionalBranch(inst),
src/arch/sparc64/Mir.zig+1
...@@ -42,6 +42,7 @@ pub const Inst = struct {...@@ -42,6 +42,7 @@ pub const Inst = struct {
42 /// This uses the arithmetic_3op field.42 /// This uses the arithmetic_3op field.
43 // TODO add other operations.43 // TODO add other operations.
44 add,44 add,
45 addcc,
4546
46 /// A.3 Branch on Integer Register with Prediction (BPr)47 /// A.3 Branch on Integer Register with Prediction (BPr)
47 /// This uses the branch_predict_reg field.48 /// This uses the branch_predict_reg field.