authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-20 19:05:05-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-08-20 19:05:05-04:00
loga2438357e1f0f3853886a128f272cd27c714b176
treef5b200eedeee3aab50b0bfe69e50e2e1a72bb8eb
parent0cd361219c107bce48f2d7b44c6f3dd05ea6ccf4
parent224fe49be23c44095d39297a4986782bd19ce8b2
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #9597 from joachimschmidt557/stage2-arm-bitshift

stage2 ARM: implement bitshifts

3 files changed, 239 insertions(+), 4 deletions(-)

src/codegen.zig+66-4
......@@ -1602,15 +1602,53 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16021602 }
16031603
16041604 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
16051636 const lhs = try self.resolveInst(op_lhs);
16061637 const rhs = try self.resolveInst(op_rhs);
16071638
16081639 const lhs_is_register = lhs == .register;
16091640 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 };
16111645 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
16121646 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
16131647 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 };
16141652
16151653 // Destination must be a register
16161654 var dst_mcv: MCValue = undefined;
......@@ -1627,7 +1665,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
16271665 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
16281666 }
16291667 dst_mcv = lhs;
1630 } else if (reuse_rhs) {
1668 } else if (reuse_rhs and can_swap_lhs_and_rhs) {
16311669 // Allocate 0 or 1 registers
16321670 if (!lhs_is_register and lhs_should_be_register) {
16331671 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 {
16661704 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) };
16671705 lhs_mcv = dst_mcv;
16681706 }
1669 } else if (rhs_should_be_register) {
1707 } else if (rhs_should_be_register and can_swap_lhs_and_rhs) {
16701708 // LHS is immediate
16711709 if (rhs_is_register) {
16721710 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 {
16931731 rhs_mcv,
16941732 swap_lhs_and_rhs,
16951733 op,
1734 signedness,
16961735 );
16971736 return dst_mcv;
16981737 }
......@@ -1704,6 +1743,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17041743 rhs_mcv: MCValue,
17051744 swap_lhs_and_rhs: bool,
17061745 op: Air.Inst.Tag,
1746 signedness: std.builtin.Signedness,
17071747 ) !void {
17081748 assert(lhs_mcv == .register or rhs_mcv == .register);
17091749
......@@ -1749,6 +1789,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
17491789 .cmp_eq => {
17501790 writeInt(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32());
17511791 },
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 },
17521813 else => unreachable, // not a binary instruction
17531814 }
17541815 }
......@@ -2999,7 +3060,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29993060 }
30003061
30013062 // 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);
30033065
30043066 break :result switch (ty.isSignedInt()) {
30053067 true => MCValue{ .compare_flags_signed = op },
src/codegen/arm.zig+87
......@@ -1142,6 +1142,79 @@ pub const Instruction = union(enum) {
11421142 return stmdb(cond, .sp, true, @bitCast(RegisterList, register_list));
11431143 }
11441144 }
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 }
11451218};
11461219
11471220test "serialize instructions" {
......@@ -1262,6 +1335,20 @@ test "aliases" {
12621335 .actual = Instruction.push(.al, .{ .r0, .r2 }),
12631336 .expected = Instruction.stmdb(.al, .sp, true, .{ .r0 = true, .r2 = true }),
12641337 },
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 },
12651352 };
12661353
12671354 for (testcases) |case| {
test/stage2/arm.zig+86
......@@ -204,6 +204,48 @@ pub fn addCases(ctx: *TestContext) !void {
204204 ,
205205 "123456",
206206 );
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 );
207249 }
208250
209251 {
......@@ -429,4 +471,48 @@ pub fn addCases(ctx: *TestContext) !void {
429471 "",
430472 );
431473 }
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 }
432518}