authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-20 17:50:08+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-20 17:59:47+01:00
log4a40c0a80c8f7014097f3ac6f523c7e21cda80a9
treea28cc9201ae104bc5a90d4833cda08ad4fc1a451
parent70bff2f4d5f52f17a6e7090e11c300aff81bd87b

stage2: refactor errors thrown for size mismatch in mirMovImpl

* introduce `EmitResult` wrapper struct for easier manipulation of intermediate emit results - this is mainly to track errors such as size mismatch between operands * create an informative `ErrorMsg` directly at the callsite

1 files changed, 110 insertions(+), 36 deletions(-)

src/arch/x86_64/Emit.zig+110-36
...@@ -48,9 +48,31 @@ const InnerError = error{...@@ -48,9 +48,31 @@ const InnerError = error{
48 EmitFail,48 EmitFail,
49};49};
5050
51const EmitError = error{51const EmitResult = union(enum) {
52 OutOfMemory,52 ok: void,
53 OperandSizeMismatch,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};
5577
56const Reloc = struct {78const Reloc = struct {
...@@ -167,9 +189,15 @@ pub fn deinit(emit: *Emit) void {...@@ -167,9 +189,15 @@ pub fn deinit(emit: *Emit) void {
167}189}
168190
169fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {191fn 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
197fn failWithErrorMsg(emit: *Emit, err_msg: *ErrorMsg) InnerError {
170 @setCold(true);198 @setCold(true);
171 assert(emit.err_msg == null);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 return error.EmitFail;201 return error.EmitFail;
174}202}
175203
...@@ -858,24 +886,28 @@ fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE...@@ -858,24 +886,28 @@ fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
858}886}
859887
860fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {888fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
861 return mirMovImpl(889 const res = try mirMovImpl(
890 emit.bin_file.allocator,
862 emit.mir.instructions,891 emit.mir.instructions,
863 emit.mir.extra,892 emit.mir.extra,
864 inst,893 inst,
894 emit.src_loc,
865 emit.code,895 emit.code,
866 ) catch |err| switch (err) {896 );
867 // TODO better formating of operands in case of an error897 switch (res) {
868 error.OperandSizeMismatch => emit.fail("operand size mismatch", .{}),898 .ok => {},
869 else => emit.fail("emit failed with error: {}", .{err}),899 .err => |err_msg| return emit.failWithErrorMsg(err_msg),
870 };900 }
871}901}
872902
873fn mirMovImpl(903fn mirMovImpl(
904 allocator: Allocator,
874 mir_instructions: std.MultiArrayList(Mir.Inst).Slice,905 mir_instructions: std.MultiArrayList(Mir.Inst).Slice,
875 mir_extra: []const u32,906 mir_extra: []const u32,
876 inst: Mir.Inst.Index,907 inst: Mir.Inst.Index,
908 src_loc: Module.SrcLoc,
877 code: *std.ArrayList(u8),909 code: *std.ArrayList(u8),
878) EmitError!void {910) error{OutOfMemory}!EmitResult {
879 const ops = Mir.Ops.decode(mir_instructions.items(.ops)[inst]);911 const ops = Mir.Ops.decode(mir_instructions.items(.ops)[inst]);
880 switch (ops.flags) {912 switch (ops.flags) {
881 0b00 => blk: {913 0b00 => blk: {
...@@ -900,11 +932,31 @@ fn mirMovImpl(...@@ -900,11 +932,31 @@ fn mirMovImpl(
900 encoder.modRm_direct(modrm_ext, ops.reg1.lowId());932 encoder.modRm_direct(modrm_ext, ops.reg1.lowId());
901 switch (ops.reg1.size()) {933 switch (ops.reg1.size()) {
902 8 => {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 encoder.imm8(imm8);946 encoder.imm8(imm8);
905 },947 },
906 16 => {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 encoder.imm16(imm16);960 encoder.imm16(imm16);
909 },961 },
910 32, 64 => {962 32, 64 => {
...@@ -917,7 +969,10 @@ fn mirMovImpl(...@@ -917,7 +969,10 @@ fn mirMovImpl(
917 // mov reg1, reg2969 // mov reg1, reg2
918 // MR970 // MR
919 if (ops.reg1.size() != ops.reg2.size()) {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 const opc: u8 = if (ops.reg1.size() == 8) 0x88 else 0x89;977 const opc: u8 = if (ops.reg1.size() == 8) 0x88 else 0x89;
923 const encoder = try Encoder.init(code, 3);978 const encoder = try Encoder.init(code, 3);
...@@ -954,7 +1009,7 @@ fn mirMovImpl(...@@ -954,7 +1009,7 @@ fn mirMovImpl(
954 // TODO handle 32-bit base register - requires prefix 0x671009 // TODO handle 32-bit base register - requires prefix 0x67
955 // Intel Manual, Vol 1, chapter 3.6 and 3.6.11010 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
956 if (ops.reg2.size() != 64) {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 const encoder = try Encoder.init(code, 8);1014 const encoder = try Encoder.init(code, 8);
960 if (ops.reg1.size() == 16) {1015 if (ops.reg1.size() == 16) {
...@@ -978,7 +1033,7 @@ fn mirMovImpl(...@@ -978,7 +1033,7 @@ fn mirMovImpl(
978 // TODO handle 32-bit base register - requires prefix 0x671033 // TODO handle 32-bit base register - requires prefix 0x67
979 // Intel Manual, Vol 1, chapter 3.6 and 3.6.11034 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
980 if (ops.reg1.size() != 64) {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 if (ops.reg2 == .none) {1038 if (ops.reg2 == .none) {
984 // mov [reg1 + 0], imm321039 // mov [reg1 + 0], imm32
...@@ -1041,7 +1096,7 @@ fn mirMovImpl(...@@ -1041,7 +1096,7 @@ fn mirMovImpl(
1041 // a byte, word or dword ptr.1096 // a byte, word or dword ptr.
1042 // TODO we currently don't have a way to flag imm32 64bit sign extended1097 // TODO we currently don't have a way to flag imm32 64bit sign extended
1043 if (ops.reg1.size() != 64) {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 const payload = mir_instructions.items(.data)[inst].payload;1101 const payload = mir_instructions.items(.data)[inst].payload;
1047 const imm_pair = Mir.extraData(mir_extra, Mir.ImmPair, payload).data;1102 const imm_pair = Mir.extraData(mir_extra, Mir.ImmPair, payload).data;
...@@ -1077,6 +1132,7 @@ fn mirMovImpl(...@@ -1077,6 +1132,7 @@ fn mirMovImpl(
1077 encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());1132 encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
1078 },1133 },
1079 }1134 }
1135 return EmitResult.ok();
1080}1136}
10811137
1082fn immOpSize(imm: i32) u8 {1138fn immOpSize(imm: i32) u8 {
...@@ -1488,23 +1544,36 @@ const Mock = struct {...@@ -1488,23 +1544,36 @@ const Mock = struct {
1488 return result;1544 return result;
1489 }1545 }
14901546
1547 fn dummySrcLoc() Module.SrcLoc {
1548 return .{
1549 .file_scope = undefined,
1550 .parent_decl_node = 0,
1551 .lazy = .unneeded,
1552 };
1553 }
1554
1491 fn testEmitSingleSuccess(1555 fn testEmitSingleSuccess(
1492 self: *Mock,1556 self: *Mock,
1493 mir_inst: Mir.Inst,1557 mir_inst: Mir.Inst,
1494 expected_enc: []const u8,1558 expected_enc: []const u8,
1495 assembly: []const u8,1559 assembly: []const u8,
1496 ) !void {1560 ) !void {
1561 const dummy_src_loc = Mock.dummySrcLoc();
1497 const code_index = self.code.items.len;1562 const code_index = self.code.items.len;
1498 const mir_index = try self.addInst(mir_inst);1563 const mir_index = try self.addInst(mir_inst);
1499 switch (mir_inst.tag) {1564 const res = switch (mir_inst.tag) {
1500 .mov => try mirMovImpl(1565 .mov => try mirMovImpl(
1566 testing.allocator,
1501 self.mir_instructions.slice(),1567 self.mir_instructions.slice(),
1502 self.mir_extra.items,1568 self.mir_extra.items,
1503 mir_index,1569 mir_index,
1570 dummy_src_loc,
1504 &self.code,1571 &self.code,
1505 ),1572 ),
1506 else => unreachable,1573 else => unreachable,
1507 }1574 };
1575 defer res.deinit(testing.allocator);
1576 try testing.expect(res == .ok);
1508 const code_len = if (self.code.items[code_index..].len >= expected_enc.len)1577 const code_len = if (self.code.items[code_index..].len >= expected_enc.len)
1509 expected_enc.len1578 expected_enc.len
1510 else1579 else
...@@ -1512,18 +1581,23 @@ const Mock = struct {...@@ -1512,18 +1581,23 @@ const Mock = struct {
1512 try expectEqualHexStrings(expected_enc, self.code.items[code_index..][0..code_len], assembly);1581 try expectEqualHexStrings(expected_enc, self.code.items[code_index..][0..code_len], assembly);
1513 }1582 }
15141583
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 const index = try self.addInst(mir_inst);1586 const index = try self.addInst(mir_inst);
1517 const res = switch (mir_inst.tag) {1587 const res = switch (mir_inst.tag) {
1518 .mov => mirMovImpl(1588 .mov => try mirMovImpl(
1589 testing.allocator,
1519 self.mir_instructions.slice(),1590 self.mir_instructions.slice(),
1520 self.mir_extra.items,1591 self.mir_extra.items,
1521 index,1592 index,
1593 dummy_src_loc,
1522 &self.code,1594 &self.code,
1523 ),1595 ),
1524 else => unreachable,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};
15291603
...@@ -1560,16 +1634,16 @@ test "mov dst_reg, src_reg" {...@@ -1560,16 +1634,16 @@ test "mov dst_reg, src_reg" {
1560 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .rax }).encode(),1634 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .rax }).encode(),
1561 .data = undefined,1635 .data = undefined,
1562 }, "\x49\x89\xc4", "mov r12, rax");1636 }, "\x49\x89\xc4", "mov r12, rax");
1563 try mock.testEmitSingleError(.{1637 try mock.testEmitSingleFail(.{
1564 .tag = .mov,1638 .tag = .mov,
1565 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .eax }).encode(),1639 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .eax }).encode(),
1566 .data = undefined,1640 .data = undefined,
1567 }, error.OperandSizeMismatch);1641 }, "size mismatch: sizeof Register.r12 != sizeof Register.eax");
1568 try mock.testEmitSingleError(.{1642 try mock.testEmitSingleFail(.{
1569 .tag = .mov,1643 .tag = .mov,
1570 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .rax }).encode(),1644 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .rax }).encode(),
1571 .data = undefined,1645 .data = undefined,
1572 }, error.OperandSizeMismatch);1646 }, "size mismatch: sizeof Register.r12d != sizeof Register.rax");
1573 try mock.testEmitSingleSuccess(.{1647 try mock.testEmitSingleSuccess(.{
1574 .tag = .mov,1648 .tag = .mov,
1575 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .eax }).encode(),1649 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .eax }).encode(),
...@@ -1615,16 +1689,16 @@ test "mov dst_reg, imm" {...@@ -1615,16 +1689,16 @@ test "mov dst_reg, imm" {
1615 .ops = (Mir.Ops{ .reg1 = .cl }).encode(),1689 .ops = (Mir.Ops{ .reg1 = .cl }).encode(),
1616 .data = .{ .imm = 0x10 },1690 .data = .{ .imm = 0x10 },
1617 }, "\xc6\xc1\x10", "mov cl, 0x10");1691 }, "\xc6\xc1\x10", "mov cl, 0x10");
1618 try mock.testEmitSingleError(.{1692 try mock.testEmitSingleFail(.{
1619 .tag = .mov,1693 .tag = .mov,
1620 .ops = (Mir.Ops{ .reg1 = .cx }).encode(),1694 .ops = (Mir.Ops{ .reg1 = .cx }).encode(),
1621 .data = .{ .imm = 0x10000000 },1695 .data = .{ .imm = 0x10000000 },
1622 }, error.OperandSizeMismatch);1696 }, "size mismatch: sizeof Register.cx != sizeof 0x10000000");
1623 try mock.testEmitSingleError(.{1697 try mock.testEmitSingleFail(.{
1624 .tag = .mov,1698 .tag = .mov,
1625 .ops = (Mir.Ops{ .reg1 = .cl }).encode(),1699 .ops = (Mir.Ops{ .reg1 = .cl }).encode(),
1626 .data = .{ .imm = 0x1000 },1700 .data = .{ .imm = 0x1000 },
1627 }, error.OperandSizeMismatch);1701 }, "size mismatch: sizeof Register.cl != sizeof 0x1000");
1628}1702}
16291703
1630test "mov dst_reg, [imm32]" {1704test "mov dst_reg, [imm32]" {
...@@ -1705,11 +1779,11 @@ test "mov [dst_reg + 0], imm" {...@@ -1705,11 +1779,11 @@ test "mov [dst_reg + 0], imm" {
1705 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),1779 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
1706 .data = .{ .imm = 0x10 },1780 .data = .{ .imm = 0x10 },
1707 }, "\xC7\x00\x10\x00\x00\x00", "mov dword ptr [rax + 0], 0x10");1781 }, "\xC7\x00\x10\x00\x00\x00", "mov dword ptr [rax + 0], 0x10");
1708 try mock.testEmitSingleError(.{1782 try mock.testEmitSingleFail(.{
1709 .tag = .mov,1783 .tag = .mov,
1710 .ops = (Mir.Ops{ .reg1 = .eax, .flags = 0b10 }).encode(),1784 .ops = (Mir.Ops{ .reg1 = .eax, .flags = 0b10 }).encode(),
1711 .data = .{ .imm = 0x10 },1785 .data = .{ .imm = 0x10 },
1712 }, error.OperandSizeMismatch);1786 }, "size mismatch: sizeof Register.eax != 8");
1713}1787}
17141788
1715test "mov [dst_reg + imm32], src_reg" {1789test "mov [dst_reg + imm32], src_reg" {
...@@ -1745,11 +1819,11 @@ test "mov [dst_reg + imm32], src_reg" {...@@ -1745,11 +1819,11 @@ test "mov [dst_reg + imm32], src_reg" {
1745 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .eax, .flags = 0b10 }).encode(),1819 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .eax, .flags = 0b10 }).encode(),
1746 .data = .{ .imm = 0x10 },1820 .data = .{ .imm = 0x10 },
1747 }, "\x41\x89\x43\x10", "mov dword ptr [r11 + 0x10], eax");1821 }, "\x41\x89\x43\x10", "mov dword ptr [r11 + 0x10], eax");
1748 try mock.testEmitSingleError(.{1822 try mock.testEmitSingleFail(.{
1749 .tag = .mov,1823 .tag = .mov,
1750 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .ax, .flags = 0b10 }).encode(),1824 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .ax, .flags = 0b10 }).encode(),
1751 .data = .{ .imm = 0x10 },1825 .data = .{ .imm = 0x10 },
1752 }, error.OperandSizeMismatch);1826 }, "size mismatch: sizeof Register.r11w != 8");
1753}1827}
17541828
1755test "mov [dst_reg + imm32], imm32" {1829test "mov [dst_reg + imm32], imm32" {
...@@ -1815,10 +1889,10 @@ test "mov [dst_reg + imm32], imm32" {...@@ -1815,10 +1889,10 @@ test "mov [dst_reg + imm32], imm32" {
1815 .dest_off = 0x10,1889 .dest_off = 0x10,
1816 .operand = 0x20,1890 .operand = 0x20,
1817 });1891 });
1818 try mock.testEmitSingleError(.{1892 try mock.testEmitSingleFail(.{
1819 .tag = .mov,1893 .tag = .mov,
1820 .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b11 }).encode(),1894 .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b11 }).encode(),
1821 .data = .{ .payload = payload },1895 .data = .{ .payload = payload },
1822 }, error.OperandSizeMismatch);1896 }, "size mismatch: sizeof Register.r11d != 8");
1823 }1897 }
1824}1898}