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{
4848 EmitFail,
4949};
5050
51const EmitError = error{
52 OutOfMemory,
53 OperandSizeMismatch,
51const 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 }
5476};
5577
5678const Reloc = struct {
......@@ -167,9 +189,15 @@ pub fn deinit(emit: *Emit) void {
167189}
168190
169191fn 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 {
170198 @setCold(true);
171199 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;
173201 return error.EmitFail;
174202}
175203
......@@ -858,24 +886,28 @@ fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
858886}
859887
860888fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
861 return mirMovImpl(
889 const res = try mirMovImpl(
890 emit.bin_file.allocator,
862891 emit.mir.instructions,
863892 emit.mir.extra,
864893 inst,
894 emit.src_loc,
865895 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 }
871901}
872902
873903fn mirMovImpl(
904 allocator: Allocator,
874905 mir_instructions: std.MultiArrayList(Mir.Inst).Slice,
875906 mir_extra: []const u32,
876907 inst: Mir.Inst.Index,
908 src_loc: Module.SrcLoc,
877909 code: *std.ArrayList(u8),
878) EmitError!void {
910) error{OutOfMemory}!EmitResult {
879911 const ops = Mir.Ops.decode(mir_instructions.items(.ops)[inst]);
880912 switch (ops.flags) {
881913 0b00 => blk: {
......@@ -900,11 +932,31 @@ fn mirMovImpl(
900932 encoder.modRm_direct(modrm_ext, ops.reg1.lowId());
901933 switch (ops.reg1.size()) {
902934 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 };
904946 encoder.imm8(imm8);
905947 },
906948 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 };
908960 encoder.imm16(imm16);
909961 },
910962 32, 64 => {
......@@ -917,7 +969,10 @@ fn mirMovImpl(
917969 // mov reg1, reg2
918970 // MR
919971 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 });
921976 }
922977 const opc: u8 = if (ops.reg1.size() == 8) 0x88 else 0x89;
923978 const encoder = try Encoder.init(code, 3);
......@@ -954,7 +1009,7 @@ fn mirMovImpl(
9541009 // TODO handle 32-bit base register - requires prefix 0x67
9551010 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
9561011 if (ops.reg2.size() != 64) {
957 return error.OperandSizeMismatch;
1012 return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != 8", .{ops.reg2});
9581013 }
9591014 const encoder = try Encoder.init(code, 8);
9601015 if (ops.reg1.size() == 16) {
......@@ -978,7 +1033,7 @@ fn mirMovImpl(
9781033 // TODO handle 32-bit base register - requires prefix 0x67
9791034 // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
9801035 if (ops.reg1.size() != 64) {
981 return error.OperandSizeMismatch;
1036 return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != 8", .{ops.reg1});
9821037 }
9831038 if (ops.reg2 == .none) {
9841039 // mov [reg1 + 0], imm32
......@@ -1041,7 +1096,7 @@ fn mirMovImpl(
10411096 // a byte, word or dword ptr.
10421097 // TODO we currently don't have a way to flag imm32 64bit sign extended
10431098 if (ops.reg1.size() != 64) {
1044 return error.OperandSizeMismatch;
1099 return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != 8", .{ops.reg1});
10451100 }
10461101 const payload = mir_instructions.items(.data)[inst].payload;
10471102 const imm_pair = Mir.extraData(mir_extra, Mir.ImmPair, payload).data;
......@@ -1077,6 +1132,7 @@ fn mirMovImpl(
10771132 encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
10781133 },
10791134 }
1135 return EmitResult.ok();
10801136}
10811137
10821138fn immOpSize(imm: i32) u8 {
......@@ -1488,23 +1544,36 @@ const Mock = struct {
14881544 return result;
14891545 }
14901546
1547 fn dummySrcLoc() Module.SrcLoc {
1548 return .{
1549 .file_scope = undefined,
1550 .parent_decl_node = 0,
1551 .lazy = .unneeded,
1552 };
1553 }
1554
14911555 fn testEmitSingleSuccess(
14921556 self: *Mock,
14931557 mir_inst: Mir.Inst,
14941558 expected_enc: []const u8,
14951559 assembly: []const u8,
14961560 ) !void {
1561 const dummy_src_loc = Mock.dummySrcLoc();
14971562 const code_index = self.code.items.len;
14981563 const mir_index = try self.addInst(mir_inst);
1499 switch (mir_inst.tag) {
1564 const res = switch (mir_inst.tag) {
15001565 .mov => try mirMovImpl(
1566 testing.allocator,
15011567 self.mir_instructions.slice(),
15021568 self.mir_extra.items,
15031569 mir_index,
1570 dummy_src_loc,
15041571 &self.code,
15051572 ),
15061573 else => unreachable,
1507 }
1574 };
1575 defer res.deinit(testing.allocator);
1576 try testing.expect(res == .ok);
15081577 const code_len = if (self.code.items[code_index..].len >= expected_enc.len)
15091578 expected_enc.len
15101579 else
......@@ -1512,18 +1581,23 @@ const Mock = struct {
15121581 try expectEqualHexStrings(expected_enc, self.code.items[code_index..][0..code_len], assembly);
15131582 }
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();
15161586 const index = try self.addInst(mir_inst);
15171587 const res = switch (mir_inst.tag) {
1518 .mov => mirMovImpl(
1588 .mov => try mirMovImpl(
1589 testing.allocator,
15191590 self.mir_instructions.slice(),
15201591 self.mir_extra.items,
15211592 index,
1593 dummy_src_loc,
15221594 &self.code,
15231595 ),
15241596 else => unreachable,
15251597 };
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);
15271601 }
15281602};
15291603
......@@ -1560,16 +1634,16 @@ test "mov dst_reg, src_reg" {
15601634 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .rax }).encode(),
15611635 .data = undefined,
15621636 }, "\x49\x89\xc4", "mov r12, rax");
1563 try mock.testEmitSingleError(.{
1637 try mock.testEmitSingleFail(.{
15641638 .tag = .mov,
15651639 .ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .eax }).encode(),
15661640 .data = undefined,
1567 }, error.OperandSizeMismatch);
1568 try mock.testEmitSingleError(.{
1641 }, "size mismatch: sizeof Register.r12 != sizeof Register.eax");
1642 try mock.testEmitSingleFail(.{
15691643 .tag = .mov,
15701644 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .rax }).encode(),
15711645 .data = undefined,
1572 }, error.OperandSizeMismatch);
1646 }, "size mismatch: sizeof Register.r12d != sizeof Register.rax");
15731647 try mock.testEmitSingleSuccess(.{
15741648 .tag = .mov,
15751649 .ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .eax }).encode(),
......@@ -1615,16 +1689,16 @@ test "mov dst_reg, imm" {
16151689 .ops = (Mir.Ops{ .reg1 = .cl }).encode(),
16161690 .data = .{ .imm = 0x10 },
16171691 }, "\xc6\xc1\x10", "mov cl, 0x10");
1618 try mock.testEmitSingleError(.{
1692 try mock.testEmitSingleFail(.{
16191693 .tag = .mov,
16201694 .ops = (Mir.Ops{ .reg1 = .cx }).encode(),
16211695 .data = .{ .imm = 0x10000000 },
1622 }, error.OperandSizeMismatch);
1623 try mock.testEmitSingleError(.{
1696 }, "size mismatch: sizeof Register.cx != sizeof 0x10000000");
1697 try mock.testEmitSingleFail(.{
16241698 .tag = .mov,
16251699 .ops = (Mir.Ops{ .reg1 = .cl }).encode(),
16261700 .data = .{ .imm = 0x1000 },
1627 }, error.OperandSizeMismatch);
1701 }, "size mismatch: sizeof Register.cl != sizeof 0x1000");
16281702}
16291703
16301704test "mov dst_reg, [imm32]" {
......@@ -1705,11 +1779,11 @@ test "mov [dst_reg + 0], imm" {
17051779 .ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
17061780 .data = .{ .imm = 0x10 },
17071781 }, "\xC7\x00\x10\x00\x00\x00", "mov dword ptr [rax + 0], 0x10");
1708 try mock.testEmitSingleError(.{
1782 try mock.testEmitSingleFail(.{
17091783 .tag = .mov,
17101784 .ops = (Mir.Ops{ .reg1 = .eax, .flags = 0b10 }).encode(),
17111785 .data = .{ .imm = 0x10 },
1712 }, error.OperandSizeMismatch);
1786 }, "size mismatch: sizeof Register.eax != 8");
17131787}
17141788
17151789test "mov [dst_reg + imm32], src_reg" {
......@@ -1745,11 +1819,11 @@ test "mov [dst_reg + imm32], src_reg" {
17451819 .ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .eax, .flags = 0b10 }).encode(),
17461820 .data = .{ .imm = 0x10 },
17471821 }, "\x41\x89\x43\x10", "mov dword ptr [r11 + 0x10], eax");
1748 try mock.testEmitSingleError(.{
1822 try mock.testEmitSingleFail(.{
17491823 .tag = .mov,
17501824 .ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .ax, .flags = 0b10 }).encode(),
17511825 .data = .{ .imm = 0x10 },
1752 }, error.OperandSizeMismatch);
1826 }, "size mismatch: sizeof Register.r11w != 8");
17531827}
17541828
17551829test "mov [dst_reg + imm32], imm32" {
......@@ -1815,10 +1889,10 @@ test "mov [dst_reg + imm32], imm32" {
18151889 .dest_off = 0x10,
18161890 .operand = 0x20,
18171891 });
1818 try mock.testEmitSingleError(.{
1892 try mock.testEmitSingleFail(.{
18191893 .tag = .mov,
18201894 .ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b11 }).encode(),
18211895 .data = .{ .payload = payload },
1822 }, error.OperandSizeMismatch);
1896 }, "size mismatch: sizeof Register.r11d != 8");
18231897 }
18241898}