| author | |
| committer | |
| log | 8715b01005c49ff99327a87264ffaa28fb3807a0 |
| tree | 9b93f3921cdabbe1a2d4f1b7dc14234f6d8e39f3 |
| parent | aaacda4df97c03cfcea444c1d77c06f46575049d |
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( |
| 1296 | 1296 | ||
| 1297 | const dest_reg = switch (mir_tag) { | 1297 | const dest_reg = switch (mir_tag) { |
| 1298 | .cmp_shifted_register => undefined, // cmp has no destination register | 1298 | .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; |
| 1301 | 1306 | ||
| ... | @@ -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 | } |
| 1884 | 1891 | ||
| 1885 | fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1892 | fn 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 | } |
| 1889 | 1957 | ||
| 1890 | fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { | 1958 | fn 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), |
| 107 | 107 | ||
| 108 | .eor_immediate => try emit.mirLogicalImmediate(inst), | 108 | .eor_immediate => try emit.mirLogicalImmediate(inst), |
| 109 | .tst_immediate => try emit.mirLogicalImmediate(inst), | ||
| 109 | 110 | ||
| 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), |
| 167 | 168 | ||
| 168 | .mul => try emit.mirDataProcessing3Source(inst), | 169 | .mul => try emit.mirDataProcessing3Source(inst), |
| 170 | .smull => try emit.mirDataProcessing3Source(inst), | ||
| 171 | .umull => try emit.mirDataProcessing3Source(inst), | ||
| 169 | 172 | ||
| 170 | .nop => try emit.mirNop(), | 173 | .nop => try emit.mirNop(), |
| 171 | 174 | ||
| ... | @@ -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 { |
| 674 | 677 | ||
| 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 { |
| 1000 | 1004 | ||
| 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 extract | 147 | /// Signed bitfield extract |
| 148 | sbfx, | 148 | sbfx, |
| 149 | /// Signed multiply long | ||
| 150 | smull, | ||
| 149 | /// Signed extend byte | 151 | /// Signed extend byte |
| 150 | sxtb, | 152 | sxtb, |
| 151 | /// Signed extend halfword | 153 | /// 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 Call | 185 | /// Supervisor Call |
| 184 | svc, | 186 | svc, |
| 187 | /// Test bits (immediate) | ||
| 188 | tst_immediate, | ||
| 185 | /// Unsigned bitfield extract | 189 | /// Unsigned bitfield extract |
| 186 | ubfx, | 190 | ubfx, |
| 191 | /// Unsigned multiply long | ||
| 192 | umull, | ||
| 187 | /// Unsigned extend byte | 193 | /// Unsigned extend byte |
| 188 | uxtb, | 194 | uxtb, |
| 189 | /// Unsigned extend halfword | 195 | /// 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 | } |
| 1411 | 1411 | ||
| 1412 | pub fn tstImmediate(rn: Register, imms: u6, immr: u6, n: u1) Instruction { | ||
| 1413 | return andsImmediate(.xzr, rn, imms, immr, n); | ||
| 1414 | } | ||
| 1415 | |||
| 1412 | // Bitfield | 1416 | // Bitfield |
| 1413 | 1417 | ||
| 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 | } |
| 1566 | 1570 | ||
| 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 | } |
| 1574 | 1587 | ||
| 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 | }; |
| 1794 | 1827 | ||
| 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" { |
| 666 | 666 | ||
| 667 | test "@mulWithOverflow" { | 667 | test "@mulWithOverflow" { |
| 668 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 668 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 669 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 670 | 669 | ||
| 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)); |