| ... | @@ -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)"), |
| 527 | | 537 | |
| 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)"), |
| 532 | | 542 | |
| ... | @@ -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 | } |
| 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 | fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { | 779 | fn 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. |
| 955 | | 1047 | |
| 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 | } |
| 977 | | 1063 | |
| ... | @@ -1894,6 +1980,7 @@ fn binOpImmediate( | ... | @@ -1894,6 +1980,7 @@ fn binOpImmediate( |
| 1894 | | 1980 | |
| 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( |
| 2010 | | 2097 | |
| 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 address | 2894 | => 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 address | 3235 | => 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); |