| ... | @@ -1783,17 +1783,20 @@ fn binOpRegister( | ... | @@ -1783,17 +1783,20 @@ fn binOpRegister( |
| 1783 | }; | 1783 | }; |
| 1784 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); | 1784 | defer self.register_manager.unfreezeRegs(&.{rhs_reg}); |
| 1785 | | 1785 | |
| 1786 | const dest_reg = if (maybe_inst) |inst| blk: { | 1786 | const dest_reg = switch (tag) { |
| 1787 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1787 | .cmp_eq => .r0, // cmp has no destination regardless |
| | 1788 | else => if (maybe_inst) |inst| blk: { |
| | 1789 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1788 | | 1790 | |
| 1789 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { | 1791 | if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { |
| 1790 | break :blk lhs_reg; | 1792 | break :blk lhs_reg; |
| 1791 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { | 1793 | } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { |
| 1792 | break :blk rhs_reg; | 1794 | break :blk rhs_reg; |
| 1793 | } else { | 1795 | } else { |
| 1794 | break :blk try self.register_manager.allocReg(inst); | 1796 | break :blk try self.register_manager.allocReg(inst); |
| 1795 | } | 1797 | } |
| 1796 | } else try self.register_manager.allocReg(null); | 1798 | } else try self.register_manager.allocReg(null), |
| | 1799 | }; |
| 1797 | | 1800 | |
| 1798 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 1801 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1799 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); | 1802 | if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); |
| ... | @@ -1801,6 +1804,7 @@ fn binOpRegister( | ... | @@ -1801,6 +1804,7 @@ fn binOpRegister( |
| 1801 | const mir_tag: Mir.Inst.Tag = switch (tag) { | 1804 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1802 | .add, .ptr_add => .add, | 1805 | .add, .ptr_add => .add, |
| 1803 | .sub, .ptr_sub => .sub, | 1806 | .sub, .ptr_sub => .sub, |
| | 1807 | .cmp_eq => .cmp, |
| 1804 | .mul => .mul, | 1808 | .mul => .mul, |
| 1805 | .bit_and, | 1809 | .bit_and, |
| 1806 | .bool_and, | 1810 | .bool_and, |
| ... | @@ -1823,6 +1827,7 @@ fn binOpRegister( | ... | @@ -1823,6 +1827,7 @@ fn binOpRegister( |
| 1823 | const mir_data: Mir.Inst.Data = switch (tag) { | 1827 | const mir_data: Mir.Inst.Data = switch (tag) { |
| 1824 | .add, | 1828 | .add, |
| 1825 | .sub, | 1829 | .sub, |
| | 1830 | .cmp_eq, |
| 1826 | .bit_and, | 1831 | .bit_and, |
| 1827 | .bool_and, | 1832 | .bool_and, |
| 1828 | .bit_or, | 1833 | .bit_or, |
| ... | @@ -1904,26 +1909,30 @@ fn binOpImmediate( | ... | @@ -1904,26 +1909,30 @@ fn binOpImmediate( |
| 1904 | }; | 1909 | }; |
| 1905 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); | 1910 | defer self.register_manager.unfreezeRegs(&.{lhs_reg}); |
| 1906 | | 1911 | |
| 1907 | const dest_reg = if (maybe_inst) |inst| blk: { | 1912 | const dest_reg = switch (tag) { |
| 1908 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1913 | .cmp_eq => .r0, // cmp has no destination reg |
| | 1914 | else => if (maybe_inst) |inst| blk: { |
| | 1915 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1909 | | 1916 | |
| 1910 | if (lhs_is_register and self.reuseOperand( | 1917 | if (lhs_is_register and self.reuseOperand( |
| 1911 | inst, | 1918 | inst, |
| 1912 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, | 1919 | if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, |
| 1913 | if (lhs_and_rhs_swapped) 1 else 0, | 1920 | if (lhs_and_rhs_swapped) 1 else 0, |
| 1914 | lhs, | 1921 | lhs, |
| 1915 | )) { | 1922 | )) { |
| 1916 | break :blk lhs_reg; | 1923 | break :blk lhs_reg; |
| 1917 | } else { | 1924 | } else { |
| 1918 | break :blk try self.register_manager.allocReg(inst); | 1925 | break :blk try self.register_manager.allocReg(inst); |
| 1919 | } | 1926 | } |
| 1920 | } else try self.register_manager.allocReg(null); | 1927 | } else try self.register_manager.allocReg(null), |
| | 1928 | }; |
| 1921 | | 1929 | |
| 1922 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); | 1930 | if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); |
| 1923 | | 1931 | |
| 1924 | const mir_tag: Mir.Inst.Tag = switch (tag) { | 1932 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1925 | .add => .add, | 1933 | .add => .add, |
| 1926 | .sub => .sub, | 1934 | .sub => .sub, |
| | 1935 | .cmp_eq => .cmp, |
| 1927 | .bit_and, | 1936 | .bit_and, |
| 1928 | .bool_and, | 1937 | .bool_and, |
| 1929 | => .@"and", | 1938 | => .@"and", |
| ... | @@ -1945,6 +1954,7 @@ fn binOpImmediate( | ... | @@ -1945,6 +1954,7 @@ fn binOpImmediate( |
| 1945 | const mir_data: Mir.Inst.Data = switch (tag) { | 1954 | const mir_data: Mir.Inst.Data = switch (tag) { |
| 1946 | .add, | 1955 | .add, |
| 1947 | .sub, | 1956 | .sub, |
| | 1957 | .cmp_eq, |
| 1948 | .bit_and, | 1958 | .bit_and, |
| 1949 | .bool_and, | 1959 | .bool_and, |
| 1950 | .bit_or, | 1960 | .bit_or, |
| ... | @@ -1999,6 +2009,7 @@ fn binOp( | ... | @@ -1999,6 +2009,7 @@ fn binOp( |
| 1999 | switch (tag) { | 2009 | switch (tag) { |
| 2000 | .add, | 2010 | .add, |
| 2001 | .sub, | 2011 | .sub, |
| | 2012 | .cmp_eq, |
| 2002 | => { | 2013 | => { |
| 2003 | switch (lhs_ty.zigTypeTag()) { | 2014 | switch (lhs_ty.zigTypeTag()) { |
| 2004 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | 2015 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), |
| ... | @@ -2012,12 +2023,15 @@ fn binOp( | ... | @@ -2012,12 +2023,15 @@ fn binOp( |
| 2012 | // operands | 2023 | // operands |
| 2013 | const lhs_immediate_ok = switch (tag) { | 2024 | const lhs_immediate_ok = switch (tag) { |
| 2014 | .add => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null, | 2025 | .add => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null, |
| 2015 | .sub => false, | 2026 | .sub, |
| | 2027 | .cmp_eq, |
| | 2028 | => false, |
| 2016 | else => unreachable, | 2029 | else => unreachable, |
| 2017 | }; | 2030 | }; |
| 2018 | const rhs_immediate_ok = switch (tag) { | 2031 | const rhs_immediate_ok = switch (tag) { |
| 2019 | .add, | 2032 | .add, |
| 2020 | .sub, | 2033 | .sub, |
| | 2034 | .cmp_eq, |
| 2021 | => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null, | 2035 | => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null, |
| 2022 | else => unreachable, | 2036 | else => unreachable, |
| 2023 | }; | 2037 | }; |
| ... | @@ -2150,31 +2164,6 @@ fn binOp( | ... | @@ -2150,31 +2164,6 @@ fn binOp( |
| 2150 | } | 2164 | } |
| 2151 | } | 2165 | } |
| 2152 | | 2166 | |
| 2153 | fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool { | | |
| 2154 | return switch (mcv) { | | |
| 2155 | .none => unreachable, | | |
| 2156 | .undef => unreachable, | | |
| 2157 | .dead, .unreach => unreachable, | | |
| 2158 | .compare_flags_unsigned => unreachable, | | |
| 2159 | .compare_flags_signed => unreachable, | | |
| 2160 | .ptr_stack_offset => unreachable, | | |
| 2161 | .ptr_embedded_in_code => unreachable, | | |
| 2162 | .immediate => |imm| blk: { | | |
| 2163 | if (imm > std.math.maxInt(u32)) return self.fail("TODO ARM binary arithmetic immediate larger than u32", .{}); | | |
| 2164 | | | |
| 2165 | // Load immediate into register if it doesn't fit | | |
| 2166 | // in an operand | | |
| 2167 | break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) == null; | | |
| 2168 | }, | | |
| 2169 | .register => true, | | |
| 2170 | .stack_offset, | | |
| 2171 | .stack_argument_offset, | | |
| 2172 | .embedded_in_code, | | |
| 2173 | .memory, | | |
| 2174 | => true, | | |
| 2175 | }; | | |
| 2176 | } | | |
| 2177 | | | |
| 2178 | fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, abi_size: u32) !void { | 2167 | fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, abi_size: u32) !void { |
| 2179 | switch (abi_size) { | 2168 | switch (abi_size) { |
| 2180 | 1, 3, 4 => { | 2169 | 1, 3, 4 => { |
| ... | @@ -2548,84 +2537,38 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { | ... | @@ -2548,84 +2537,38 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2548 | const rhs = try self.resolveInst(bin_op.rhs); | 2537 | const rhs = try self.resolveInst(bin_op.rhs); |
| 2549 | const lhs_ty = self.air.typeOf(bin_op.lhs); | 2538 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 2550 | | 2539 | |
| 2551 | if (lhs_ty.abiSize(self.target.*) > 4) { | 2540 | switch (lhs_ty.zigTypeTag()) { |
| 2552 | return self.fail("TODO cmp for types with size > 4", .{}); | 2541 | .Vector => return self.fail("TODO ARM cmp vectors", .{}), |
| 2553 | } | 2542 | .Optional => return self.fail("TODO ARM cmp optionals", .{}), |
| 2554 | | 2543 | .Float => return self.fail("TODO ARM cmp floats", .{}), |
| 2555 | const signedness: std.builtin.Signedness = blk: { | 2544 | .Int, .Bool, .Pointer, .ErrorSet, .Enum => { |
| 2556 | // by default we tell the operand type is unsigned (i.e. bools and enum values) | 2545 | var int_buffer: Type.Payload.Bits = undefined; |
| 2557 | if (lhs_ty.zigTypeTag() != .Int) break :blk .unsigned; | 2546 | const int_ty = switch (lhs_ty.zigTypeTag()) { |
| 2558 | | 2547 | .Enum => lhs_ty.intTagType(&int_buffer), |
| 2559 | // incase of an actual integer, we emit the correct signedness | 2548 | .Int => lhs_ty, |
| 2560 | break :blk lhs_ty.intInfo(self.target.*).signedness; | 2549 | .Bool => Type.initTag(.u1), |
| 2561 | }; | 2550 | .Pointer => Type.usize, |
| 2562 | | 2551 | .ErrorSet => Type.initTag(.u16), |
| 2563 | try self.spillCompareFlagsIfOccupied(); | 2552 | else => unreachable, |
| 2564 | self.compare_flags_inst = inst; | 2553 | }; |
| 2565 | | | |
| 2566 | const lhs_is_register = lhs == .register; | | |
| 2567 | const rhs_is_register = rhs == .register; | | |
| 2568 | // lhs should always be a register | | |
| 2569 | const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs); | | |
| 2570 | | 2554 | |
| 2571 | if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); | 2555 | const int_info = int_ty.intInfo(self.target.*); |
| 2572 | defer if (lhs_is_register) self.register_manager.unfreezeRegs(&.{lhs.register}); | 2556 | if (int_info.bits <= 32) { |
| 2573 | if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); | 2557 | try self.spillCompareFlagsIfOccupied(); |
| 2574 | defer if (rhs_is_register) self.register_manager.unfreezeRegs(&.{rhs.register}); | 2558 | self.compare_flags_inst = inst; |
| 2575 | | 2559 | |
| 2576 | var lhs_mcv = lhs; | 2560 | _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); |
| 2577 | var rhs_mcv = rhs; | | |
| 2578 | | 2561 | |
| 2579 | // Allocate registers | 2562 | break :result switch (int_info.signedness) { |
| 2580 | if (rhs_should_be_register) { | 2563 | .signed => MCValue{ .compare_flags_signed = op }, |
| 2581 | if (!lhs_is_register and !rhs_is_register) { | 2564 | .unsigned => MCValue{ .compare_flags_unsigned = op }, |
| 2582 | const regs = try self.register_manager.allocRegs(2, .{ | 2565 | }; |
| 2583 | Air.refToIndex(bin_op.lhs).?, Air.refToIndex(bin_op.rhs).?, | 2566 | } else { |
| 2584 | }); | 2567 | return self.fail("TODO ARM cmp for ints > 32 bits", .{}); |
| 2585 | lhs_mcv = MCValue{ .register = regs[0] }; | 2568 | } |
| 2586 | rhs_mcv = MCValue{ .register = regs[1] }; | 2569 | }, |
| 2587 | } else if (!rhs_is_register) { | 2570 | else => unreachable, |
| 2588 | const track_inst = if (self.liveness.operandDies(inst, 1)) null else Air.refToIndex(bin_op.rhs).?; | | |
| 2589 | rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst) }; | | |
| 2590 | } else if (!lhs_is_register) { | | |
| 2591 | const track_inst = if (self.liveness.operandDies(inst, 0)) null else Air.refToIndex(bin_op.lhs).?; | | |
| 2592 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst) }; | | |
| 2593 | } | | |
| 2594 | } else { | | |
| 2595 | if (!lhs_is_register) { | | |
| 2596 | const track_inst = if (self.liveness.operandDies(inst, 0)) null else Air.refToIndex(bin_op.lhs).?; | | |
| 2597 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(track_inst) }; | | |
| 2598 | } | | |
| 2599 | } | | |
| 2600 | | | |
| 2601 | // Move the operands to the newly allocated registers | | |
| 2602 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; | | |
| 2603 | if (lhs_mcv == .register and !lhs_is_register) { | | |
| 2604 | try self.genSetReg(lhs_ty, lhs_mcv.register, lhs); | | |
| 2605 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.lhs).?, lhs); | | |
| 2606 | } | | |
| 2607 | if (rhs_mcv == .register and !rhs_is_register) { | | |
| 2608 | try self.genSetReg(lhs_ty, rhs_mcv.register, rhs); | | |
| 2609 | branch.inst_table.putAssumeCapacity(Air.refToIndex(bin_op.rhs).?, rhs); | | |
| 2610 | } | 2571 | } |
| 2611 | | | |
| 2612 | _ = try self.addInst(.{ | | |
| 2613 | .tag = .cmp, | | |
| 2614 | .data = .{ .rr_op = .{ | | |
| 2615 | .rd = undefined, | | |
| 2616 | .rn = lhs_mcv.register, | | |
| 2617 | .op = switch (rhs_mcv) { | | |
| 2618 | .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?, | | |
| 2619 | .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none), | | |
| 2620 | else => unreachable, | | |
| 2621 | }, | | |
| 2622 | } }, | | |
| 2623 | }); | | |
| 2624 | | | |
| 2625 | break :result switch (signedness) { | | |
| 2626 | .signed => MCValue{ .compare_flags_signed = op }, | | |
| 2627 | .unsigned => MCValue{ .compare_flags_unsigned = op }, | | |
| 2628 | }; | | |
| 2629 | }; | 2572 | }; |
| 2630 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 2573 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2631 | } | 2574 | } |