| author | |
| committer | |
| log | a2438357e1f0f3853886a128f272cd27c714b176 |
| tree | f5b200eedeee3aab50b0bfe69e50e2e1a72bb8eb |
| parent | 0cd361219c107bce48f2d7b44c6f3dd05ea6ccf4 |
| parent | 224fe49be23c44095d39297a4986782bd19ce8b2 |
| signature |
stage2 ARM: implement bitshifts3 files changed, 239 insertions(+), 4 deletions(-)
src/codegen.zig+66-4| ... | ... | @@ -1602,15 +1602,53 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1602 | 1602 | } |
| 1603 | 1603 | |
| 1604 | 1604 | fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue { |
| 1605 | // In the case of bitshifts, the type of rhs is different | |
| 1606 | // from the resulting type | |
| 1607 | const ty = self.air.typeOf(op_lhs); | |
| 1608 | ||
| 1609 | switch (ty.zigTypeTag()) { | |
| 1610 | .Float => return self.fail("TODO ARM binary operations on floats", .{}), | |
| 1611 | .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), | |
| 1612 | .Bool => { | |
| 1613 | return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned); | |
| 1614 | }, | |
| 1615 | .Int => { | |
| 1616 | const int_info = ty.intInfo(self.target.*); | |
| 1617 | return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness); | |
| 1618 | }, | |
| 1619 | else => unreachable, | |
| 1620 | } | |
| 1621 | } | |
| 1622 | ||
| 1623 | fn genArmBinIntOp( | |
| 1624 | self: *Self, | |
| 1625 | inst: Air.Inst.Index, | |
| 1626 | op_lhs: Air.Inst.Ref, | |
| 1627 | op_rhs: Air.Inst.Ref, | |
| 1628 | op: Air.Inst.Tag, | |
| 1629 | bits: u16, | |
| 1630 | signedness: std.builtin.Signedness, | |
| 1631 | ) !MCValue { | |
| 1632 | if (bits > 32) { | |
| 1633 | return self.fail("TODO ARM binary operations on integers > u32/i32", .{}); | |
| 1634 | } | |
| 1635 | ||
| 1605 | 1636 | const lhs = try self.resolveInst(op_lhs); |
| 1606 | 1637 | const rhs = try self.resolveInst(op_rhs); |
| 1607 | 1638 | |
| 1608 | 1639 | const lhs_is_register = lhs == .register; |
| 1609 | 1640 | const rhs_is_register = rhs == .register; |
| 1610 | const lhs_should_be_register = try self.armOperandShouldBeRegister(lhs); | |
| 1641 | const lhs_should_be_register = switch (op) { | |
| 1642 | .shr, .shl => true, | |
| 1643 | else => try self.armOperandShouldBeRegister(lhs), | |
| 1644 | }; | |
| 1611 | 1645 | const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs); |
| 1612 | 1646 | const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs); |
| 1613 | 1647 | const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs); |
| 1648 | const can_swap_lhs_and_rhs = switch (op) { | |
| 1649 | .shr, .shl => false, | |
| 1650 | else => true, | |
| 1651 | }; | |
| 1614 | 1652 | |
| 1615 | 1653 | // Destination must be a register |
| 1616 | 1654 | var dst_mcv: MCValue = undefined; |
| ... | ... | @@ -1627,7 +1665,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1627 | 1665 | branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv); |
| 1628 | 1666 | } |
| 1629 | 1667 | dst_mcv = lhs; |
| 1630 | } else if (reuse_rhs) { | |
| 1668 | } else if (reuse_rhs and can_swap_lhs_and_rhs) { | |
| 1631 | 1669 | // Allocate 0 or 1 registers |
| 1632 | 1670 | if (!lhs_is_register and lhs_should_be_register) { |
| 1633 | 1671 | lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) }; |
| ... | ... | @@ -1666,7 +1704,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1666 | 1704 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) }; |
| 1667 | 1705 | lhs_mcv = dst_mcv; |
| 1668 | 1706 | } |
| 1669 | } else if (rhs_should_be_register) { | |
| 1707 | } else if (rhs_should_be_register and can_swap_lhs_and_rhs) { | |
| 1670 | 1708 | // LHS is immediate |
| 1671 | 1709 | if (rhs_is_register) { |
| 1672 | 1710 | dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) }; |
| ... | ... | @@ -1693,6 +1731,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1693 | 1731 | rhs_mcv, |
| 1694 | 1732 | swap_lhs_and_rhs, |
| 1695 | 1733 | op, |
| 1734 | signedness, | |
| 1696 | 1735 | ); |
| 1697 | 1736 | return dst_mcv; |
| 1698 | 1737 | } |
| ... | ... | @@ -1704,6 +1743,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1704 | 1743 | rhs_mcv: MCValue, |
| 1705 | 1744 | swap_lhs_and_rhs: bool, |
| 1706 | 1745 | op: Air.Inst.Tag, |
| 1746 | signedness: std.builtin.Signedness, | |
| 1707 | 1747 | ) !void { |
| 1708 | 1748 | assert(lhs_mcv == .register or rhs_mcv == .register); |
| 1709 | 1749 | |
| ... | ... | @@ -1749,6 +1789,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1749 | 1789 | .cmp_eq => { |
| 1750 | 1790 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32()); |
| 1751 | 1791 | }, |
| 1792 | .shl => { | |
| 1793 | assert(!swap_lhs_and_rhs); | |
| 1794 | const shift_amout = switch (operand) { | |
| 1795 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), | |
| 1796 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), | |
| 1797 | }; | |
| 1798 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amout).toU32()); | |
| 1799 | }, | |
| 1800 | .shr => { | |
| 1801 | assert(!swap_lhs_and_rhs); | |
| 1802 | const shift_amout = switch (operand) { | |
| 1803 | .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)), | |
| 1804 | .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)), | |
| 1805 | }; | |
| 1806 | ||
| 1807 | const shr = switch (signedness) { | |
| 1808 | .signed => Instruction.asr, | |
| 1809 | .unsigned => Instruction.lsr, | |
| 1810 | }; | |
| 1811 | writeInt(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amout).toU32()); | |
| 1812 | }, | |
| 1752 | 1813 | else => unreachable, // not a binary instruction |
| 1753 | 1814 | } |
| 1754 | 1815 | } |
| ... | ... | @@ -2999,7 +3060,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2999 | 3060 | } |
| 3000 | 3061 | |
| 3001 | 3062 | // The destination register is not present in the cmp instruction |
| 3002 | try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq); | |
| 3063 | // The signedness of the integer does not matter for the cmp instruction | |
| 3064 | try self.genArmBinOpCode(undefined, lhs_mcv, rhs_mcv, false, .cmp_eq, undefined); | |
| 3003 | 3065 | |
| 3004 | 3066 | break :result switch (ty.isSignedInt()) { |
| 3005 | 3067 | true => MCValue{ .compare_flags_signed = op }, |
src/codegen/arm.zig+87| ... | ... | @@ -1142,6 +1142,79 @@ pub const Instruction = union(enum) { |
| 1142 | 1142 | return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list)); |
| 1143 | 1143 | } |
| 1144 | 1144 | } |
| 1145 | ||
| 1146 | pub const ShiftAmount = union(enum) { | |
| 1147 | immediate: u5, | |
| 1148 | register: Register, | |
| 1149 | ||
| 1150 | pub fn imm(immediate: u5) ShiftAmount { | |
| 1151 | return .{ | |
| 1152 | .immediate = immediate, | |
| 1153 | }; | |
| 1154 | } | |
| 1155 | ||
| 1156 | pub fn reg(register: Register) ShiftAmount { | |
| 1157 | return .{ | |
| 1158 | .register = register, | |
| 1159 | }; | |
| 1160 | } | |
| 1161 | }; | |
| 1162 | ||
| 1163 | pub fn lsl(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1164 | return switch (shift) { | |
| 1165 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), | |
| 1166 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), | |
| 1167 | }; | |
| 1168 | } | |
| 1169 | ||
| 1170 | pub fn lsr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1171 | return switch (shift) { | |
| 1172 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), | |
| 1173 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), | |
| 1174 | }; | |
| 1175 | } | |
| 1176 | ||
| 1177 | pub fn asr(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1178 | return switch (shift) { | |
| 1179 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), | |
| 1180 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), | |
| 1181 | }; | |
| 1182 | } | |
| 1183 | ||
| 1184 | pub fn ror(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1185 | return switch (shift) { | |
| 1186 | .immediate => |imm| mov(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), | |
| 1187 | .register => |reg| mov(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), | |
| 1188 | }; | |
| 1189 | } | |
| 1190 | ||
| 1191 | pub fn lsls(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1192 | return switch (shift) { | |
| 1193 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_left))), | |
| 1194 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_left))), | |
| 1195 | }; | |
| 1196 | } | |
| 1197 | ||
| 1198 | pub fn lsrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1199 | return switch (shift) { | |
| 1200 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .logical_right))), | |
| 1201 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .logical_right))), | |
| 1202 | }; | |
| 1203 | } | |
| 1204 | ||
| 1205 | pub fn asrs(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1206 | return switch (shift) { | |
| 1207 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .arithmetic_right))), | |
| 1208 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .arithmetic_right))), | |
| 1209 | }; | |
| 1210 | } | |
| 1211 | ||
| 1212 | pub fn rors(cond: Condition, rd: Register, rm: Register, shift: ShiftAmount) Instruction { | |
| 1213 | return switch (shift) { | |
| 1214 | .immediate => |imm| movs(cond, rd, Operand.reg(rm, Operand.Shift.imm(imm, .rotate_right))), | |
| 1215 | .register => |reg| movs(cond, rd, Operand.reg(rm, Operand.Shift.reg(reg, .rotate_right))), | |
| 1216 | }; | |
| 1217 | } | |
| 1145 | 1218 | }; |
| 1146 | 1219 | |
| 1147 | 1220 | test "serialize instructions" { |
| ... | ... | @@ -1262,6 +1335,20 @@ test "aliases" { |
| 1262 | 1335 | .actual = Instruction.push(.al, .{ .r0, .r2 }), |
| 1263 | 1336 | .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }), |
| 1264 | 1337 | }, |
| 1338 | .{ // lsl r4, r5, #5 | |
| 1339 | .actual = Instruction.lsl(.al, .r4, .r5, Instruction.ShiftAmount.imm(5)), | |
| 1340 | .expected = Instruction.mov(.al, .r4, Instruction.Operand.reg( | |
| 1341 | .r5, | |
| 1342 | Instruction.Operand.Shift.imm(5, .logical_left), | |
| 1343 | )), | |
| 1344 | }, | |
| 1345 | .{ // asrs r1, r1, r3 | |
| 1346 | .actual = Instruction.asrs(.al, .r1, .r1, Instruction.ShiftAmount.reg(.r3)), | |
| 1347 | .expected = Instruction.movs(.al, .r1, Instruction.Operand.reg( | |
| 1348 | .r1, | |
| 1349 | Instruction.Operand.Shift.reg(.r3, .arithmetic_right), | |
| 1350 | )), | |
| 1351 | }, | |
| 1265 | 1352 | }; |
| 1266 | 1353 | |
| 1267 | 1354 | for (testcases) |case| { |
test/stage2/arm.zig+86| ... | ... | @@ -204,6 +204,48 @@ pub fn addCases(ctx: *TestContext) !void { |
| 204 | 204 | , |
| 205 | 205 | "123456", |
| 206 | 206 | ); |
| 207 | ||
| 208 | // Bit Shift Left | |
| 209 | case.addCompareOutput( | |
| 210 | \\pub fn main() void { | |
| 211 | \\ var x: u32 = 1; | |
| 212 | \\ assert(x << 1 == 2); | |
| 213 | \\ | |
| 214 | \\ x <<= 1; | |
| 215 | \\ assert(x << 2 == 8); | |
| 216 | \\ assert(x << 3 == 16); | |
| 217 | \\} | |
| 218 | \\ | |
| 219 | \\pub fn assert(ok: bool) void { | |
| 220 | \\ if (!ok) unreachable; // assertion failure | |
| 221 | \\} | |
| 222 | , | |
| 223 | "", | |
| 224 | ); | |
| 225 | ||
| 226 | // Bit Shift Right | |
| 227 | case.addCompareOutput( | |
| 228 | \\pub fn main() void { | |
| 229 | \\ var a: u32 = 1024; | |
| 230 | \\ assert(a >> 1 == 512); | |
| 231 | \\ | |
| 232 | \\ a >>= 1; | |
| 233 | \\ assert(a >> 2 == 128); | |
| 234 | \\ assert(a >> 3 == 64); | |
| 235 | \\ assert(a >> 4 == 32); | |
| 236 | \\ assert(a >> 5 == 16); | |
| 237 | \\ assert(a >> 6 == 8); | |
| 238 | \\ assert(a >> 7 == 4); | |
| 239 | \\ assert(a >> 8 == 2); | |
| 240 | \\ assert(a >> 9 == 1); | |
| 241 | \\} | |
| 242 | \\ | |
| 243 | \\pub fn assert(ok: bool) void { | |
| 244 | \\ if (!ok) unreachable; // assertion failure | |
| 245 | \\} | |
| 246 | , | |
| 247 | "", | |
| 248 | ); | |
| 207 | 249 | } |
| 208 | 250 | |
| 209 | 251 | { |
| ... | ... | @@ -429,4 +471,48 @@ pub fn addCases(ctx: *TestContext) !void { |
| 429 | 471 | "", |
| 430 | 472 | ); |
| 431 | 473 | } |
| 474 | ||
| 475 | { | |
| 476 | var case = ctx.exe("print u32s", linux_arm); | |
| 477 | case.addCompareOutput( | |
| 478 | \\pub fn main() void { | |
| 479 | \\ printNumberHex(0x00000000); | |
| 480 | \\ printNumberHex(0xaaaaaaaa); | |
| 481 | \\ printNumberHex(0xdeadbeef); | |
| 482 | \\ printNumberHex(0x31415926); | |
| 483 | \\} | |
| 484 | \\ | |
| 485 | \\fn printNumberHex(x: u32) void { | |
| 486 | \\ var i: u5 = 28; | |
| 487 | \\ while (true) : (i -= 4) { | |
| 488 | \\ const digit = (x >> i) & 0xf; | |
| 489 | \\ asm volatile ("svc #0" | |
| 490 | \\ : | |
| 491 | \\ : [number] "{r7}" (4), | |
| 492 | \\ [arg1] "{r0}" (1), | |
| 493 | \\ [arg2] "{r1}" (@ptrToInt("0123456789abcdef") + digit), | |
| 494 | \\ [arg3] "{r2}" (1) | |
| 495 | \\ : "memory" | |
| 496 | \\ ); | |
| 497 | \\ | |
| 498 | \\ if (i == 0) break; | |
| 499 | \\ } | |
| 500 | \\ asm volatile ("svc #0" | |
| 501 | \\ : | |
| 502 | \\ : [number] "{r7}" (4), | |
| 503 | \\ [arg1] "{r0}" (1), | |
| 504 | \\ [arg2] "{r1}" (@ptrToInt("\n")), | |
| 505 | \\ [arg3] "{r2}" (1) | |
| 506 | \\ : "memory" | |
| 507 | \\ ); | |
| 508 | \\} | |
| 509 | , | |
| 510 | \\00000000 | |
| 511 | \\aaaaaaaa | |
| 512 | \\deadbeef | |
| 513 | \\31415926 | |
| 514 | \\ | |
| 515 | , | |
| 516 | ); | |
| 517 | } | |
| 432 | 518 | } |