| ... | @@ -1592,15 +1592,53 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1592,15 +1592,53 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1592 | } | 1592 | } |
| 1593 | | 1593 | |
| 1594 | fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue { | 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 | const lhs = try self.resolveInst(op_lhs); | 1626 | const lhs = try self.resolveInst(op_lhs); |
| 1596 | const rhs = try self.resolveInst(op_rhs); | 1627 | const rhs = try self.resolveInst(op_rhs); |
| 1597 | | 1628 | |
| 1598 | const lhs_is_register = lhs == .register; | 1629 | const lhs_is_register = lhs == .register; |
| 1599 | const rhs_is_register = rhs == .register; | 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 | const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs); | 1635 | const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs); |
| 1602 | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs); | 1636 | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs); |
| 1603 | const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs); | 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 | // Destination must be a register | 1643 | // Destination must be a register |
| 1606 | var dst_mcv: MCValue = undefined; | 1644 | var dst_mcv: MCValue = undefined; |
| ... | @@ -1617,7 +1655,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1617,7 +1655,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1617 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); | 1655 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); |
| 1618 | } | 1656 | } |
| 1619 | dst_mcv = lhs; | 1657 | dst_mcv = lhs; |
| 1620 | } else if (reuse_rhs) { | 1658 | } else if (reuse_rhs and can_swap_lhs_and_rhs) { |
| 1621 | // Allocate 0 or 1 registers | 1659 | // Allocate 0 or 1 registers |
| 1622 | if (!lhs_is_register and lhs_should_be_register) { | 1660 | if (!lhs_is_register and lhs_should_be_register) { |
| 1623 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) }; | 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,7 +1694,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1656 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) }; | 1694 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) }; |
| 1657 | lhs_mcv = dst_mcv; | 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 | // LHS is immediate | 1698 | // LHS is immediate |
| 1661 | if (rhs_is_register) { | 1699 | if (rhs_is_register) { |
| 1662 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) }; | 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,6 +1721,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1683 | rhs_mcv, | 1721 | rhs_mcv, |
| 1684 | swap_lhs_and_rhs, | 1722 | swap_lhs_and_rhs, |
| 1685 | op, | 1723 | op, |
| | 1724 | signedness, |
| 1686 | ); | 1725 | ); |
| 1687 | return dst_mcv; | 1726 | return dst_mcv; |
| 1688 | } | 1727 | } |
| ... | @@ -1694,6 +1733,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1694,6 +1733,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1694 | rhs_mcv: MCValue, | 1733 | rhs_mcv: MCValue, |
| 1695 | swap_lhs_and_rhs: bool, | 1734 | swap_lhs_and_rhs: bool, |
| 1696 | op: Air.Inst.Tag, | 1735 | op: Air.Inst.Tag, |
| | 1736 | signedness: std.builtin.Signedness, |
| 1697 | ) !void { | 1737 | ) !void { |
| 1698 | assert(lhs_mcv == .register or rhs_mcv == .register); | 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,6 +1779,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1739 | .cmp_eq => { | 1779 | .cmp_eq => { |
| 1740 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32()); | 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 | else => unreachable, // not a binary instruction | 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,7 +3050,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2989 | } | 3050 | } |
| 2990 | | 3051 | |
| 2991 | // The destination register is not present in the cmp instruction | 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 | break :result switch (ty.isSignedInt()) { | 3056 | break :result switch (ty.isSignedInt()) { |
| 2995 | true => MCValue{ .compare_flags_signed = op }, | 3057 | true => MCValue{ .compare_flags_signed = op }, |