authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-03 12:20:27+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-05 21:43:36+02:00
log8715b01005c49ff99327a87264ffaa28fb3807a0
tree9b93f3921cdabbe1a2d4f1b7dc14234f6d8e39f3
parentaaacda4df97c03cfcea444c1d77c06f46575049d

aarch64: implement mul_with_overflow for <= 32bit ints

Add emitters for `smull`, `umull` and `tst (immediate)` instructions.

5 files changed, 115 insertions(+), 3 deletions(-)

src/arch/aarch64/CodeGen.zig+70-2
...@@ -1296,6 +1296,11 @@ fn binOpRegister(...@@ -1296,6 +1296,11 @@ fn binOpRegister(
12961296
1297 const dest_reg = switch (mir_tag) {1297 const dest_reg = switch (mir_tag) {
1298 .cmp_shifted_register => undefined, // cmp has no destination register1298 .cmp_shifted_register => undefined, // cmp has no destination register
1299 .smull, .umull => blk: {
1300 // TODO can we reuse anything for smull and umull?
1301 const raw_reg = try self.register_manager.allocReg(null);
1302 break :blk raw_reg.to64();
1303 },
1299 else => if (maybe_inst) |inst| blk: {1304 else => if (maybe_inst) |inst| blk: {
1300 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1305 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
13011306
...@@ -1335,6 +1340,8 @@ fn binOpRegister(...@@ -1335,6 +1340,8 @@ fn binOpRegister(
1335 .shift = .lsl,1340 .shift = .lsl,
1336 } },1341 } },
1337 .mul,1342 .mul,
1343 .smull,
1344 .umull,
1338 .lsl_register,1345 .lsl_register,
1339 .asr_register,1346 .asr_register,
1340 .lsr_register,1347 .lsr_register,
...@@ -1883,8 +1890,69 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1883,8 +1890,69 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
1883}1890}
18841891
1885fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1892fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1886 _ = inst;1893 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1887 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch});1894 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1895 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
1896 const result: MCValue = result: {
1897 const lhs = try self.resolveInst(extra.lhs);
1898 const rhs = try self.resolveInst(extra.rhs);
1899 const lhs_ty = self.air.typeOf(extra.lhs);
1900 const rhs_ty = self.air.typeOf(extra.rhs);
1901
1902 const tuple_ty = self.air.typeOfIndex(inst);
1903 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*));
1904 const tuple_align = tuple_ty.abiAlignment(self.target.*);
1905 const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*));
1906
1907 switch (lhs_ty.zigTypeTag()) {
1908 .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),
1909 .Int => {
1910 const int_info = lhs_ty.intInfo(self.target.*);
1911
1912 if (int_info.bits <= 32) {
1913 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
1914
1915 try self.spillCompareFlagsIfOccupied();
1916 self.compare_flags_inst = null;
1917
1918 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
1919 .signed => .smull,
1920 .unsigned => .umull,
1921 };
1922
1923 const dest = try self.binOpRegister(base_tag, null, lhs, rhs, lhs_ty, rhs_ty);
1924 const dest_reg = dest.register;
1925 self.register_manager.freezeRegs(&.{dest_reg});
1926 defer self.register_manager.unfreezeRegs(&.{dest_reg});
1927
1928 const truncated_reg = try self.register_manager.allocReg(null);
1929 self.register_manager.freezeRegs(&.{truncated_reg});
1930 defer self.register_manager.unfreezeRegs(&.{truncated_reg});
1931
1932 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
1933 _ = try self.binOp(
1934 .cmp_eq,
1935 null,
1936 dest,
1937 .{ .register = truncated_reg },
1938 Type.usize,
1939 Type.usize,
1940 );
1941
1942 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
1943 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{
1944 .compare_flags_unsigned = .neq,
1945 });
1946
1947 break :result MCValue{ .stack_offset = stack_offset };
1948 } else if (int_info.bits <= 64) {
1949 return self.fail("TODO implement mul_with_overflow for ints", .{});
1950 } else return self.fail("TODO implmenet mul_with_overflow for integers > u64/i64", .{});
1951 },
1952 else => unreachable,
1953 }
1954 };
1955 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
1888}1956}
18891957
1890fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {1958fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
src/arch/aarch64/Emit.zig+6
...@@ -106,6 +106,7 @@ pub fn emitMir(...@@ -106,6 +106,7 @@ pub fn emitMir(
106 .call_extern => try emit.mirCallExtern(inst),106 .call_extern => try emit.mirCallExtern(inst),
107107
108 .eor_immediate => try emit.mirLogicalImmediate(inst),108 .eor_immediate => try emit.mirLogicalImmediate(inst),
109 .tst_immediate => try emit.mirLogicalImmediate(inst),
109110
110 .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),111 .add_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
111 .adds_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),112 .adds_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
...@@ -166,6 +167,8 @@ pub fn emitMir(...@@ -166,6 +167,8 @@ pub fn emitMir(
166 .movz => try emit.mirMoveWideImmediate(inst),167 .movz => try emit.mirMoveWideImmediate(inst),
167168
168 .mul => try emit.mirDataProcessing3Source(inst),169 .mul => try emit.mirDataProcessing3Source(inst),
170 .smull => try emit.mirDataProcessing3Source(inst),
171 .umull => try emit.mirDataProcessing3Source(inst),
169172
170 .nop => try emit.mirNop(),173 .nop => try emit.mirNop(),
171174
...@@ -674,6 +677,7 @@ fn mirLogicalImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -674,6 +677,7 @@ fn mirLogicalImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
674677
675 switch (tag) {678 switch (tag) {
676 .eor_immediate => try emit.writeInstruction(Instruction.eorImmediate(rd, rn, imms, immr, n)),679 .eor_immediate => try emit.writeInstruction(Instruction.eorImmediate(rd, rn, imms, immr, n)),
680 .tst_immediate => try emit.writeInstruction(Instruction.tstImmediate(rn, imms, immr, n)),
677 else => unreachable,681 else => unreachable,
678 }682 }
679}683}
...@@ -1000,6 +1004,8 @@ fn mirDataProcessing3Source(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -1000,6 +1004,8 @@ fn mirDataProcessing3Source(emit: *Emit, inst: Mir.Inst.Index) !void {
10001004
1001 switch (tag) {1005 switch (tag) {
1002 .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)),1006 .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)),
1007 .smull => try emit.writeInstruction(Instruction.smull(rrr.rd, rrr.rn, rrr.rm)),
1008 .umull => try emit.writeInstruction(Instruction.umull(rrr.rd, rrr.rn, rrr.rm)),
1003 else => unreachable,1009 else => unreachable,
1004 }1010 }
1005}1011}
src/arch/aarch64/Mir.zig+6
...@@ -146,6 +146,8 @@ pub const Inst = struct {...@@ -146,6 +146,8 @@ pub const Inst = struct {
146 ret,146 ret,
147 /// Signed bitfield extract147 /// Signed bitfield extract
148 sbfx,148 sbfx,
149 /// Signed multiply long
150 smull,
149 /// Signed extend byte151 /// Signed extend byte
150 sxtb,152 sxtb,
151 /// Signed extend halfword153 /// Signed extend halfword
...@@ -182,8 +184,12 @@ pub const Inst = struct {...@@ -182,8 +184,12 @@ pub const Inst = struct {
182 subs_shifted_register,184 subs_shifted_register,
183 /// Supervisor Call185 /// Supervisor Call
184 svc,186 svc,
187 /// Test bits (immediate)
188 tst_immediate,
185 /// Unsigned bitfield extract189 /// Unsigned bitfield extract
186 ubfx,190 ubfx,
191 /// Unsigned multiply long
192 umull,
187 /// Unsigned extend byte193 /// Unsigned extend byte
188 uxtb,194 uxtb,
189 /// Unsigned extend halfword195 /// Unsigned extend halfword
src/arch/aarch64/bits.zig+33
...@@ -1409,6 +1409,10 @@ pub const Instruction = union(enum) {...@@ -1409,6 +1409,10 @@ pub const Instruction = union(enum) {
1409 return logicalImmediate(0b11, rd, rn, imms, immr, n);1409 return logicalImmediate(0b11, rd, rn, imms, immr, n);
1410 }1410 }
14111411
1412 pub fn tstImmediate(rn: Register, imms: u6, immr: u6, n: u1) Instruction {
1413 return andsImmediate(.xzr, rn, imms, immr, n);
1414 }
1415
1412 // Bitfield1416 // Bitfield
14131417
1414 pub fn sbfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction {1418 pub fn sbfm(rd: Register, rn: Register, immr: u6, imms: u6) Instruction {
...@@ -1564,6 +1568,15 @@ pub const Instruction = union(enum) {...@@ -1564,6 +1568,15 @@ pub const Instruction = union(enum) {
1564 return dataProcessing3Source(0b00, 0b000, 0b0, rd, rn, rm, ra);1568 return dataProcessing3Source(0b00, 0b000, 0b0, rd, rn, rm, ra);
1565 }1569 }
15661570
1571 pub fn smaddl(rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
1572 return dataProcessing3Source(0b00, 0b001, 0b0, rd, rn, rm, ra);
1573 }
1574
1575 pub fn umaddl(rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
1576 assert(rd.size() == 64);
1577 return dataProcessing3Source(0b00, 0b101, 0b0, rd, rn, rm, ra);
1578 }
1579
1567 pub fn msub(rd: Register, rn: Register, rm: Register, ra: Register) Instruction {1580 pub fn msub(rd: Register, rn: Register, rm: Register, ra: Register) Instruction {
1568 return dataProcessing3Source(0b00, 0b000, 0b1, rd, rn, rm, ra);1581 return dataProcessing3Source(0b00, 0b000, 0b1, rd, rn, rm, ra);
1569 }1582 }
...@@ -1572,6 +1585,14 @@ pub const Instruction = union(enum) {...@@ -1572,6 +1585,14 @@ pub const Instruction = union(enum) {
1572 return madd(rd, rn, rm, .xzr);1585 return madd(rd, rn, rm, .xzr);
1573 }1586 }
15741587
1588 pub fn smull(rd: Register, rn: Register, rm: Register) Instruction {
1589 return smaddl(rd, rn, rm, .xzr);
1590 }
1591
1592 pub fn umull(rd: Register, rn: Register, rm: Register) Instruction {
1593 return umaddl(rd, rn, rm, .xzr);
1594 }
1595
1575 pub fn mneg(rd: Register, rn: Register, rm: Register) Instruction {1596 pub fn mneg(rd: Register, rn: Register, rm: Register) Instruction {
1576 return msub(rd, rn, rm, .xzr);1597 return msub(rd, rn, rm, .xzr);
1577 }1598 }
...@@ -1790,6 +1811,18 @@ test "serialize instructions" {...@@ -1790,6 +1811,18 @@ test "serialize instructions" {
1790 .inst = Instruction.lsrImmediate(.x4, .x2, 63),1811 .inst = Instruction.lsrImmediate(.x4, .x2, 63),
1791 .expected = 0b1_10_100110_1_111111_111111_00010_00100,1812 .expected = 0b1_10_100110_1_111111_111111_00010_00100,
1792 },1813 },
1814 .{ // umull x0, w0, w1
1815 .inst = Instruction.umull(.x0, .w0, .w1),
1816 .expected = 0b1_00_11011_1_01_00001_0_11111_00000_00000,
1817 },
1818 .{ // smull x0, w0, w1
1819 .inst = Instruction.smull(.x0, .w0, .w1),
1820 .expected = 0b1_00_11011_0_01_00001_0_11111_00000_00000,
1821 },
1822 .{ // tst x0, #0xffffffff00000000
1823 .inst = Instruction.tstImmediate(.x0, 0b011111, 0b100000, 0b1),
1824 .expected = 0b1_11_100100_1_100000_011111_00000_11111,
1825 },
1793 };1826 };
17941827
1795 for (testcases) |case| {1828 for (testcases) |case| {
test/behavior/math.zig-1
...@@ -666,7 +666,6 @@ test "small int addition" {...@@ -666,7 +666,6 @@ test "small int addition" {
666666
667test "@mulWithOverflow" {667test "@mulWithOverflow" {
668 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO668 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
669 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
670669
671 var result: u8 = undefined;670 var result: u8 = undefined;
672 try expect(@mulWithOverflow(u8, 86, 3, &result));671 try expect(@mulWithOverflow(u8, 86, 3, &result));