diff options
Diffstat (limited to 'src/arch/x86_64/encoder.zig')
| -rw-r--r-- | src/arch/x86_64/encoder.zig | 919 |
1 files changed, 539 insertions, 380 deletions
diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 05f66062ac..e7e231c063 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -11,12 +11,9 @@ const Memory = bits.Memory; const Register = bits.Register; pub const Instruction = struct { - op1: Operand = .none, - op2: Operand = .none, - op3: Operand = .none, - op4: Operand = .none, prefix: Prefix = .none, encoding: Encoding, + ops: [4]Operand = .{.none} ** 4, pub const Mnemonic = Encoding.Mnemonic; @@ -107,102 +104,87 @@ pub const Instruction = struct { } }; - pub const Init = struct { - prefix: Prefix = .none, - op1: Operand = .none, - op2: Operand = .none, - op3: Operand = .none, - op4: Operand = .none, - }; - - pub fn new(mnemonic: Mnemonic, args: Init) !Instruction { - const encoding = (try Encoding.findByMnemonic(mnemonic, args)) orelse { + pub fn new(prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) !Instruction { + const encoding = (try Encoding.findByMnemonic(prefix, mnemonic, ops)) orelse { log.err("no encoding found for: {s} {s} {s} {s} {s} {s}", .{ - @tagName(args.prefix), + @tagName(prefix), @tagName(mnemonic), - @tagName(Encoding.Op.fromOperand(args.op1)), - @tagName(Encoding.Op.fromOperand(args.op2)), - @tagName(Encoding.Op.fromOperand(args.op3)), - @tagName(Encoding.Op.fromOperand(args.op4)), + @tagName(if (ops.len > 0) Encoding.Op.fromOperand(ops[0]) else .none), + @tagName(if (ops.len > 1) Encoding.Op.fromOperand(ops[1]) else .none), + @tagName(if (ops.len > 2) Encoding.Op.fromOperand(ops[2]) else .none), + @tagName(if (ops.len > 3) Encoding.Op.fromOperand(ops[3]) else .none), }); return error.InvalidInstruction; }; log.debug("selected encoding: {}", .{encoding}); - return .{ - .prefix = args.prefix, - .op1 = args.op1, - .op2 = args.op2, - .op3 = args.op3, - .op4 = args.op4, + + var inst = Instruction{ + .prefix = prefix, .encoding = encoding, + .ops = [1]Operand{.none} ** 4, }; + std.mem.copy(Operand, &inst.ops, ops); + return inst; } pub fn fmtPrint(inst: Instruction, writer: anytype) !void { if (inst.prefix != .none) try writer.print("{s} ", .{@tagName(inst.prefix)}); try writer.print("{s}", .{@tagName(inst.encoding.mnemonic)}); - const ops = [_]struct { Operand, Encoding.Op }{ - .{ inst.op1, inst.encoding.op1 }, - .{ inst.op2, inst.encoding.op2 }, - .{ inst.op3, inst.encoding.op3 }, - .{ inst.op4, inst.encoding.op4 }, - }; - for (&ops, 0..) |op, i| { - if (op[0] == .none) break; - if (i > 0) { - try writer.writeByte(','); - } + for (inst.ops, inst.encodings.ops, 0..) |op, enc, i| { + if (op == .none) break; + if (i > 0) try writer.writeByte(','); try writer.writeByte(' '); - try op[0].fmtPrint(op[1], writer); + try op.fmtPrint(enc, writer); } } pub fn encode(inst: Instruction, writer: anytype) !void { const encoder = Encoder(@TypeOf(writer)){ .writer = writer }; - const encoding = inst.encoding; + const enc = inst.encoding; + const data = enc.data; try inst.encodeLegacyPrefixes(encoder); try inst.encodeMandatoryPrefix(encoder); try inst.encodeRexPrefix(encoder); try inst.encodeOpcode(encoder); - switch (encoding.op_en) { + switch (data.op_en) { .np, .o => {}, - .i, .d => try encodeImm(inst.op1.imm, encoding.op1, encoder), - .zi, .oi => try encodeImm(inst.op2.imm, encoding.op2, encoder), - .fd => try encoder.imm64(inst.op2.mem.moffs.offset), - .td => try encoder.imm64(inst.op1.mem.moffs.offset), + .i, .d => try encodeImm(inst.ops[0].imm, data.ops[0], encoder), + .zi, .oi => try encodeImm(inst.ops[1].imm, data.ops[1], encoder), + .fd => try encoder.imm64(inst.ops[1].mem.moffs.offset), + .td => try encoder.imm64(inst.ops[0].mem.moffs.offset), else => { - const mem_op = switch (encoding.op_en) { - .m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.op1, - .rm, .rmi => inst.op2, + const mem_op = switch (data.op_en) { + .m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.ops[0], + .rm, .rmi => inst.ops[1], else => unreachable, }; switch (mem_op) { .reg => |reg| { - const rm = switch (encoding.op_en) { - .m, .mi, .m1, .mc => encoding.modRmExt(), - .mr, .mri, .mrc => inst.op2.reg.lowEnc(), - .rm, .rmi => inst.op1.reg.lowEnc(), + const rm = switch (data.op_en) { + .m, .mi, .m1, .mc => enc.modRmExt(), + .mr, .mri, .mrc => inst.ops[1].reg.lowEnc(), + .rm, .rmi => inst.ops[0].reg.lowEnc(), else => unreachable, }; try encoder.modRm_direct(rm, reg.lowEnc()); }, .mem => |mem| { - const op = switch (encoding.op_en) { + const op = switch (data.op_en) { .m, .mi, .m1, .mc => .none, - .mr, .mri, .mrc => inst.op2, - .rm, .rmi => inst.op1, + .mr, .mri, .mrc => inst.ops[1], + .rm, .rmi => inst.ops[0], else => unreachable, }; - try encodeMemory(encoding, mem, op, encoder); + try encodeMemory(enc, mem, op, encoder); }, else => unreachable, } - switch (encoding.op_en) { - .mi => try encodeImm(inst.op2.imm, encoding.op2, encoder), - .rmi, .mri => try encodeImm(inst.op3.imm, encoding.op3, encoder), + switch (data.op_en) { + .mi => try encodeImm(inst.ops[1].imm, data.ops[1], encoder), + .rmi, .mri => try encodeImm(inst.ops[2].imm, data.ops[2], encoder), else => {}, } }, @@ -214,15 +196,16 @@ pub const Instruction = struct { const first = @boolToInt(inst.encoding.mandatoryPrefix() != null); const final = opcode.len - 1; for (opcode[first..final]) |byte| try encoder.opcode_1byte(byte); - switch (inst.encoding.op_en) { - .o, .oi => try encoder.opcode_withReg(opcode[final], inst.op1.reg.lowEnc()), + switch (inst.encoding.data.op_en) { + .o, .oi => try encoder.opcode_withReg(opcode[final], inst.ops[0].reg.lowEnc()), else => try encoder.opcode_1byte(opcode[final]), } } fn encodeLegacyPrefixes(inst: Instruction, encoder: anytype) !void { const enc = inst.encoding; - const op_en = enc.op_en; + const data = enc.data; + const op_en = data.op_en; var legacy = LegacyPrefixes{}; @@ -233,7 +216,7 @@ pub const Instruction = struct { .rep, .repe, .repz => legacy.prefix_f3 = true, } - if (enc.mode == .none) { + if (data.mode == .none) { const bit_size = enc.operandBitSize(); if (bit_size == 16) { legacy.set16BitOverride(); @@ -242,17 +225,17 @@ pub const Instruction = struct { const segment_override: ?Register = switch (op_en) { .i, .zi, .o, .oi, .d, .np => null, - .fd => inst.op2.mem.base().?, - .td => inst.op1.mem.base().?, - .rm, .rmi => if (inst.op2.isSegmentRegister()) blk: { - break :blk switch (inst.op2) { + .fd => inst.ops[1].mem.base().?, + .td => inst.ops[0].mem.base().?, + .rm, .rmi => if (inst.ops[1].isSegmentRegister()) blk: { + break :blk switch (inst.ops[1]) { .reg => |r| r, .mem => |m| m.base().?, else => unreachable, }; } else null, - .m, .mi, .m1, .mc, .mr, .mri, .mrc => if (inst.op1.isSegmentRegister()) blk: { - break :blk switch (inst.op1) { + .m, .mi, .m1, .mc, .mr, .mri, .mrc => if (inst.ops[0].isSegmentRegister()) blk: { + break :blk switch (inst.ops[0]) { .reg => |r| r, .mem => |m| m.base().?, else => unreachable, @@ -267,19 +250,19 @@ pub const Instruction = struct { } fn encodeRexPrefix(inst: Instruction, encoder: anytype) !void { - const op_en = inst.encoding.op_en; + const op_en = inst.encoding.data.op_en; var rex = Rex{}; - rex.present = inst.encoding.mode == .rex; - rex.w = inst.encoding.mode == .long; + rex.present = inst.encoding.data.mode == .rex; + rex.w = inst.encoding.data.mode == .long; switch (op_en) { .np, .i, .zi, .fd, .td, .d => {}, - .o, .oi => rex.b = inst.op1.reg.isExtended(), + .o, .oi => rex.b = inst.ops[0].reg.isExtended(), .m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc => { const r_op = switch (op_en) { - .rm, .rmi => inst.op1, - .mr, .mri, .mrc => inst.op2, + .rm, .rmi => inst.ops[0], + .mr, .mri, .mrc => inst.ops[1], else => null, }; if (r_op) |op| { @@ -287,8 +270,8 @@ pub const Instruction = struct { } const b_x_op = switch (op_en) { - .rm, .rmi => inst.op2, - .m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.op1, + .rm, .rmi => inst.ops[1], + .m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.ops[0], else => unreachable, }; switch (b_x_op) { @@ -827,16 +810,14 @@ const TestEncode = struct { buffer: [32]u8 = undefined, index: usize = 0, - fn encode(enc: *TestEncode, mnemonic: Instruction.Mnemonic, args: Instruction.Init) !void { + fn encode( + enc: *TestEncode, + mnemonic: Instruction.Mnemonic, + ops: []const Instruction.Operand, + ) !void { var stream = std.io.fixedBufferStream(&enc.buffer); var count_writer = std.io.countingWriter(stream.writer()); - const inst = try Instruction.new(mnemonic, .{ - .prefix = args.prefix, - .op1 = args.op1, - .op2 = args.op2, - .op3 = args.op3, - .op4 = args.op4, - }); + const inst = try Instruction.new(.none, mnemonic, ops); try inst.encode(count_writer.writer()); enc.index = count_writer.bytes_written; } @@ -850,9 +831,9 @@ test "encode" { var buf = std.ArrayList(u8).init(testing.allocator); defer buf.deinit(); - const inst = try Instruction.new(.mov, .{ - .op1 = .{ .reg = .rbx }, - .op2 = .{ .imm = Immediate.u(4) }, + const inst = try Instruction.new(.none, .mov, &.{ + .{ .reg = .rbx }, + .{ .imm = Immediate.u(4) }, }); try inst.encode(buf.writer()); try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items); @@ -861,61 +842,94 @@ test "encode" { test "lower I encoding" { var enc = TestEncode{}; - try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.push, &.{ + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10"); - try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000) } }); + try enc.encode(.push, &.{ + .{ .imm = Immediate.u(0x1000) }, + }); try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000"); - try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x10000000) } }); + try enc.encode(.push, &.{ + .{ .imm = Immediate.u(0x10000000) }, + }); try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000"); - try enc.encode(.adc, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10000000) } }); + try enc.encode(.adc, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x10000000) }, + }); try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000"); - try enc.encode(.add, .{ .op1 = .{ .reg = .al }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.add, &.{ + .{ .reg = .al }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10"); - try enc.encode(.add, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.add, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); - try enc.encode(.sbb, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.sbb, &.{ + .{ .reg = .ax }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10"); - try enc.encode(.xor, .{ .op1 = .{ .reg = .al }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.xor, &.{ + .{ .reg = .al }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10"); } test "lower MI encoding" { var enc = TestEncode{}; - try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r12 }, + .{ .imm = Immediate.u(0x1000) }, + }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); - try enc.encode(.mov, .{ - .op1 = .{ .mem = Memory.sib(.byte, .{ .base = .r12 }) }, - .op2 = .{ .imm = Immediate.u(0x10) }, + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.byte, .{ .base = .r12 }) }, + .{ .imm = Immediate.u(0x10) }, }); try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r12 }, + .{ .imm = Immediate.u(0x1000) }, + }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r12 }, + .{ .imm = Immediate.u(0x1000) }, + }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10"); - try enc.encode(.mov, .{ - .op1 = .{ .mem = Memory.sib(.dword, .{ .base = .r11 }) }, - .op2 = .{ .imm = Immediate.u(0x10) }, + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .r11 }) }, + .{ .imm = Immediate.u(0x10) }, }); try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); - try enc.encode(.mov, .{ - .op1 = .{ .mem = Memory.rip(.qword, 0x10) }, - .op2 = .{ .imm = Immediate.u(0x10) }, + try enc.encode(.mov, &.{ + .{ .mem = Memory.rip(.qword, 0x10) }, + .{ .imm = Immediate.u(0x10) }, }); try expectEqualHexStrings( "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00", @@ -923,99 +937,108 @@ test "lower MI encoding" { "mov QWORD PTR [rip + 0x10], 0x10", ); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .disp = -8, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -8 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.word, .{ - .base = .rbp, - .disp = -2, - }) }, .op2 = .{ .imm = Immediate.s(-16) } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -2 }) }, + .{ .imm = Immediate.s(-16) }, + }); try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ - .base = .rbp, - .disp = -1, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.byte, .{ .base = .rbp, .disp = -1 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .ds, - .disp = 0x10000000, - .scale_index = .{ - .scale = 2, - .index = .rcx, - }, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.qword, .{ + .base = .ds, + .disp = 0x10000000, + .scale_index = .{ .scale = 2, .index = .rcx }, + }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings( "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rcx*2 + 0x10000000], 0x10", ); - try enc.encode(.adc, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ - .base = .rbp, - .disp = -0x10, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.adc, &.{ + .{ .mem = Memory.sib(.byte, .{ .base = .rbp, .disp = -0x10 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); - try enc.encode(.adc, .{ .op1 = .{ .mem = Memory.rip(.qword, 0) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.adc, &.{ + .{ .mem = Memory.rip(.qword, 0) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10"); - try enc.encode(.adc, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.adc, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10"); - try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .rdx, - .disp = -8, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.add, &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .rdx, .disp = -8 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10"); - try enc.encode(.add, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.add, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); - try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .disp = -0x10, - }) }, .op2 = .{ .imm = Immediate.s(-0x10) } }); + try enc.encode(.add, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -0x10 }) }, + .{ .imm = Immediate.s(-0x10) }, + }); try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); - try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .ds, - .disp = 0x10000000, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.@"and", &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings( "\x83\x24\x25\x00\x00\x00\x10\x10", enc.code(), "and DWORD PTR ds:0x10000000, 0x10", ); - try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .es, - .disp = 0x10000000, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.@"and", &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .es, .disp = 0x10000000 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings( "\x26\x83\x24\x25\x00\x00\x00\x10\x10", enc.code(), "and DWORD PTR es:0x10000000, 0x10", ); - try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .r12, - .disp = 0x10000000, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.@"and", &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .r12, .disp = 0x10000000 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings( "\x41\x83\xA4\x24\x00\x00\x00\x10\x10", enc.code(), "and DWORD PTR [r12 + 0x10000000], 0x10", ); - try enc.encode(.sub, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .r11, - .disp = 0x10000000, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.sub, &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .r11, .disp = 0x10000000 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings( "\x41\x83\xAB\x00\x00\x00\x10\x10", enc.code(), @@ -1026,185 +1049,227 @@ test "lower MI encoding" { test "lower RM encoding" { var enc = TestEncode{}; - try enc.encode(.mov, .{ - .op1 = .{ .reg = .rax }, - .op2 = .{ .mem = Memory.sib(.qword, .{ .base = .r11 }) }, + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.sib(.qword, .{ .base = .r11 }) }, }); try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .rbx }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .ds, - .disp = 0x10, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rbx }, + .{ .mem = Memory.sib(.qword, .{ .base = .ds, .disp = 0x10 }) }, + }); try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .disp = -4, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) }, + }); try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .scale_index = .{ - .scale = 1, - .index = .rcx, - }, - .disp = -8, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .scale_index = .{ .scale = 1, .index = .rcx }, + .disp = -8, + }) }, + }); try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*1 - 8]"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.dword, .{ - .base = .rbp, - .scale_index = .{ - .scale = 4, - .index = .rdx, - }, - .disp = -4, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.sib(.dword, .{ + .base = .rbp, + .scale_index = .{ .scale = 4, .index = .rdx }, + .disp = -4, + }) }, + }); try expectEqualHexStrings("\x8B\x44\x95\xFC", enc.code(), "mov eax, dword ptr [rbp + rdx*4 - 4]"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .scale_index = .{ - .scale = 8, - .index = .rcx, - }, - .disp = -8, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .scale_index = .{ .scale = 8, .index = .rcx }, + .disp = -8, + }) }, + }); try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*8 - 8]"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r8b }, .op2 = .{ .mem = Memory.sib(.byte, .{ - .base = .rsi, - .scale_index = .{ - .scale = 1, - .index = .rcx, - }, - .disp = -24, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r8b }, + .{ .mem = Memory.sib(.byte, .{ + .base = .rsi, + .scale_index = .{ .scale = 1, .index = .rcx }, + .disp = -24, + }) }, + }); try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", enc.code(), "mov r8b, BYTE PTR [rsi + rcx*1 - 24]"); // TODO this mnemonic needs cleanup as some prefixes are obsolete. - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .cs } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .reg = .cs }, + }); try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .disp = -16, - }) }, .op2 = .{ .reg = .fs } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -16 }) }, + .{ .reg = .fs }, + }); try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], fs"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r12w }, .op2 = .{ .reg = .cs } }); + try enc.encode(.mov, &.{ + .{ .reg = .r12w }, + .{ .reg = .cs }, + }); try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.word, .{ - .base = .rbp, - .disp = -16, - }) }, .op2 = .{ .reg = .fs } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, + .{ .reg = .fs }, + }); try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs"); - try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .reg = .bx } }); + try enc.encode(.movsx, &.{ + .{ .reg = .eax }, + .{ .reg = .bx }, + }); try expectEqualHexStrings("\x0F\xBF\xC3", enc.code(), "movsx eax, bx"); - try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .reg = .bl } }); + try enc.encode(.movsx, &.{ + .{ .reg = .eax }, + .{ .reg = .bl }, + }); try expectEqualHexStrings("\x0F\xBE\xC3", enc.code(), "movsx eax, bl"); - try enc.encode(.movsx, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .reg = .bl } }); + try enc.encode(.movsx, &.{ + .{ .reg = .ax }, + .{ .reg = .bl }, + }); try expectEqualHexStrings("\x66\x0F\xBE\xC3", enc.code(), "movsx ax, bl"); - try enc.encode(.movsx, .{ - .op1 = .{ .reg = .eax }, - .op2 = .{ .mem = Memory.sib(.word, .{ .base = .rbp }) }, + try enc.encode(.movsx, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.sib(.word, .{ .base = .rbp }) }, }); try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]"); - try enc.encode(.movsx, .{ - .op1 = .{ .reg = .eax }, - .op2 = .{ .mem = Memory.sib(.byte, .{ .scale_index = .{ .index = .rax, .scale = 2 } }) }, + try enc.encode(.movsx, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.sib(.byte, .{ .scale_index = .{ .index = .rax, .scale = 2 } }) }, }); try expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]"); - try enc.encode(.movsx, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.rip(.byte, 0x10) } }); + try enc.encode(.movsx, &.{ + .{ .reg = .ax }, + .{ .mem = Memory.rip(.byte, 0x10) }, + }); try expectEqualHexStrings("\x66\x0F\xBE\x05\x10\x00\x00\x00", enc.code(), "movsx ax, BYTE PTR [rip + 0x10]"); - try enc.encode(.movsx, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .bx } }); + try enc.encode(.movsx, &.{ + .{ .reg = .rax }, + .{ .reg = .bx }, + }); try expectEqualHexStrings("\x48\x0F\xBF\xC3", enc.code(), "movsx rax, bx"); - try enc.encode(.movsxd, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .ebx } }); + try enc.encode(.movsxd, &.{ + .{ .reg = .rax }, + .{ .reg = .ebx }, + }); try expectEqualHexStrings("\x48\x63\xC3", enc.code(), "movsxd rax, ebx"); - try enc.encode(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.rip(.qword, 0x10) } }); + try enc.encode(.lea, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.rip(.qword, 0x10) }, + }); try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]"); - try enc.encode(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.rip(.dword, 0x10) } }); + try enc.encode(.lea, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.rip(.dword, 0x10) }, + }); try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]"); - try enc.encode(.lea, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.rip(.dword, 0x10) } }); + try enc.encode(.lea, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.rip(.dword, 0x10) }, + }); try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]"); - try enc.encode(.lea, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.rip(.word, 0x10) } }); + try enc.encode(.lea, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.rip(.word, 0x10) }, + }); try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]"); - try enc.encode(.lea, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.rip(.byte, 0x10) } }); + try enc.encode(.lea, &.{ + .{ .reg = .ax }, + .{ .mem = Memory.rip(.byte, 0x10) }, + }); try expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]"); - try enc.encode(.lea, .{ - .op1 = .{ .reg = .rsi }, - .op2 = .{ .mem = Memory.sib(.qword, .{ + try enc.encode(.lea, &.{ + .{ .reg = .rsi }, + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .scale_index = .{ .scale = 1, .index = .rcx }, }) }, }); try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", enc.code(), "lea rsi, QWORD PTR [rbp + rcx*1 + 0]"); - try enc.encode(.add, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .ds, - .disp = 0x10000000, - }) } }); + try enc.encode(.add, &.{ + .{ .reg = .r11 }, + .{ .mem = Memory.sib(.qword, .{ .base = .ds, .disp = 0x10000000 }) }, + }); try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000"); - try enc.encode(.add, .{ .op1 = .{ .reg = .r12b }, .op2 = .{ .mem = Memory.sib(.byte, .{ - .base = .ds, - .disp = 0x10000000, - }) } }); + try enc.encode(.add, &.{ + .{ .reg = .r12b }, + .{ .mem = Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) }, + }); try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000"); - try enc.encode(.add, .{ .op1 = .{ .reg = .r12b }, .op2 = .{ .mem = Memory.sib(.byte, .{ - .base = .fs, - .disp = 0x10000000, - }) } }); + try enc.encode(.add, &.{ + .{ .reg = .r12b }, + .{ .mem = Memory.sib(.byte, .{ .base = .fs, .disp = 0x10000000 }) }, + }); try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000"); - try enc.encode(.sub, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .r13, - .disp = 0x10000000, - }) } }); + try enc.encode(.sub, &.{ + .{ .reg = .r11 }, + .{ .mem = Memory.sib(.qword, .{ .base = .r13, .disp = 0x10000000 }) }, + }); try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]"); - try enc.encode(.sub, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .r12, - .disp = 0x10000000, - }) } }); + try enc.encode(.sub, &.{ + .{ .reg = .r11 }, + .{ .mem = Memory.sib(.qword, .{ .base = .r12, .disp = 0x10000000 }) }, + }); try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]"); - try enc.encode(.imul, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .reg = .r12 } }); + try enc.encode(.imul, &.{ + .{ .reg = .r11 }, + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x4D\x0F\xAF\xDC", enc.code(), "mov r11, r12"); } test "lower RMI encoding" { var enc = TestEncode{}; - try enc.encode(.imul, .{ - .op1 = .{ .reg = .r11 }, - .op2 = .{ .reg = .r12 }, - .op3 = .{ .imm = Immediate.s(-2) }, + try enc.encode(.imul, &.{ + .{ .reg = .r11 }, + .{ .reg = .r12 }, + .{ .imm = Immediate.s(-2) }, }); try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2"); - try enc.encode(.imul, .{ - .op1 = .{ .reg = .r11 }, - .op2 = .{ .mem = Memory.rip(.qword, -16) }, - .op3 = .{ .imm = Immediate.s(-1024) }, + try enc.encode(.imul, &.{ + .{ .reg = .r11 }, + .{ .mem = Memory.rip(.qword, -16) }, + .{ .imm = Immediate.s(-1024) }, }); try expectEqualHexStrings( "\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF", @@ -1212,13 +1277,10 @@ test "lower RMI encoding" { "imul r11, QWORD PTR [rip - 16], -1024", ); - try enc.encode(.imul, .{ - .op1 = .{ .reg = .bx }, - .op2 = .{ .mem = Memory.sib(.word, .{ - .base = .rbp, - .disp = -16, - }) }, - .op3 = .{ .imm = Immediate.s(-1024) }, + try enc.encode(.imul, &.{ + .{ .reg = .bx }, + .{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, + .{ .imm = Immediate.s(-1024) }, }); try expectEqualHexStrings( "\x66\x69\x5D\xF0\x00\xFC", @@ -1226,13 +1288,10 @@ test "lower RMI encoding" { "imul bx, WORD PTR [rbp - 16], -1024", ); - try enc.encode(.imul, .{ - .op1 = .{ .reg = .bx }, - .op2 = .{ .mem = Memory.sib(.word, .{ - .base = .rbp, - .disp = -16, - }) }, - .op3 = .{ .imm = Immediate.u(1024) }, + try enc.encode(.imul, &.{ + .{ .reg = .bx }, + .{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, + .{ .imm = Immediate.u(1024) }, }); try expectEqualHexStrings( "\x66\x69\x5D\xF0\x00\x04", @@ -1244,238 +1303,343 @@ test "lower RMI encoding" { test "lower MR encoding" { var enc = TestEncode{}; - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .rbx } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .reg = .rbx }, + }); try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .disp = -4, - }) }, .op2 = .{ .reg = .r11 } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) }, + .{ .reg = .r11 }, + }); try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.rip(.qword, 0x10) }, .op2 = .{ .reg = .r12 } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.rip(.qword, 0x10) }, + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .r11, - .scale_index = .{ - .scale = 2, - .index = .r12, - }, - .disp = 0x10, - }) }, .op2 = .{ .reg = .r13 } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.qword, .{ + .base = .r11, + .scale_index = .{ .scale = 2, .index = .r12 }, + .disp = 0x10, + }) }, + .{ .reg = .r13 }, + }); try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.rip(.word, -0x10) }, .op2 = .{ .reg = .r12w } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.rip(.word, -0x10) }, + .{ .reg = .r12w }, + }); try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ - .base = .r11, - .scale_index = .{ - .scale = 2, - .index = .r12, - }, - .disp = 0x10, - }) }, .op2 = .{ .reg = .r13b } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.byte, .{ + .base = .r11, + .scale_index = .{ .scale = 2, .index = .r12 }, + .disp = 0x10, + }) }, + .{ .reg = .r13b }, + }); try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b"); - try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ - .base = .ds, - .disp = 0x10000000, - }) }, .op2 = .{ .reg = .r12b } }); + try enc.encode(.add, &.{ + .{ .mem = Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) }, + .{ .reg = .r12b }, + }); try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b"); - try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .ds, - .disp = 0x10000000, - }) }, .op2 = .{ .reg = .r12d } }); + try enc.encode(.add, &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) }, + .{ .reg = .r12d }, + }); try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d"); - try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .gs, - .disp = 0x10000000, - }) }, .op2 = .{ .reg = .r12d } }); + try enc.encode(.add, &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .gs, .disp = 0x10000000 }) }, + .{ .reg = .r12d }, + }); try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d"); - try enc.encode(.sub, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .r11, - .disp = 0x10000000, - }) }, .op2 = .{ .reg = .r12 } }); + try enc.encode(.sub, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .r11, .disp = 0x10000000 }) }, + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12"); } test "lower M encoding" { var enc = TestEncode{}; - try enc.encode(.call, .{ .op1 = .{ .reg = .r12 } }); + try enc.encode(.call, &.{ + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12"); - try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ .base = .r12 }) } }); + try enc.encode(.call, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .r12 }) }, + }); try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]"); - try enc.encode(.call, .{ - .op1 = .{ .mem = Memory.sib(.qword, .{ + try enc.encode(.call, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = null, .scale_index = .{ .index = .r11, .scale = 2 }, }) }, }); try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]"); - try enc.encode(.call, .{ - .op1 = .{ .mem = Memory.sib(.qword, .{ + try enc.encode(.call, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = null, .scale_index = .{ .index = .r12, .scale = 2 }, }) }, }); try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]"); - try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ .base = .gs }) } }); + try enc.encode(.call, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .gs }) }, + }); try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0"); - try enc.encode(.call, .{ .op1 = .{ .imm = Immediate.s(0) } }); + try enc.encode(.call, &.{ + .{ .imm = Immediate.s(0) }, + }); try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0"); - try enc.encode(.push, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ .base = .rbp }) } }); + try enc.encode(.push, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .rbp }) }, + }); try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); - try enc.encode(.push, .{ .op1 = .{ .mem = Memory.sib(.word, .{ .base = .rbp }) } }); + try enc.encode(.push, &.{ + .{ .mem = Memory.sib(.word, .{ .base = .rbp }) }, + }); try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); - try enc.encode(.pop, .{ .op1 = .{ .mem = Memory.rip(.qword, 0) } }); + try enc.encode(.pop, &.{ + .{ .mem = Memory.rip(.qword, 0) }, + }); try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]"); - try enc.encode(.pop, .{ .op1 = .{ .mem = Memory.rip(.word, 0) } }); + try enc.encode(.pop, &.{ + .{ .mem = Memory.rip(.word, 0) }, + }); try expectEqualHexStrings("\x66\x8F\x05\x00\x00\x00\x00", enc.code(), "pop WORD PTR [rbp]"); - try enc.encode(.imul, .{ .op1 = .{ .reg = .rax } }); + try enc.encode(.imul, &.{ + .{ .reg = .rax }, + }); try expectEqualHexStrings("\x48\xF7\xE8", enc.code(), "imul rax"); - try enc.encode(.imul, .{ .op1 = .{ .reg = .r12 } }); + try enc.encode(.imul, &.{ + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x49\xF7\xEC", enc.code(), "imul r12"); } test "lower O encoding" { var enc = TestEncode{}; - try enc.encode(.push, .{ .op1 = .{ .reg = .rax } }); + try enc.encode(.push, &.{ + .{ .reg = .rax }, + }); try expectEqualHexStrings("\x50", enc.code(), "push rax"); - try enc.encode(.push, .{ .op1 = .{ .reg = .r12w } }); + try enc.encode(.push, &.{ + .{ .reg = .r12w }, + }); try expectEqualHexStrings("\x66\x41\x54", enc.code(), "push r12w"); - try enc.encode(.pop, .{ .op1 = .{ .reg = .r12 } }); + try enc.encode(.pop, &.{ + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x41\x5c", enc.code(), "pop r12"); } test "lower OI encoding" { var enc = TestEncode{}; - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x1000000000000000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x1000000000000000) }, + }); try expectEqualHexStrings( "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10", enc.code(), "movabs rax, 0x1000000000000000", ); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .imm = Immediate.u(0x1000000000000000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r11 }, + .{ .imm = Immediate.u(0x1000000000000000) }, + }); try expectEqualHexStrings( "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10", enc.code(), "movabs r11, 0x1000000000000000", ); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r11d }, .op2 = .{ .imm = Immediate.u(0x10000000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r11d }, + .{ .imm = Immediate.u(0x10000000) }, + }); try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r11w }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r11w }, + .{ .imm = Immediate.u(0x1000) }, + }); try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r11b }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r11b }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10"); } test "lower FD/TD encoding" { var enc = TestEncode{}; - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.moffs(.cs, 0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.moffs(.cs, 0x10) }, + }); try expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.moffs(.fs, 0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.moffs(.fs, 0x10) }, + }); try expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.moffs(.gs, 0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .ax }, + .{ .mem = Memory.moffs(.gs, 0x10) }, + }); try expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .al }, .op2 = .{ .mem = Memory.moffs(.ds, 0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .al }, + .{ .mem = Memory.moffs(.ds, 0x10) }, + }); try expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.cs, 0x10) }, .op2 = .{ .reg = .rax } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.moffs(.cs, 0x10) }, + .{ .reg = .rax }, + }); try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.fs, 0x10) }, .op2 = .{ .reg = .eax } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.moffs(.fs, 0x10) }, + .{ .reg = .eax }, + }); try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.gs, 0x10) }, .op2 = .{ .reg = .ax } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.moffs(.gs, 0x10) }, + .{ .reg = .ax }, + }); try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.ds, 0x10) }, .op2 = .{ .reg = .al } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.moffs(.ds, 0x10) }, + .{ .reg = .al }, + }); try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, al"); } test "lower NP encoding" { var enc = TestEncode{}; - try enc.encode(.int3, .{}); + try enc.encode(.int3, &.{}); try expectEqualHexStrings("\xCC", enc.code(), "int3"); - try enc.encode(.nop, .{}); + try enc.encode(.nop, &.{}); try expectEqualHexStrings("\x90", enc.code(), "nop"); - try enc.encode(.ret, .{}); + try enc.encode(.ret, &.{}); try expectEqualHexStrings("\xC3", enc.code(), "ret"); - try enc.encode(.syscall, .{}); + try enc.encode(.syscall, &.{}); try expectEqualHexStrings("\x0f\x05", enc.code(), "syscall"); } -fn invalidInstruction(mnemonic: Instruction.Mnemonic, args: Instruction.Init) !void { - const err = Instruction.new(mnemonic, args); +fn invalidInstruction(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand) !void { + const err = Instruction.new(.none, mnemonic, ops); try testing.expectError(error.InvalidInstruction, err); } test "invalid instruction" { - try invalidInstruction(.call, .{ .op1 = .{ .reg = .eax } }); - try invalidInstruction(.call, .{ .op1 = .{ .reg = .ax } }); - try invalidInstruction(.call, .{ .op1 = .{ .reg = .al } }); - try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.dword, 0) } }); - try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.word, 0) } }); - try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.byte, 0) } }); - try invalidInstruction(.mov, .{ .op1 = .{ .mem = Memory.rip(.word, 0x10) }, .op2 = .{ .reg = .r12 } }); - try invalidInstruction(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .rbx } }); - try invalidInstruction(.lea, .{ .op1 = .{ .reg = .al }, .op2 = .{ .mem = Memory.rip(.byte, 0) } }); - try invalidInstruction(.pop, .{ .op1 = .{ .reg = .r12b } }); - try invalidInstruction(.pop, .{ .op1 = .{ .reg = .r12d } }); - try invalidInstruction(.push, .{ .op1 = .{ .reg = .r12b } }); - try invalidInstruction(.push, .{ .op1 = .{ .reg = .r12d } }); - try invalidInstruction(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000000000000000) } }); + try invalidInstruction(.call, &.{ + .{ .reg = .eax }, + }); + try invalidInstruction(.call, &.{ + .{ .reg = .ax }, + }); + try invalidInstruction(.call, &.{ + .{ .reg = .al }, + }); + try invalidInstruction(.call, &.{ + .{ .mem = Memory.rip(.dword, 0) }, + }); + try invalidInstruction(.call, &.{ + .{ .mem = Memory.rip(.word, 0) }, + }); + try invalidInstruction(.call, &.{ + .{ .mem = Memory.rip(.byte, 0) }, + }); + try invalidInstruction(.mov, &.{ + .{ .mem = Memory.rip(.word, 0x10) }, + .{ .reg = .r12 }, + }); + try invalidInstruction(.lea, &.{ + .{ .reg = .rax }, + .{ .reg = .rbx }, + }); + try invalidInstruction(.lea, &.{ + .{ .reg = .al }, + .{ .mem = Memory.rip(.byte, 0) }, + }); + try invalidInstruction(.pop, &.{ + .{ .reg = .r12b }, + }); + try invalidInstruction(.pop, &.{ + .{ .reg = .r12d }, + }); + try invalidInstruction(.push, &.{ + .{ .reg = .r12b }, + }); + try invalidInstruction(.push, &.{ + .{ .reg = .r12d }, + }); + try invalidInstruction(.push, &.{ + .{ .imm = Immediate.u(0x1000000000000000) }, + }); } -fn cannotEncode(mnemonic: Instruction.Mnemonic, args: Instruction.Init) !void { - try testing.expectError(error.CannotEncode, Instruction.new(mnemonic, args)); +fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand) !void { + try testing.expectError(error.CannotEncode, Instruction.new(.none, mnemonic, ops)); } test "cannot encode" { - try cannotEncode(.@"test", .{ - .op1 = .{ .mem = Memory.sib(.byte, .{ .base = .r12 }) }, - .op2 = .{ .reg = .ah }, + try cannotEncode(.@"test", &.{ + .{ .mem = Memory.sib(.byte, .{ .base = .r12 }) }, + .{ .reg = .ah }, }); - try cannotEncode(.@"test", .{ - .op1 = .{ .reg = .r11b }, - .op2 = .{ .reg = .bh }, + try cannotEncode(.@"test", &.{ + .{ .reg = .r11b }, + .{ .reg = .bh }, }); - try cannotEncode(.mov, .{ - .op1 = .{ .reg = .sil }, - .op2 = .{ .reg = .ah }, + try cannotEncode(.mov, &.{ + .{ .reg = .sil }, + .{ .reg = .ah }, }); } @@ -1645,12 +1809,7 @@ const Assembler = struct { pub fn assemble(as: *Assembler, writer: anytype) !void { while (try as.next()) |parsed_inst| { - const inst = try Instruction.new(parsed_inst.mnemonic, .{ - .op1 = parsed_inst.ops[0], - .op2 = parsed_inst.ops[1], - .op3 = parsed_inst.ops[2], - .op4 = parsed_inst.ops[3], - }); + const inst = try Instruction.new(.none, parsed_inst.mnemonic, &parsed_inst.ops); try inst.encode(writer); } } |
