| ... | ... | @@ -123,6 +123,16 @@ const MCValue = union(enum) { |
| 123 | 123 | immediate: u64, |
| 124 | 124 | /// The value is in a target-specific register. |
| 125 | 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 | 136 | /// The value is in memory at a hard-coded address. |
| 127 | 137 | /// If the type is a pointer, it means the pointer address is at this memory location. |
| 128 | 138 | memory: u64, |
| ... | ... | @@ -525,8 +535,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 525 | 535 | .trunc_float, |
| 526 | 536 | => @panic("TODO try self.airUnaryMath(inst)"), |
| 527 | 537 | |
| 528 | | .add_with_overflow => @panic("TODO try self.airAddWithOverflow(inst)"), |
| 529 | | .sub_with_overflow => @panic("TODO try self.airSubWithOverflow(inst)"), |
| 538 | .add_with_overflow => try self.airAddSubWithOverflow(inst), |
| 539 | .sub_with_overflow => try self.airAddSubWithOverflow(inst), |
| 530 | 540 | .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"), |
| 531 | 541 | .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"), |
| 532 | 542 | |
| ... | ... | @@ -684,6 +694,88 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 684 | 694 | } |
| 685 | 695 | } |
| 686 | 696 | |
| 697 | fn 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 | |
| 687 | 779 | fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { |
| 688 | 780 | const stack_offset = try self.allocMemPtr(inst); |
| 689 | 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 | 1047 | |
| 956 | 1048 | switch (mc_arg) { |
| 957 | 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 | 1050 | .register => |reg| { |
| 966 | 1051 | try self.register_manager.getReg(reg, null); |
| 967 | 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 | 1057 | .ptr_stack_offset => { |
| 973 | 1058 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); |
| 974 | 1059 | }, |
| 1060 | else => unreachable, |
| 975 | 1061 | } |
| 976 | 1062 | } |
| 977 | 1063 | |
| ... | ... | @@ -1894,6 +1980,7 @@ fn binOpImmediate( |
| 1894 | 1980 | |
| 1895 | 1981 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 1896 | 1982 | .add, |
| 1983 | .addcc, |
| 1897 | 1984 | .mulx, |
| 1898 | 1985 | .subcc, |
| 1899 | 1986 | => .{ |
| ... | ... | @@ -2010,6 +2097,7 @@ fn binOpRegister( |
| 2010 | 2097 | |
| 2011 | 2098 | const mir_data: Mir.Inst.Data = switch (mir_tag) { |
| 2012 | 2099 | .add, |
| 2100 | .addcc, |
| 2013 | 2101 | .mulx, |
| 2014 | 2102 | .subcc, |
| 2015 | 2103 | => .{ |
| ... | ... | @@ -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 | 2565 | .memory => |addr| { |
| 2477 | 2566 | // The value is in memory at a hard-coded address. |
| 2478 | 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 | 2608 | return self.fail("TODO larger stack offsets", .{}); |
| 2520 | 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 | 2652 | .memory, .stack_offset => { |
| 2523 | 2653 | switch (mcv) { |
| 2524 | 2654 | .stack_offset => |off| { |
| ... | ... | @@ -2760,6 +2890,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 2760 | 2890 | .dead => unreachable, |
| 2761 | 2891 | .compare_flags_unsigned, |
| 2762 | 2892 | .compare_flags_signed, |
| 2893 | .register_with_overflow, |
| 2763 | 2894 | => unreachable, // cannot hold an address |
| 2764 | 2895 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| 2765 | 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 | 3231 | .dead => unreachable, |
| 3101 | 3232 | .compare_flags_unsigned, |
| 3102 | 3233 | .compare_flags_signed, |
| 3234 | .register_with_overflow, |
| 3103 | 3235 | => unreachable, // cannot hold an address |
| 3104 | 3236 | .immediate => |imm| { |
| 3105 | 3237 | try self.setRegOrMem(value_ty, .{ .memory = imm }, value); |