| ... | ... | @@ -48,9 +48,31 @@ const InnerError = error{ |
| 48 | 48 | EmitFail, |
| 49 | 49 | }; |
| 50 | 50 | |
| 51 | | const EmitError = error{ |
| 52 | | OutOfMemory, |
| 53 | | OperandSizeMismatch, |
| 51 | const EmitResult = union(enum) { |
| 52 | ok: void, |
| 53 | err: *ErrorMsg, |
| 54 | |
| 55 | fn ok() EmitResult { |
| 56 | return EmitResult{ .ok = .{} }; |
| 57 | } |
| 58 | |
| 59 | fn err( |
| 60 | allocator: Allocator, |
| 61 | src_loc: Module.SrcLoc, |
| 62 | comptime format: []const u8, |
| 63 | args: anytype, |
| 64 | ) error{OutOfMemory}!EmitResult { |
| 65 | return EmitResult{ |
| 66 | .err = try ErrorMsg.create(allocator, src_loc, format, args), |
| 67 | }; |
| 68 | } |
| 69 | |
| 70 | fn deinit(res: EmitResult, allocator: Allocator) void { |
| 71 | switch (res) { |
| 72 | .ok => {}, |
| 73 | .err => |err_msg| err_msg.destroy(allocator), |
| 74 | } |
| 75 | } |
| 54 | 76 | }; |
| 55 | 77 | |
| 56 | 78 | const Reloc = struct { |
| ... | ... | @@ -167,9 +189,15 @@ pub fn deinit(emit: *Emit) void { |
| 167 | 189 | } |
| 168 | 190 | |
| 169 | 191 | fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { |
| 192 | @setCold(true); |
| 193 | const err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); |
| 194 | return emit.failWithErrorMsg(err_msg); |
| 195 | } |
| 196 | |
| 197 | fn failWithErrorMsg(emit: *Emit, err_msg: *ErrorMsg) InnerError { |
| 170 | 198 | @setCold(true); |
| 171 | 199 | assert(emit.err_msg == null); |
| 172 | | emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); |
| 200 | emit.err_msg = err_msg; |
| 173 | 201 | return error.EmitFail; |
| 174 | 202 | } |
| 175 | 203 | |
| ... | ... | @@ -858,24 +886,28 @@ fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE |
| 858 | 886 | } |
| 859 | 887 | |
| 860 | 888 | fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 861 | | return mirMovImpl( |
| 889 | const res = try mirMovImpl( |
| 890 | emit.bin_file.allocator, |
| 862 | 891 | emit.mir.instructions, |
| 863 | 892 | emit.mir.extra, |
| 864 | 893 | inst, |
| 894 | emit.src_loc, |
| 865 | 895 | emit.code, |
| 866 | | ) catch |err| switch (err) { |
| 867 | | // TODO better formating of operands in case of an error |
| 868 | | error.OperandSizeMismatch => emit.fail("operand size mismatch", .{}), |
| 869 | | else => emit.fail("emit failed with error: {}", .{err}), |
| 870 | | }; |
| 896 | ); |
| 897 | switch (res) { |
| 898 | .ok => {}, |
| 899 | .err => |err_msg| return emit.failWithErrorMsg(err_msg), |
| 900 | } |
| 871 | 901 | } |
| 872 | 902 | |
| 873 | 903 | fn mirMovImpl( |
| 904 | allocator: Allocator, |
| 874 | 905 | mir_instructions: std.MultiArrayList(Mir.Inst).Slice, |
| 875 | 906 | mir_extra: []const u32, |
| 876 | 907 | inst: Mir.Inst.Index, |
| 908 | src_loc: Module.SrcLoc, |
| 877 | 909 | code: *std.ArrayList(u8), |
| 878 | | ) EmitError!void { |
| 910 | ) error{OutOfMemory}!EmitResult { |
| 879 | 911 | const ops = Mir.Ops.decode(mir_instructions.items(.ops)[inst]); |
| 880 | 912 | switch (ops.flags) { |
| 881 | 913 | 0b00 => blk: { |
| ... | ... | @@ -900,11 +932,31 @@ fn mirMovImpl( |
| 900 | 932 | encoder.modRm_direct(modrm_ext, ops.reg1.lowId()); |
| 901 | 933 | switch (ops.reg1.size()) { |
| 902 | 934 | 8 => { |
| 903 | | const imm8 = math.cast(i8, imm) catch return error.OperandSizeMismatch; |
| 935 | const imm8 = math.cast(i8, imm) catch { |
| 936 | return EmitResult.err( |
| 937 | allocator, |
| 938 | src_loc, |
| 939 | "size mismatch: sizeof {} != sizeof 0x{x}", |
| 940 | .{ |
| 941 | ops.reg1, |
| 942 | imm, |
| 943 | }, |
| 944 | ); |
| 945 | }; |
| 904 | 946 | encoder.imm8(imm8); |
| 905 | 947 | }, |
| 906 | 948 | 16 => { |
| 907 | | const imm16 = math.cast(i16, imm) catch return error.OperandSizeMismatch; |
| 949 | const imm16 = math.cast(i16, imm) catch { |
| 950 | return EmitResult.err( |
| 951 | allocator, |
| 952 | src_loc, |
| 953 | "size mismatch: sizeof {} != sizeof 0x{x}", |
| 954 | .{ |
| 955 | ops.reg1, |
| 956 | imm, |
| 957 | }, |
| 958 | ); |
| 959 | }; |
| 908 | 960 | encoder.imm16(imm16); |
| 909 | 961 | }, |
| 910 | 962 | 32, 64 => { |
| ... | ... | @@ -917,7 +969,10 @@ fn mirMovImpl( |
| 917 | 969 | // mov reg1, reg2 |
| 918 | 970 | // MR |
| 919 | 971 | if (ops.reg1.size() != ops.reg2.size()) { |
| 920 | | return error.OperandSizeMismatch; |
| 972 | return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != sizeof {}", .{ |
| 973 | ops.reg1, |
| 974 | ops.reg2, |
| 975 | }); |
| 921 | 976 | } |
| 922 | 977 | const opc: u8 = if (ops.reg1.size() == 8) 0x88 else 0x89; |
| 923 | 978 | const encoder = try Encoder.init(code, 3); |
| ... | ... | @@ -954,7 +1009,7 @@ fn mirMovImpl( |
| 954 | 1009 | // TODO handle 32-bit base register - requires prefix 0x67 |
| 955 | 1010 | // Intel Manual, Vol 1, chapter 3.6 and 3.6.1 |
| 956 | 1011 | if (ops.reg2.size() != 64) { |
| 957 | | return error.OperandSizeMismatch; |
| 1012 | return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != 8", .{ops.reg2}); |
| 958 | 1013 | } |
| 959 | 1014 | const encoder = try Encoder.init(code, 8); |
| 960 | 1015 | if (ops.reg1.size() == 16) { |
| ... | ... | @@ -978,7 +1033,7 @@ fn mirMovImpl( |
| 978 | 1033 | // TODO handle 32-bit base register - requires prefix 0x67 |
| 979 | 1034 | // Intel Manual, Vol 1, chapter 3.6 and 3.6.1 |
| 980 | 1035 | if (ops.reg1.size() != 64) { |
| 981 | | return error.OperandSizeMismatch; |
| 1036 | return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != 8", .{ops.reg1}); |
| 982 | 1037 | } |
| 983 | 1038 | if (ops.reg2 == .none) { |
| 984 | 1039 | // mov [reg1 + 0], imm32 |
| ... | ... | @@ -1041,7 +1096,7 @@ fn mirMovImpl( |
| 1041 | 1096 | // a byte, word or dword ptr. |
| 1042 | 1097 | // TODO we currently don't have a way to flag imm32 64bit sign extended |
| 1043 | 1098 | if (ops.reg1.size() != 64) { |
| 1044 | | return error.OperandSizeMismatch; |
| 1099 | return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != 8", .{ops.reg1}); |
| 1045 | 1100 | } |
| 1046 | 1101 | const payload = mir_instructions.items(.data)[inst].payload; |
| 1047 | 1102 | const imm_pair = Mir.extraData(mir_extra, Mir.ImmPair, payload).data; |
| ... | ... | @@ -1077,6 +1132,7 @@ fn mirMovImpl( |
| 1077 | 1132 | encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId()); |
| 1078 | 1133 | }, |
| 1079 | 1134 | } |
| 1135 | return EmitResult.ok(); |
| 1080 | 1136 | } |
| 1081 | 1137 | |
| 1082 | 1138 | fn immOpSize(imm: i32) u8 { |
| ... | ... | @@ -1488,23 +1544,36 @@ const Mock = struct { |
| 1488 | 1544 | return result; |
| 1489 | 1545 | } |
| 1490 | 1546 | |
| 1547 | fn dummySrcLoc() Module.SrcLoc { |
| 1548 | return .{ |
| 1549 | .file_scope = undefined, |
| 1550 | .parent_decl_node = 0, |
| 1551 | .lazy = .unneeded, |
| 1552 | }; |
| 1553 | } |
| 1554 | |
| 1491 | 1555 | fn testEmitSingleSuccess( |
| 1492 | 1556 | self: *Mock, |
| 1493 | 1557 | mir_inst: Mir.Inst, |
| 1494 | 1558 | expected_enc: []const u8, |
| 1495 | 1559 | assembly: []const u8, |
| 1496 | 1560 | ) !void { |
| 1561 | const dummy_src_loc = Mock.dummySrcLoc(); |
| 1497 | 1562 | const code_index = self.code.items.len; |
| 1498 | 1563 | const mir_index = try self.addInst(mir_inst); |
| 1499 | | switch (mir_inst.tag) { |
| 1564 | const res = switch (mir_inst.tag) { |
| 1500 | 1565 | .mov => try mirMovImpl( |
| 1566 | testing.allocator, |
| 1501 | 1567 | self.mir_instructions.slice(), |
| 1502 | 1568 | self.mir_extra.items, |
| 1503 | 1569 | mir_index, |
| 1570 | dummy_src_loc, |
| 1504 | 1571 | &self.code, |
| 1505 | 1572 | ), |
| 1506 | 1573 | else => unreachable, |
| 1507 | | } |
| 1574 | }; |
| 1575 | defer res.deinit(testing.allocator); |
| 1576 | try testing.expect(res == .ok); |
| 1508 | 1577 | const code_len = if (self.code.items[code_index..].len >= expected_enc.len) |
| 1509 | 1578 | expected_enc.len |
| 1510 | 1579 | else |
| ... | ... | @@ -1512,18 +1581,23 @@ const Mock = struct { |
| 1512 | 1581 | try expectEqualHexStrings(expected_enc, self.code.items[code_index..][0..code_len], assembly); |
| 1513 | 1582 | } |
| 1514 | 1583 | |
| 1515 | | fn testEmitSingleError(self: *Mock, mir_inst: Mir.Inst, err: EmitError) !void { |
| 1584 | fn testEmitSingleFail(self: *Mock, mir_inst: Mir.Inst, msg: []const u8) !void { |
| 1585 | const dummy_src_loc = Mock.dummySrcLoc(); |
| 1516 | 1586 | const index = try self.addInst(mir_inst); |
| 1517 | 1587 | const res = switch (mir_inst.tag) { |
| 1518 | | .mov => mirMovImpl( |
| 1588 | .mov => try mirMovImpl( |
| 1589 | testing.allocator, |
| 1519 | 1590 | self.mir_instructions.slice(), |
| 1520 | 1591 | self.mir_extra.items, |
| 1521 | 1592 | index, |
| 1593 | dummy_src_loc, |
| 1522 | 1594 | &self.code, |
| 1523 | 1595 | ), |
| 1524 | 1596 | else => unreachable, |
| 1525 | 1597 | }; |
| 1526 | | try testing.expectError(err, res); |
| 1598 | defer res.deinit(testing.allocator); |
| 1599 | try testing.expect(res == .err); |
| 1600 | try testing.expectEqualStrings(msg, res.err.msg); |
| 1527 | 1601 | } |
| 1528 | 1602 | }; |
| 1529 | 1603 | |
| ... | ... | @@ -1560,16 +1634,16 @@ test "mov dst_reg, src_reg" { |
| 1560 | 1634 | .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .rax }).encode(), |
| 1561 | 1635 | .data = undefined, |
| 1562 | 1636 | }, "\x49\x89\xc4", "mov r12, rax"); |
| 1563 | | try mock.testEmitSingleError(.{ |
| 1637 | try mock.testEmitSingleFail(.{ |
| 1564 | 1638 | .tag = .mov, |
| 1565 | 1639 | .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .eax }).encode(), |
| 1566 | 1640 | .data = undefined, |
| 1567 | | }, error.OperandSizeMismatch); |
| 1568 | | try mock.testEmitSingleError(.{ |
| 1641 | }, "size mismatch: sizeof Register.r12 != sizeof Register.eax"); |
| 1642 | try mock.testEmitSingleFail(.{ |
| 1569 | 1643 | .tag = .mov, |
| 1570 | 1644 | .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .rax }).encode(), |
| 1571 | 1645 | .data = undefined, |
| 1572 | | }, error.OperandSizeMismatch); |
| 1646 | }, "size mismatch: sizeof Register.r12d != sizeof Register.rax"); |
| 1573 | 1647 | try mock.testEmitSingleSuccess(.{ |
| 1574 | 1648 | .tag = .mov, |
| 1575 | 1649 | .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .eax }).encode(), |
| ... | ... | @@ -1615,16 +1689,16 @@ test "mov dst_reg, imm" { |
| 1615 | 1689 | .ops = (Mir.Ops{ .reg1 = .cl }).encode(), |
| 1616 | 1690 | .data = .{ .imm = 0x10 }, |
| 1617 | 1691 | }, "\xc6\xc1\x10", "mov cl, 0x10"); |
| 1618 | | try mock.testEmitSingleError(.{ |
| 1692 | try mock.testEmitSingleFail(.{ |
| 1619 | 1693 | .tag = .mov, |
| 1620 | 1694 | .ops = (Mir.Ops{ .reg1 = .cx }).encode(), |
| 1621 | 1695 | .data = .{ .imm = 0x10000000 }, |
| 1622 | | }, error.OperandSizeMismatch); |
| 1623 | | try mock.testEmitSingleError(.{ |
| 1696 | }, "size mismatch: sizeof Register.cx != sizeof 0x10000000"); |
| 1697 | try mock.testEmitSingleFail(.{ |
| 1624 | 1698 | .tag = .mov, |
| 1625 | 1699 | .ops = (Mir.Ops{ .reg1 = .cl }).encode(), |
| 1626 | 1700 | .data = .{ .imm = 0x1000 }, |
| 1627 | | }, error.OperandSizeMismatch); |
| 1701 | }, "size mismatch: sizeof Register.cl != sizeof 0x1000"); |
| 1628 | 1702 | } |
| 1629 | 1703 | |
| 1630 | 1704 | test "mov dst_reg, [imm32]" { |
| ... | ... | @@ -1705,11 +1779,11 @@ test "mov [dst_reg + 0], imm" { |
| 1705 | 1779 | .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(), |
| 1706 | 1780 | .data = .{ .imm = 0x10 }, |
| 1707 | 1781 | }, "\xC7\x00\x10\x00\x00\x00", "mov dword ptr [rax + 0], 0x10"); |
| 1708 | | try mock.testEmitSingleError(.{ |
| 1782 | try mock.testEmitSingleFail(.{ |
| 1709 | 1783 | .tag = .mov, |
| 1710 | 1784 | .ops = (Mir.Ops{ .reg1 = .eax, .flags = 0b10 }).encode(), |
| 1711 | 1785 | .data = .{ .imm = 0x10 }, |
| 1712 | | }, error.OperandSizeMismatch); |
| 1786 | }, "size mismatch: sizeof Register.eax != 8"); |
| 1713 | 1787 | } |
| 1714 | 1788 | |
| 1715 | 1789 | test "mov [dst_reg + imm32], src_reg" { |
| ... | ... | @@ -1745,11 +1819,11 @@ test "mov [dst_reg + imm32], src_reg" { |
| 1745 | 1819 | .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .eax, .flags = 0b10 }).encode(), |
| 1746 | 1820 | .data = .{ .imm = 0x10 }, |
| 1747 | 1821 | }, "\x41\x89\x43\x10", "mov dword ptr [r11 + 0x10], eax"); |
| 1748 | | try mock.testEmitSingleError(.{ |
| 1822 | try mock.testEmitSingleFail(.{ |
| 1749 | 1823 | .tag = .mov, |
| 1750 | 1824 | .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .ax, .flags = 0b10 }).encode(), |
| 1751 | 1825 | .data = .{ .imm = 0x10 }, |
| 1752 | | }, error.OperandSizeMismatch); |
| 1826 | }, "size mismatch: sizeof Register.r11w != 8"); |
| 1753 | 1827 | } |
| 1754 | 1828 | |
| 1755 | 1829 | test "mov [dst_reg + imm32], imm32" { |
| ... | ... | @@ -1815,10 +1889,10 @@ test "mov [dst_reg + imm32], imm32" { |
| 1815 | 1889 | .dest_off = 0x10, |
| 1816 | 1890 | .operand = 0x20, |
| 1817 | 1891 | }); |
| 1818 | | try mock.testEmitSingleError(.{ |
| 1892 | try mock.testEmitSingleFail(.{ |
| 1819 | 1893 | .tag = .mov, |
| 1820 | 1894 | .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b11 }).encode(), |
| 1821 | 1895 | .data = .{ .payload = payload }, |
| 1822 | | }, error.OperandSizeMismatch); |
| 1896 | }, "size mismatch: sizeof Register.r11d != 8"); |
| 1823 | 1897 | } |
| 1824 | 1898 | } |