Commit 4a40c0a80c
Changed files (1)
src
arch
x86_64
src/arch/x86_64/Emit.zig
@@ -48,9 +48,31 @@ const InnerError = error{
EmitFail,
};
-const EmitError = error{
- OutOfMemory,
- OperandSizeMismatch,
+const EmitResult = union(enum) {
+ ok: void,
+ err: *ErrorMsg,
+
+ fn ok() EmitResult {
+ return EmitResult{ .ok = .{} };
+ }
+
+ fn err(
+ allocator: Allocator,
+ src_loc: Module.SrcLoc,
+ comptime format: []const u8,
+ args: anytype,
+ ) error{OutOfMemory}!EmitResult {
+ return EmitResult{
+ .err = try ErrorMsg.create(allocator, src_loc, format, args),
+ };
+ }
+
+ fn deinit(res: EmitResult, allocator: Allocator) void {
+ switch (res) {
+ .ok => {},
+ .err => |err_msg| err_msg.destroy(allocator),
+ }
+ }
};
const Reloc = struct {
@@ -167,9 +189,15 @@ pub fn deinit(emit: *Emit) void {
}
fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
+ @setCold(true);
+ const err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
+ return emit.failWithErrorMsg(err_msg);
+}
+
+fn failWithErrorMsg(emit: *Emit, err_msg: *ErrorMsg) InnerError {
@setCold(true);
assert(emit.err_msg == null);
- emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args);
+ emit.err_msg = err_msg;
return error.EmitFail;
}
@@ -858,24 +886,28 @@ fn mirArithScaleImm(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerE
}
fn mirMov(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
- return mirMovImpl(
+ const res = try mirMovImpl(
+ emit.bin_file.allocator,
emit.mir.instructions,
emit.mir.extra,
inst,
+ emit.src_loc,
emit.code,
- ) catch |err| switch (err) {
- // TODO better formating of operands in case of an error
- error.OperandSizeMismatch => emit.fail("operand size mismatch", .{}),
- else => emit.fail("emit failed with error: {}", .{err}),
- };
+ );
+ switch (res) {
+ .ok => {},
+ .err => |err_msg| return emit.failWithErrorMsg(err_msg),
+ }
}
fn mirMovImpl(
+ allocator: Allocator,
mir_instructions: std.MultiArrayList(Mir.Inst).Slice,
mir_extra: []const u32,
inst: Mir.Inst.Index,
+ src_loc: Module.SrcLoc,
code: *std.ArrayList(u8),
-) EmitError!void {
+) error{OutOfMemory}!EmitResult {
const ops = Mir.Ops.decode(mir_instructions.items(.ops)[inst]);
switch (ops.flags) {
0b00 => blk: {
@@ -900,11 +932,31 @@ fn mirMovImpl(
encoder.modRm_direct(modrm_ext, ops.reg1.lowId());
switch (ops.reg1.size()) {
8 => {
- const imm8 = math.cast(i8, imm) catch return error.OperandSizeMismatch;
+ const imm8 = math.cast(i8, imm) catch {
+ return EmitResult.err(
+ allocator,
+ src_loc,
+ "size mismatch: sizeof {} != sizeof 0x{x}",
+ .{
+ ops.reg1,
+ imm,
+ },
+ );
+ };
encoder.imm8(imm8);
},
16 => {
- const imm16 = math.cast(i16, imm) catch return error.OperandSizeMismatch;
+ const imm16 = math.cast(i16, imm) catch {
+ return EmitResult.err(
+ allocator,
+ src_loc,
+ "size mismatch: sizeof {} != sizeof 0x{x}",
+ .{
+ ops.reg1,
+ imm,
+ },
+ );
+ };
encoder.imm16(imm16);
},
32, 64 => {
@@ -917,7 +969,10 @@ fn mirMovImpl(
// mov reg1, reg2
// MR
if (ops.reg1.size() != ops.reg2.size()) {
- return error.OperandSizeMismatch;
+ return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != sizeof {}", .{
+ ops.reg1,
+ ops.reg2,
+ });
}
const opc: u8 = if (ops.reg1.size() == 8) 0x88 else 0x89;
const encoder = try Encoder.init(code, 3);
@@ -954,7 +1009,7 @@ fn mirMovImpl(
// TODO handle 32-bit base register - requires prefix 0x67
// Intel Manual, Vol 1, chapter 3.6 and 3.6.1
if (ops.reg2.size() != 64) {
- return error.OperandSizeMismatch;
+ return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != 8", .{ops.reg2});
}
const encoder = try Encoder.init(code, 8);
if (ops.reg1.size() == 16) {
@@ -978,7 +1033,7 @@ fn mirMovImpl(
// TODO handle 32-bit base register - requires prefix 0x67
// Intel Manual, Vol 1, chapter 3.6 and 3.6.1
if (ops.reg1.size() != 64) {
- return error.OperandSizeMismatch;
+ return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != 8", .{ops.reg1});
}
if (ops.reg2 == .none) {
// mov [reg1 + 0], imm32
@@ -1041,7 +1096,7 @@ fn mirMovImpl(
// a byte, word or dword ptr.
// TODO we currently don't have a way to flag imm32 64bit sign extended
if (ops.reg1.size() != 64) {
- return error.OperandSizeMismatch;
+ return EmitResult.err(allocator, src_loc, "size mismatch: sizeof {} != 8", .{ops.reg1});
}
const payload = mir_instructions.items(.data)[inst].payload;
const imm_pair = Mir.extraData(mir_extra, Mir.ImmPair, payload).data;
@@ -1077,6 +1132,7 @@ fn mirMovImpl(
encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
},
}
+ return EmitResult.ok();
}
fn immOpSize(imm: i32) u8 {
@@ -1488,23 +1544,36 @@ const Mock = struct {
return result;
}
+ fn dummySrcLoc() Module.SrcLoc {
+ return .{
+ .file_scope = undefined,
+ .parent_decl_node = 0,
+ .lazy = .unneeded,
+ };
+ }
+
fn testEmitSingleSuccess(
self: *Mock,
mir_inst: Mir.Inst,
expected_enc: []const u8,
assembly: []const u8,
) !void {
+ const dummy_src_loc = Mock.dummySrcLoc();
const code_index = self.code.items.len;
const mir_index = try self.addInst(mir_inst);
- switch (mir_inst.tag) {
+ const res = switch (mir_inst.tag) {
.mov => try mirMovImpl(
+ testing.allocator,
self.mir_instructions.slice(),
self.mir_extra.items,
mir_index,
+ dummy_src_loc,
&self.code,
),
else => unreachable,
- }
+ };
+ defer res.deinit(testing.allocator);
+ try testing.expect(res == .ok);
const code_len = if (self.code.items[code_index..].len >= expected_enc.len)
expected_enc.len
else
@@ -1512,18 +1581,23 @@ const Mock = struct {
try expectEqualHexStrings(expected_enc, self.code.items[code_index..][0..code_len], assembly);
}
- fn testEmitSingleError(self: *Mock, mir_inst: Mir.Inst, err: EmitError) !void {
+ fn testEmitSingleFail(self: *Mock, mir_inst: Mir.Inst, msg: []const u8) !void {
+ const dummy_src_loc = Mock.dummySrcLoc();
const index = try self.addInst(mir_inst);
const res = switch (mir_inst.tag) {
- .mov => mirMovImpl(
+ .mov => try mirMovImpl(
+ testing.allocator,
self.mir_instructions.slice(),
self.mir_extra.items,
index,
+ dummy_src_loc,
&self.code,
),
else => unreachable,
};
- try testing.expectError(err, res);
+ defer res.deinit(testing.allocator);
+ try testing.expect(res == .err);
+ try testing.expectEqualStrings(msg, res.err.msg);
}
};
@@ -1560,16 +1634,16 @@ test "mov dst_reg, src_reg" {
.ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .rax }).encode(),
.data = undefined,
}, "\x49\x89\xc4", "mov r12, rax");
- try mock.testEmitSingleError(.{
+ try mock.testEmitSingleFail(.{
.tag = .mov,
.ops = (Mir.Ops{ .reg1 = .r12, .reg2 = .eax }).encode(),
.data = undefined,
- }, error.OperandSizeMismatch);
- try mock.testEmitSingleError(.{
+ }, "size mismatch: sizeof Register.r12 != sizeof Register.eax");
+ try mock.testEmitSingleFail(.{
.tag = .mov,
.ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .rax }).encode(),
.data = undefined,
- }, error.OperandSizeMismatch);
+ }, "size mismatch: sizeof Register.r12d != sizeof Register.rax");
try mock.testEmitSingleSuccess(.{
.tag = .mov,
.ops = (Mir.Ops{ .reg1 = .r12d, .reg2 = .eax }).encode(),
@@ -1615,16 +1689,16 @@ test "mov dst_reg, imm" {
.ops = (Mir.Ops{ .reg1 = .cl }).encode(),
.data = .{ .imm = 0x10 },
}, "\xc6\xc1\x10", "mov cl, 0x10");
- try mock.testEmitSingleError(.{
+ try mock.testEmitSingleFail(.{
.tag = .mov,
.ops = (Mir.Ops{ .reg1 = .cx }).encode(),
.data = .{ .imm = 0x10000000 },
- }, error.OperandSizeMismatch);
- try mock.testEmitSingleError(.{
+ }, "size mismatch: sizeof Register.cx != sizeof 0x10000000");
+ try mock.testEmitSingleFail(.{
.tag = .mov,
.ops = (Mir.Ops{ .reg1 = .cl }).encode(),
.data = .{ .imm = 0x1000 },
- }, error.OperandSizeMismatch);
+ }, "size mismatch: sizeof Register.cl != sizeof 0x1000");
}
test "mov dst_reg, [imm32]" {
@@ -1705,11 +1779,11 @@ test "mov [dst_reg + 0], imm" {
.ops = (Mir.Ops{ .reg1 = .rax, .flags = 0b10 }).encode(),
.data = .{ .imm = 0x10 },
}, "\xC7\x00\x10\x00\x00\x00", "mov dword ptr [rax + 0], 0x10");
- try mock.testEmitSingleError(.{
+ try mock.testEmitSingleFail(.{
.tag = .mov,
.ops = (Mir.Ops{ .reg1 = .eax, .flags = 0b10 }).encode(),
.data = .{ .imm = 0x10 },
- }, error.OperandSizeMismatch);
+ }, "size mismatch: sizeof Register.eax != 8");
}
test "mov [dst_reg + imm32], src_reg" {
@@ -1745,11 +1819,11 @@ test "mov [dst_reg + imm32], src_reg" {
.ops = (Mir.Ops{ .reg1 = .r11, .reg2 = .eax, .flags = 0b10 }).encode(),
.data = .{ .imm = 0x10 },
}, "\x41\x89\x43\x10", "mov dword ptr [r11 + 0x10], eax");
- try mock.testEmitSingleError(.{
+ try mock.testEmitSingleFail(.{
.tag = .mov,
.ops = (Mir.Ops{ .reg1 = .r11w, .reg2 = .ax, .flags = 0b10 }).encode(),
.data = .{ .imm = 0x10 },
- }, error.OperandSizeMismatch);
+ }, "size mismatch: sizeof Register.r11w != 8");
}
test "mov [dst_reg + imm32], imm32" {
@@ -1815,10 +1889,10 @@ test "mov [dst_reg + imm32], imm32" {
.dest_off = 0x10,
.operand = 0x20,
});
- try mock.testEmitSingleError(.{
+ try mock.testEmitSingleFail(.{
.tag = .mov,
.ops = (Mir.Ops{ .reg1 = .r11d, .flags = 0b11 }).encode(),
.data = .{ .payload = payload },
- }, error.OperandSizeMismatch);
+ }, "size mismatch: sizeof Register.r11d != 8");
}
}