| ... | ... | @@ -1592,15 +1592,53 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1592 | 1592 | } |
| 1593 | 1593 | |
| 1594 | 1594 | fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue { |
| 1595 | // In the case of bitshifts, the type of rhs is different |
| 1596 | // from the resulting type |
| 1597 | const ty = self.air.typeOf(op_lhs); |
| 1598 | |
| 1599 | switch (ty.zigTypeTag()) { |
| 1600 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), |
| 1601 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), |
| 1602 | .Bool => { |
| 1603 | return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned); |
| 1604 | }, |
| 1605 | .Int => { |
| 1606 | const int_info = ty.intInfo(self.target.*); |
| 1607 | return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness); |
| 1608 | }, |
| 1609 | else => unreachable, |
| 1610 | } |
| 1611 | } |
| 1612 | |
| 1613 | fn genArmBinIntOp( |
| 1614 | self: *Self, |
| 1615 | inst: Air.Inst.Index, |
| 1616 | op_lhs: Air.Inst.Ref, |
| 1617 | op_rhs: Air.Inst.Ref, |
| 1618 | op: Air.Inst.Tag, |
| 1619 | bits: u16, |
| 1620 | signedness: std.builtin.Signedness, |
| 1621 | ) !MCValue { |
| 1622 | if (bits > 32) { |
| 1623 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); |
| 1624 | } |
| 1625 | |
| 1595 | 1626 | const lhs = try self.resolveInst(op_lhs); |
| 1596 | 1627 | const rhs = try self.resolveInst(op_rhs); |
| 1597 | 1628 | |
| 1598 | 1629 | const lhs_is_register = lhs == .register; |
| 1599 | 1630 | const rhs_is_register = rhs == .register; |
| 1600 | | const lhs_should_be_register = try self.armOperandShouldBeRegister(lhs); |
| 1631 | const lhs_should_be_register = switch (op) { |
| 1632 | .shr, .shl => true, |
| 1633 | else => try self.armOperandShouldBeRegister(lhs), |
| 1634 | }; |
| 1601 | 1635 | const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs); |
| 1602 | 1636 | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs); |
| 1603 | 1637 | const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs); |
| 1638 | const can_swap_lhs_and_rhs = switch (op) { |
| 1639 | .shr, .shl => false, |
| 1640 | else => true, |
| 1641 | }; |
| 1604 | 1642 | |
| 1605 | 1643 | // Destination must be a register |
| 1606 | 1644 | var dst_mcv: MCValue = undefined; |
| ... | ... | @@ -1617,7 +1655,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1617 | 1655 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); |
| 1618 | 1656 | } |
| 1619 | 1657 | dst_mcv = lhs; |
| 1620 | | } else if (reuse_rhs) { |
| 1658 | } else if (reuse_rhs and can_swap_lhs_and_rhs) { |
| 1621 | 1659 | // Allocate 0 or 1 registers |
| 1622 | 1660 | if (!lhs_is_register and lhs_should_be_register) { |
| 1623 | 1661 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) }; |
| ... | ... | @@ -1656,7 +1694,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1656 | 1694 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) }; |
| 1657 | 1695 | lhs_mcv = dst_mcv; |
| 1658 | 1696 | } |
| 1659 | | } else if (rhs_should_be_register) { |
| 1697 | } else if (rhs_should_be_register and can_swap_lhs_and_rhs) { |
| 1660 | 1698 | // LHS is immediate |
| 1661 | 1699 | if (rhs_is_register) { |
| 1662 | 1700 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) }; |
| ... | ... | @@ -1683,6 +1721,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1683 | 1721 | rhs_mcv, |
| 1684 | 1722 | swap_lhs_and_rhs, |
| 1685 | 1723 | op, |
| 1724 | signedness, |
| 1686 | 1725 | ); |
| 1687 | 1726 | return dst_mcv; |
| 1688 | 1727 | } |
| ... | ... | @@ -1694,6 +1733,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1694 | 1733 | rhs_mcv: MCValue, |
| 1695 | 1734 | swap_lhs_and_rhs: bool, |
| 1696 | 1735 | op: Air.Inst.Tag, |
| 1736 | signedness: std.builtin.Signedness, |
| 1697 | 1737 | ) !void { |
| 1698 | 1738 | assert(lhs_mcv == .register or rhs_mcv == .register); |
| 1699 | 1739 | |
| ... | ... | @@ -1739,6 +1779,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1739 | 1779 | .cmp_eq => { |
| 1740 | 1780 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32()); |
| 1741 | 1781 | }, |
| 1782 | .shl => { |
| 1783 | assert(!swap_lhs_and_rhs); |
| 1784 | const shift_amout = switch (operand) { |
| 1785 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), |
| 1786 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), |
| 1787 | }; |
| 1788 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amout).toU32()); |
| 1789 | }, |
| 1790 | .shr => { |
| 1791 | assert(!swap_lhs_and_rhs); |
| 1792 | const shift_amout = switch (operand) { |
| 1793 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), |
| 1794 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), |
| 1795 | }; |
| 1796 | |
| 1797 | const shr = switch (signedness) { |
| 1798 | .signed => Instruction.asr, |
| 1799 | .unsigned => Instruction.lsr, |
| 1800 | }; |
| 1801 | writeInt(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amout).toU32()); |
| 1802 | }, |
| 1742 | 1803 | else => unreachable, // not a binary instruction |
| 1743 | 1804 | } |
| 1744 | 1805 | } |
| ... | ... | @@ -2989,7 +3050,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2989 | 3050 | } |
| 2990 | 3051 | |
| 2991 | 3052 | // The destination register is not present in the cmp instruction |
| 2992 | | try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq); |
| 3053 | // The signedness of the integer does not matter for the cmp instruction |
| 3054 | try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined); |
| 2993 | 3055 | |
| 2994 | 3056 | break :result switch (ty.isSignedInt()) { |
| 2995 | 3057 | true => MCValue{ .compare_flags_signed = op }, |