x86_64: refactor codegen memory repr

Also refactor linker reloc lowering.
This commit is contained in:
Jacob Young 2023-10-28 20:06:32 -04:00
parent 20ade4ce7f
commit 1fecf86ebf
8 changed files with 1249 additions and 1201 deletions

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ const Encoding = @import("Encoding.zig");
const Immediate = bits.Immediate;
const Instruction = encoder.Instruction;
const LegacyPrefixes = encoder.LegacyPrefixes;
const Memory = bits.Memory;
const Memory = Instruction.Memory;
const Register = bits.Register;
const Rex = encoder.Rex;

View File

@ -1,7 +1,6 @@
//! This file contains the functionality for emitting x86_64 MIR as machine code
lower: Lower,
bin_file: *link.File,
debug_output: DebugInfoOutput,
code: *std.ArrayList(u8),
@ -41,7 +40,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.offset = end_offset - 4,
.length = @intCast(end_offset - start_offset),
}),
.linker_extern_fn => |symbol| if (emit.bin_file.cast(link.File.Elf)) |elf_file| {
.linker_extern_fn => |symbol| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
// Add relocation to the decl.
const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
try atom_ptr.addReloc(elf_file, .{
@ -49,9 +48,10 @@ pub fn emitMir(emit: *Emit) Error!void {
.r_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | std.elf.R_X86_64_PLT32,
.r_addend = -4,
});
} else if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
} else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {
// Add relocation to the decl.
const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
const atom_index =
macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
const target = macho_file.getGlobalByIndex(symbol.sym_index);
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
.type = .branch,
@ -61,7 +61,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.pcrel = true,
.length = 2,
});
} else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
} else if (emit.lower.bin_file.cast(link.File.Coff)) |coff_file| {
// Add relocation to the decl.
const atom_index = coff_file.getAtomIndexForSymbol(
.{ .sym_index = symbol.atom_index, .file = null },
@ -76,12 +76,12 @@ pub fn emitMir(emit: *Emit) Error!void {
.length = 2,
});
} else return emit.fail("TODO implement extern reloc for {s}", .{
@tagName(emit.bin_file.tag),
@tagName(emit.lower.bin_file.tag),
}),
.linker_reloc => |data| if (emit.bin_file.cast(link.File.Elf)) |elf_file| {
.linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| {
const atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
const sym = elf_file.symbol(elf_file.zigModulePtr().symbol(data.sym_index));
if (emit.bin_file.options.pic) {
if (emit.lower.bin_file.options.pic) {
const r_type: u32 = if (sym.flags.has_zig_got)
link.File.Elf.R_X86_64_ZIG_GOTPCREL
else if (sym.flags.needs_got)
@ -111,10 +111,11 @@ pub fn emitMir(emit: *Emit) Error!void {
.linker_direct,
.linker_import,
.linker_tlv,
=> |symbol| if (emit.bin_file.cast(link.File.Elf)) |_| {
=> |symbol| if (emit.lower.bin_file.cast(link.File.Elf)) |_| {
unreachable;
} else if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
} else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {
const atom_index =
macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
.type = switch (lowered_relocs[0].target) {
.linker_got => .got,
@ -128,7 +129,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.pcrel = true,
.length = 2,
});
} else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
} else if (emit.lower.bin_file.cast(link.File.Coff)) |coff_file| {
const atom_index = coff_file.getAtomIndexForSymbol(.{
.sym_index = symbol.atom_index,
.file = null,
@ -152,7 +153,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.pcrel = true,
.length = 2,
});
} else if (emit.bin_file.cast(link.File.Plan9)) |p9_file| {
} else if (emit.lower.bin_file.cast(link.File.Plan9)) |p9_file| {
const atom_index = symbol.atom_index;
try p9_file.addReloc(atom_index, .{ // TODO we may need to add a .type field to the relocs if they are .linker_got instead of just .linker_direct
.target = symbol.sym_index, // we set sym_index to just be the atom index
@ -161,7 +162,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.type = .pcrel,
});
} else return emit.fail("TODO implement linker reloc for {s}", .{
@tagName(emit.bin_file.tag),
@tagName(emit.lower.bin_file.tag),
}),
};
}

View File

@ -803,7 +803,10 @@ fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Op
@memcpy(inst.ops[0..ops.len], ops);
var cwriter = std.io.countingWriter(std.io.null_writer);
inst.encode(cwriter.writer(), .{ .allow_frame_loc = true }) catch unreachable; // Not allowed to fail here unless OOM.
inst.encode(cwriter.writer(), .{
.allow_frame_locs = true,
.allow_symbols = true,
}) catch unreachable; // Not allowed to fail here unless OOM.
return @as(usize, @intCast(cwriter.bytes_written));
}

View File

@ -50,12 +50,12 @@ pub const Reloc = struct {
const Target = union(enum) {
inst: Mir.Inst.Index,
linker_reloc: Mir.Reloc,
linker_extern_fn: Mir.Reloc,
linker_got: Mir.Reloc,
linker_direct: Mir.Reloc,
linker_import: Mir.Reloc,
linker_tlv: Mir.Reloc,
linker_reloc: bits.Symbol,
linker_extern_fn: bits.Symbol,
linker_got: bits.Symbol,
linker_direct: bits.Symbol,
linker_import: bits.Symbol,
linker_tlv: bits.Symbol,
};
};
@ -99,17 +99,15 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.{ .reg = inst.data.rr.r2 },
});
},
.pseudo_cmov_nz_or_p_rm_sib,
.pseudo_cmov_nz_or_p_rm_rip,
=> {
.pseudo_cmov_nz_or_p_rm => {
assert(inst.data.rx.fixes == ._);
try lower.emit(.none, .cmovnz, &.{
.{ .reg = inst.data.rx.r1 },
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
.{ .mem = lower.mem(inst.data.rx.payload) },
});
try lower.emit(.none, .cmovp, &.{
.{ .reg = inst.data.rx.r1 },
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
.{ .mem = lower.mem(inst.data.rx.payload) },
});
},
.pseudo_set_z_and_np_r => {
@ -125,18 +123,16 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.{ .reg = inst.data.rr.r2 },
});
},
.pseudo_set_z_and_np_m_sib,
.pseudo_set_z_and_np_m_rip,
=> {
.pseudo_set_z_and_np_m => {
assert(inst.data.rx.fixes == ._);
try lower.emit(.none, .setz, &.{
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
.{ .mem = lower.mem(inst.data.rx.payload) },
});
try lower.emit(.none, .setnp, &.{
.{ .reg = inst.data.rx.r1 },
});
try lower.emit(.none, .@"and", &.{
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
.{ .mem = lower.mem(inst.data.rx.payload) },
.{ .reg = inst.data.rx.r1 },
});
},
@ -153,18 +149,16 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.{ .reg = inst.data.rr.r2 },
});
},
.pseudo_set_nz_or_p_m_sib,
.pseudo_set_nz_or_p_m_rip,
=> {
.pseudo_set_nz_or_p_m => {
assert(inst.data.rx.fixes == ._);
try lower.emit(.none, .setnz, &.{
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
.{ .mem = lower.mem(inst.data.rx.payload) },
});
try lower.emit(.none, .setp, &.{
.{ .reg = inst.data.rx.r1 },
});
try lower.emit(.none, .@"or", &.{
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
.{ .mem = lower.mem(inst.data.rx.payload) },
.{ .reg = inst.data.rx.r1 },
});
},
@ -289,28 +283,20 @@ fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
.rri_s,
.ri_s,
.i_s,
.mi_sib_s,
.mi_rip_s,
.rmi_sib_s,
.rmi_rip_s,
.mi_s,
.rmi_s,
=> Immediate.s(@bitCast(i)),
.rrri,
.rri_u,
.ri_u,
.i_u,
.mi_sib_u,
.mi_rip_u,
.rmi_sib,
.rmi_rip,
.rmi_sib_u,
.rmi_rip_u,
.mri_sib,
.mri_rip,
.rrm_sib,
.rrm_rip,
.rrmi_sib,
.rrmi_rip,
.mi_u,
.rmi,
.rmi_u,
.mri,
.rrm,
.rrmi,
=> Immediate.u(i),
.ri64 => Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()),
@ -319,50 +305,8 @@ fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
};
}
fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory {
return lower.mir.resolveFrameLoc(switch (ops) {
.rm_sib,
.rmi_sib,
.rmi_sib_s,
.rmi_sib_u,
.m_sib,
.mi_sib_u,
.mi_sib_s,
.mr_sib,
.mrr_sib,
.mri_sib,
.rrm_sib,
.rrmi_sib,
.pseudo_cmov_nz_or_p_rm_sib,
.pseudo_set_z_and_np_m_sib,
.pseudo_set_nz_or_p_m_sib,
=> lower.mir.extraData(Mir.MemorySib, payload).data.decode(),
.rm_rip,
.rmi_rip,
.rmi_rip_s,
.rmi_rip_u,
.m_rip,
.mi_rip_u,
.mi_rip_s,
.mr_rip,
.mrr_rip,
.mri_rip,
.rrm_rip,
.rrmi_rip,
.pseudo_cmov_nz_or_p_rm_rip,
.pseudo_set_z_and_np_m_rip,
.pseudo_set_nz_or_p_m_rip,
=> lower.mir.extraData(Mir.MemoryRip, payload).data.decode(),
.rax_moffs,
.moffs_rax,
=> lower.mir.extraData(Mir.MemoryMoffs, payload).data.decode(),
else => unreachable,
});
fn mem(lower: Lower, payload: u32) Memory {
return lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode();
}
fn reloc(lower: *Lower, target: Reloc.Target) Immediate {
@ -375,7 +319,42 @@ fn reloc(lower: *Lower, target: Reloc.Target) Immediate {
}
fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void {
lower.result_insts[lower.result_insts_len] = try Instruction.new(prefix, mnemonic, ops);
var emit_prefix = prefix;
var emit_mnemonic = mnemonic;
var emit_ops_storage: [4]Operand = undefined;
const emit_ops = emit_ops_storage[0..ops.len];
for (emit_ops, ops) |*emit_op, op| {
emit_op.* = switch (op) {
else => op,
.mem => |mem_op| switch (mem_op.base()) {
else => op,
.reloc => |sym| op: {
assert(prefix == .none);
assert(mem_op.sib.disp == 0);
assert(mem_op.sib.scale_index.scale == 0);
_ = lower.reloc(.{ .linker_reloc = sym });
break :op if (lower.bin_file.options.pic) switch (mnemonic) {
.mov, .lea => .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) },
else => unreachable,
} else switch (mnemonic) {
.call => .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
.base = .{ .reg = .ds },
}) },
.lea => {
emit_mnemonic = .mov;
break :op .{ .imm = Immediate.s(0) };
},
.mov => .{ .mem = Memory.sib(mem_op.sib.ptr_size, .{
.base = .{ .reg = .ds },
}) },
else => unreachable,
};
},
},
};
}
lower.result_insts[lower.result_insts_len] =
try Instruction.new(emit_prefix, emit_mnemonic, emit_ops);
lower.result_insts_len += 1;
}
@ -391,74 +370,13 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
.rrri => inst.data.rrri.fixes,
.rri_s, .rri_u => inst.data.rri.fixes,
.ri_s, .ri_u => inst.data.ri.fixes,
.ri64,
.rm_sib,
.rm_rip,
.rmi_sib_s,
.rmi_sib_u,
.rmi_rip_s,
.rmi_rip_u,
.mr_sib,
.mr_rip,
=> inst.data.rx.fixes,
.mrr_sib, .mrr_rip, .rrm_sib, .rrm_rip => inst.data.rrx.fixes,
.rmi_sib, .rmi_rip, .mri_sib, .mri_rip => inst.data.rix.fixes,
.rrmi_sib, .rrmi_rip => inst.data.rrix.fixes,
.mi_sib_u, .mi_rip_u, .mi_sib_s, .mi_rip_s => inst.data.x.fixes,
.m_sib, .m_rip, .rax_moffs, .moffs_rax => inst.data.x.fixes,
.extern_fn_reloc,
.got_reloc,
.direct_reloc,
.import_reloc,
.tlv_reloc,
=> ._,
.linker_reloc => {
if (lower.bin_file.options.pic) {
assert(inst.data.rx.fixes == ._);
const reg = inst.data.rx.r1;
const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
_ = lower.reloc(.{ .linker_reloc = extra });
const mnemonic: Mnemonic = switch (inst.tag) {
.mov => .mov,
.lea => .lea,
else => unreachable,
};
try lower.emit(.none, mnemonic, &.{
.{ .reg = reg },
.{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) },
});
} else {
switch (inst.tag) {
.call => {
_ = lower.reloc(.{ .linker_reloc = inst.data.reloc });
try lower.emit(.none, .call, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) },
});
},
.lea => {
assert(inst.data.rx.fixes == ._);
const reg = inst.data.rx.r1;
const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
try lower.emit(.none, .mov, &.{
.{ .reg = reg },
.{ .imm = lower.reloc(.{ .linker_reloc = extra }) },
});
},
.mov => {
assert(inst.data.rx.fixes == ._);
const reg = inst.data.rx.r1;
const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
_ = lower.reloc(.{ .linker_reloc = extra });
try lower.emit(.none, .mov, &.{
.{ .reg = reg },
.{ .mem = Memory.sib(.qword, .{ .base = .{ .reg = .ds }, .disp = 0 }) },
});
},
else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }),
}
}
return;
},
.ri64, .rm, .rmi_s, .mr => inst.data.rx.fixes,
.mrr, .rrm => inst.data.rrx.fixes,
.rmi, .mri => inst.data.rix.fixes,
.rrmi => inst.data.rrix.fixes,
.mi_u, .mi_s => inst.data.x.fixes,
.m => inst.data.x.fixes,
.extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ._,
else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}),
};
try lower.emit(switch (fixes) {
@ -527,73 +445,64 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
.{ .reg = inst.data.rri.r2 },
.{ .imm = lower.imm(inst.ops, inst.data.rri.i) },
},
.m_sib, .m_rip => &.{
.{ .mem = lower.mem(inst.ops, inst.data.x.payload) },
.m => &.{
.{ .mem = lower.mem(inst.data.x.payload) },
},
.mi_sib_s, .mi_sib_u, .mi_rip_s, .mi_rip_u => &.{
.{ .mem = lower.mem(inst.ops, inst.data.x.payload + 1) },
.mi_s, .mi_u => &.{
.{ .mem = lower.mem(inst.data.x.payload + 1) },
.{ .imm = lower.imm(
inst.ops,
lower.mir.extraData(Mir.Imm32, inst.data.x.payload).data.imm,
) },
},
.rm_sib, .rm_rip => &.{
.rm => &.{
.{ .reg = inst.data.rx.r1 },
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
.{ .mem = lower.mem(inst.data.rx.payload) },
},
.rmi_sib, .rmi_rip => &.{
.rmi => &.{
.{ .reg = inst.data.rix.r1 },
.{ .mem = lower.mem(inst.ops, inst.data.rix.payload) },
.{ .mem = lower.mem(inst.data.rix.payload) },
.{ .imm = lower.imm(inst.ops, inst.data.rix.i) },
},
.rmi_sib_s, .rmi_sib_u, .rmi_rip_s, .rmi_rip_u => &.{
.rmi_s, .rmi_u => &.{
.{ .reg = inst.data.rx.r1 },
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload + 1) },
.{ .mem = lower.mem(inst.data.rx.payload + 1) },
.{ .imm = lower.imm(
inst.ops,
lower.mir.extraData(Mir.Imm32, inst.data.rx.payload).data.imm,
) },
},
.mr_sib, .mr_rip => &.{
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) },
.mr => &.{
.{ .mem = lower.mem(inst.data.rx.payload) },
.{ .reg = inst.data.rx.r1 },
},
.mrr_sib, .mrr_rip => &.{
.{ .mem = lower.mem(inst.ops, inst.data.rrx.payload) },
.mrr => &.{
.{ .mem = lower.mem(inst.data.rrx.payload) },
.{ .reg = inst.data.rrx.r1 },
.{ .reg = inst.data.rrx.r2 },
},
.mri_sib, .mri_rip => &.{
.{ .mem = lower.mem(inst.ops, inst.data.rix.payload) },
.mri => &.{
.{ .mem = lower.mem(inst.data.rix.payload) },
.{ .reg = inst.data.rix.r1 },
.{ .imm = lower.imm(inst.ops, inst.data.rix.i) },
},
.rrm_sib, .rrm_rip => &.{
.rrm => &.{
.{ .reg = inst.data.rrx.r1 },
.{ .reg = inst.data.rrx.r2 },
.{ .mem = lower.mem(inst.ops, inst.data.rrx.payload) },
.{ .mem = lower.mem(inst.data.rrx.payload) },
},
.rrmi_sib, .rrmi_rip => &.{
.rrmi => &.{
.{ .reg = inst.data.rrix.r1 },
.{ .reg = inst.data.rrix.r2 },
.{ .mem = lower.mem(inst.ops, inst.data.rrix.payload) },
.{ .mem = lower.mem(inst.data.rrix.payload) },
.{ .imm = lower.imm(inst.ops, inst.data.rrix.i) },
},
.rax_moffs => &.{
.{ .reg = .rax },
.{ .mem = lower.mem(inst.ops, inst.data.x.payload) },
},
.moffs_rax => &.{
.{ .mem = lower.mem(inst.ops, inst.data.x.payload) },
.{ .reg = .rax },
},
.extern_fn_reloc => &.{
.{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) },
},
.linker_reloc => unreachable,
.got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: {
const reg = inst.data.rx.r1;
const extra = lower.mir.extraData(Mir.Reloc, inst.data.rx.payload).data;
const extra = lower.mir.extraData(bits.Symbol, inst.data.rx.payload).data;
_ = lower.reloc(switch (inst.ops) {
.got_reloc => .{ .linker_got = extra },
.direct_reloc => .{ .linker_direct = extra },
@ -635,7 +544,7 @@ const ErrorMsg = Module.ErrorMsg;
const Immediate = bits.Immediate;
const Instruction = encoder.Instruction;
const Lower = @This();
const Memory = bits.Memory;
const Memory = Instruction.Memory;
const Mir = @import("Mir.zig");
const Mnemonic = Instruction.Mnemonic;
const Module = @import("../../Module.zig");

View File

@ -17,7 +17,6 @@ const encoder = @import("encoder.zig");
const Air = @import("../../Air.zig");
const CodeGen = @import("CodeGen.zig");
const IntegerBitSet = std.bit_set.IntegerBitSet;
const Memory = bits.Memory;
const Register = bits.Register;
instructions: std.MultiArrayList(Inst).Slice,
@ -767,84 +766,42 @@ pub const Inst = struct {
/// Relative displacement operand.
/// Uses `imm` payload.
rel,
/// Register, memory (SIB) operands.
/// Register, memory operands.
/// Uses `rx` payload.
rm_sib,
/// Register, memory (RIP) operands.
/// Uses `rx` payload.
rm_rip,
/// Register, memory (SIB), immediate (word) operands.
/// Uses `rix` payload with extra data of type `MemorySib`.
rmi_sib,
/// Register, memory (RIP), immediate (word) operands.
/// Uses `rix` payload with extra data of type `MemoryRip`.
rmi_rip,
/// Register, memory (SIB), immediate (signed) operands.
/// Uses `rx` payload with extra data of type `Imm32` followed by `MemorySib`.
rmi_sib_s,
/// Register, memory (SIB), immediate (unsigned) operands.
/// Uses `rx` payload with extra data of type `Imm32` followed by `MemorySib`.
rmi_sib_u,
/// Register, memory (RIP), immediate (signed) operands.
/// Uses `rx` payload with extra data of type `Imm32` followed by `MemoryRip`.
rmi_rip_s,
/// Register, memory (RIP), immediate (unsigned) operands.
/// Uses `rx` payload with extra data of type `Imm32` followed by `MemoryRip`.
rmi_rip_u,
/// Register, register, memory (RIP).
/// Uses `rrix` payload with extra data of type `MemoryRip`.
rrm_rip,
/// Register, register, memory (SIB).
/// Uses `rrix` payload with extra data of type `MemorySib`.
rrm_sib,
/// Register, register, memory (RIP), immediate (byte) operands.
/// Uses `rrix` payload with extra data of type `MemoryRip`.
rrmi_rip,
/// Register, register, memory (SIB), immediate (byte) operands.
/// Uses `rrix` payload with extra data of type `MemorySib`.
rrmi_sib,
/// Single memory (SIB) operand.
/// Uses `x` with extra data of type `MemorySib`.
m_sib,
/// Single memory (RIP) operand.
/// Uses `x` with extra data of type `MemoryRip`.
m_rip,
/// Memory (SIB), immediate (sign-extend) operands.
/// Uses `x` payload with extra data of type `Imm32` followed by `MemorySib`.
mi_sib_s,
/// Memory (SIB), immediate (unsigned) operands.
/// Uses `x` payload with extra data of type `Imm32` followed by `MemorySib`.
mi_sib_u,
/// Memory (RIP), immediate (sign-extend) operands.
/// Uses `x` payload with extra data of type `Imm32` followed by `MemoryRip`.
mi_rip_s,
/// Memory (RIP), immediate (unsigned) operands.
/// Uses `x` payload with extra data of type `Imm32` followed by `MemoryRip`.
mi_rip_u,
/// Memory (SIB), register operands.
/// Uses `rx` payload with extra data of type `MemorySib`.
mr_sib,
/// Memory (RIP), register operands.
/// Uses `rx` payload with extra data of type `MemoryRip`.
mr_rip,
/// Memory (SIB), register, register operands.
/// Uses `rrx` payload with extra data of type `MemorySib`.
mrr_sib,
/// Memory (RIP), register, register operands.
/// Uses `rrx` payload with extra data of type `MemoryRip`.
mrr_rip,
/// Memory (SIB), register, immediate (word) operands.
/// Uses `rix` payload with extra data of type `MemorySib`.
mri_sib,
/// Memory (RIP), register, immediate (word) operands.
/// Uses `rix` payload with extra data of type `MemoryRip`.
mri_rip,
/// Rax, Memory moffs.
/// Uses `x` with extra data of type `MemoryMoffs`.
rax_moffs,
/// Memory moffs, rax.
/// Uses `x` with extra data of type `MemoryMoffs`.
moffs_rax,
rm,
/// Register, memory, immediate (word) operands.
/// Uses `rix` payload with extra data of type `Memory`.
rmi,
/// Register, memory, immediate (signed) operands.
/// Uses `rx` payload with extra data of type `Imm32` followed by `Memory`.
rmi_s,
/// Register, memory, immediate (unsigned) operands.
/// Uses `rx` payload with extra data of type `Imm32` followed by `Memory`.
rmi_u,
/// Register, register, memory.
/// Uses `rrix` payload with extra data of type `Memory`.
rrm,
/// Register, register, memory, immediate (byte) operands.
/// Uses `rrix` payload with extra data of type `Memory`.
rrmi,
/// Single memory operand.
/// Uses `x` with extra data of type `Memory`.
m,
/// Memory, immediate (sign-extend) operands.
/// Uses `x` payload with extra data of type `Imm32` followed by `Memory`.
mi_s,
/// Memory, immediate (unsigned) operands.
/// Uses `x` payload with extra data of type `Imm32` followed by `Memory`.
mi_u,
/// Memory, register operands.
/// Uses `rx` payload with extra data of type `Memory`.
mr,
/// Memory, register, register operands.
/// Uses `rrx` payload with extra data of type `Memory`.
mrr,
/// Memory, register, immediate (word) operands.
/// Uses `rix` payload with extra data of type `Memory`.
mri,
/// References another Mir instruction directly.
/// Uses `inst` payload.
inst,
@ -852,20 +809,17 @@ pub const Inst = struct {
/// Uses `reloc` payload.
extern_fn_reloc,
/// Linker relocation - GOT indirection.
/// Uses `rx` payload with extra data of type `Reloc`.
/// Uses `rx` payload with extra data of type `bits.Symbol`.
got_reloc,
/// Linker relocation - direct reference.
/// Uses `rx` payload with extra data of type `Reloc`.
/// Uses `rx` payload with extra data of type `bits.Symbol`.
direct_reloc,
/// Linker relocation - imports table indirection (binding).
/// Uses `rx` payload with extra data of type `Reloc`.
/// Uses `rx` payload with extra data of type `bits.Symbol`.
import_reloc,
/// Linker relocation - threadlocal variable via GOT indirection.
/// Uses `rx` payload with extra data of type `Reloc`.
/// Uses `rx` payload with extra data of type `bits.Symbol`.
tlv_reloc,
/// Linker relocation.
/// Uses `rx` payload with extra data of type `Reloc`.
linker_reloc,
// Pseudo instructions:
@ -878,10 +832,7 @@ pub const Inst = struct {
pseudo_cmov_nz_or_p_rr,
/// Conditional move if zero flag not set or parity flag set
/// Uses `rx` payload.
pseudo_cmov_nz_or_p_rm_sib,
/// Conditional move if zero flag not set or parity flag set
/// Uses `rx` payload.
pseudo_cmov_nz_or_p_rm_rip,
pseudo_cmov_nz_or_p_rm,
/// Set byte if zero flag set and parity flag not set
/// Requires a scratch register!
/// Uses `rr` payload.
@ -889,11 +840,7 @@ pub const Inst = struct {
/// Set byte if zero flag set and parity flag not set
/// Requires a scratch register!
/// Uses `rx` payload.
pseudo_set_z_and_np_m_sib,
/// Set byte if zero flag set and parity flag not set
/// Requires a scratch register!
/// Uses `rx` payload.
pseudo_set_z_and_np_m_rip,
pseudo_set_z_and_np_m,
/// Set byte if zero flag not set or parity flag set
/// Requires a scratch register!
/// Uses `rr` payload.
@ -901,11 +848,7 @@ pub const Inst = struct {
/// Set byte if zero flag not set or parity flag set
/// Requires a scratch register!
/// Uses `rx` payload.
pseudo_set_nz_or_p_m_sib,
/// Set byte if zero flag not set or parity flag set
/// Requires a scratch register!
/// Uses `rx` payload.
pseudo_set_nz_or_p_m_rip,
pseudo_set_nz_or_p_m,
/// Jump if zero flag set and parity flag not set
/// Uses `inst` payload.
pseudo_j_z_and_np_inst,
@ -1036,7 +979,7 @@ pub const Inst = struct {
/// Relocation for the linker where:
/// * `atom_index` is the index of the source
/// * `sym_index` is the index of the target
reloc: Reloc,
reloc: bits.Symbol,
/// Debug line and column position
line_column: struct {
line: u32,
@ -1055,14 +998,6 @@ pub const Inst = struct {
}
};
/// A linker symbol not yet allocated in VM.
pub const Reloc = struct {
/// Index of the containing atom.
atom_index: u32,
/// Index into the linker's symbol table.
sym_index: u32,
};
/// Used in conjunction with payload to transfer a list of used registers in a compact manner.
pub const RegisterList = struct {
bitset: BitSet = BitSet.initEmpty(),
@ -1123,100 +1058,94 @@ pub const Imm64 = struct {
}
};
// TODO this can be further compacted using packed struct
pub const MemorySib = struct {
/// Size of the pointer.
ptr_size: u32,
/// Base register tag of type Memory.Base.Tag
base_tag: u32,
/// Base register of type Register or FrameIndex
pub const Memory = struct {
info: Info,
base: u32,
/// Scale starting at bit 0 and index register starting at bit 4.
scale_index: u32,
/// Displacement value.
disp: i32,
off: u32,
extra: u32,
pub fn encode(mem: Memory) MemorySib {
const sib = mem.sib;
assert(sib.scale_index.scale == 0 or std.math.isPowerOfTwo(sib.scale_index.scale));
pub const Info = packed struct(u32) {
base: @typeInfo(bits.Memory.Base).Union.tag_type.?,
mod: @typeInfo(bits.Memory.Mod).Union.tag_type.?,
size: bits.Memory.Size,
index: Register,
scale: bits.Memory.Scale,
_: u16 = undefined,
};
pub fn encode(mem: bits.Memory) Memory {
assert(mem.base != .reloc or mem.mod != .off);
return .{
.ptr_size = @intFromEnum(sib.ptr_size),
.base_tag = @intFromEnum(sib.base),
.base = switch (sib.base) {
.info = .{
.base = mem.base,
.mod = mem.mod,
.size = switch (mem.mod) {
.rm => |rm| rm.size,
.off => undefined,
},
.index = switch (mem.mod) {
.rm => |rm| rm.index,
.off => undefined,
},
.scale = switch (mem.mod) {
.rm => |rm| rm.scale,
.off => undefined,
},
},
.base = switch (mem.base) {
.none => undefined,
.reg => |r| @intFromEnum(r),
.frame => |fi| @intFromEnum(fi),
.reg => |reg| @intFromEnum(reg),
.frame => |frame_index| @intFromEnum(frame_index),
.reloc => |symbol| symbol.sym_index,
},
.scale_index = @as(u32, sib.scale_index.scale) << 0 |
@as(u32, if (sib.scale_index.scale > 0)
@intFromEnum(sib.scale_index.index)
.off = switch (mem.mod) {
.rm => |rm| @bitCast(rm.disp),
.off => |off| @truncate(off),
},
.extra = if (mem.base == .reloc)
mem.base.reloc.atom_index
else if (mem.mod == .off)
@intCast(mem.mod.off >> 32)
else
undefined) << 4,
.disp = sib.disp,
undefined,
};
}
pub fn decode(msib: MemorySib) Memory {
const scale: u4 = @truncate(msib.scale_index);
assert(scale == 0 or std.math.isPowerOfTwo(scale));
return .{ .sib = .{
.ptr_size = @enumFromInt(msib.ptr_size),
.base = switch (@as(Memory.Base.Tag, @enumFromInt(msib.base_tag))) {
pub fn decode(mem: Memory) encoder.Instruction.Memory {
switch (mem.info.mod) {
.rm => {
if (mem.info.base == .reg and @as(Register, @enumFromInt(mem.base)) == .rip) {
assert(mem.info.index == .none and mem.info.scale == .@"1");
return encoder.Instruction.Memory.rip(mem.info.size, @bitCast(mem.off));
}
return encoder.Instruction.Memory.sib(mem.info.size, .{
.disp = @bitCast(mem.off),
.base = switch (mem.info.base) {
.none => .none,
.reg => .{ .reg = @enumFromInt(msib.base) },
.frame => .{ .frame = @enumFromInt(msib.base) },
.reg => .{ .reg = @enumFromInt(mem.base) },
.frame => .{ .frame = @enumFromInt(mem.base) },
.reloc => .{ .reloc = .{ .atom_index = mem.extra, .sym_index = mem.base } },
},
.scale_index = .{
.scale = scale,
.index = if (scale > 0) @enumFromInt(msib.scale_index >> 4) else undefined,
.scale_index = switch (mem.info.index) {
.none => null,
else => |index| .{ .scale = switch (mem.info.scale) {
inline else => |scale| comptime std.fmt.parseInt(
u4,
@tagName(scale),
10,
) catch unreachable,
}, .index = index },
},
});
},
.off => {
assert(mem.info.base == .reg);
return encoder.Instruction.Memory.moffs(
@enumFromInt(mem.base),
@as(u64, mem.extra) << 32 | mem.off,
);
},
.disp = msib.disp,
} };
}
};
pub const MemoryRip = struct {
/// Size of the pointer.
ptr_size: u32,
/// Displacement value.
disp: i32,
pub fn encode(mem: Memory) MemoryRip {
return .{
.ptr_size = @intFromEnum(mem.rip.ptr_size),
.disp = mem.rip.disp,
};
}
pub fn decode(mrip: MemoryRip) Memory {
return .{ .rip = .{
.ptr_size = @enumFromInt(mrip.ptr_size),
.disp = mrip.disp,
} };
}
};
pub const MemoryMoffs = struct {
/// Segment register.
seg: u32,
/// Absolute offset wrt to the segment register split between MSB and LSB parts much like
/// `Imm64` payload.
msb: u32,
lsb: u32,
pub fn encode(seg: Register, offset: u64) MemoryMoffs {
return .{
.seg = @intFromEnum(seg),
.msb = @truncate(offset >> 32),
.lsb = @truncate(offset >> 0),
};
}
pub fn decode(moffs: MemoryMoffs) Memory {
return .{ .moffs = .{
.seg = @enumFromInt(moffs.seg),
.offset = @as(u64, moffs.msb) << 32 | @as(u64, moffs.lsb) << 0,
} };
}
};
@ -1234,8 +1163,8 @@ pub fn extraData(mir: Mir, comptime T: type, index: u32) struct { data: T, end:
inline for (fields) |field| {
@field(result, field.name) = switch (field.type) {
u32 => mir.extra[i],
i32 => @bitCast(mir.extra[i]),
else => @compileError("bad field type"),
i32, Memory.Info => @bitCast(mir.extra[i]),
else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)),
};
i += 1;
}
@ -1251,15 +1180,19 @@ pub const FrameLoc = struct {
};
pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory {
return switch (mem) {
.sib => |sib| switch (sib.base) {
.none, .reg => mem,
.frame => |index| if (mir.frame_locs.len > 0) Memory.sib(sib.ptr_size, .{
.base = .{ .reg = mir.frame_locs.items(.base)[@intFromEnum(index)] },
.disp = mir.frame_locs.items(.disp)[@intFromEnum(index)] + sib.disp,
.scale_index = mem.scaleIndex(),
}) else mem,
return switch (mem.info.base) {
.none, .reg, .reloc => mem,
.frame => if (mir.frame_locs.len > 0) Memory{
.info = .{
.base = .reg,
.mod = mem.info.mod,
.size = mem.info.size,
.index = mem.info.index,
.scale = mem.info.scale,
},
.rip, .moffs => mem,
.base = @intFromEnum(mir.frame_locs.items(.base)[mem.base]),
.off = @bitCast(mir.frame_locs.items(.disp)[mem.base] + @as(i32, @bitCast(mem.off))),
.extra = mem.extra,
} else mem,
};
}

View File

@ -181,6 +181,8 @@ pub const Register = enum(u7) {
es, cs, ss, ds, fs, gs,
rip, eip, ip,
none,
// zig fmt: on
@ -442,34 +444,58 @@ pub const FrameIndex = enum(u32) {
}
};
pub const Memory = union(enum) {
sib: Sib,
rip: Rip,
moffs: Moffs,
/// A linker symbol not yet allocated in VM.
pub const Symbol = struct {
/// Index of the containing atom.
atom_index: u32,
/// Index into the linker's symbol table.
sym_index: u32,
pub const Base = union(enum) {
pub fn format(
sym: Symbol,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) @TypeOf(writer).Error!void {
try writer.writeAll("Symbol(");
try std.fmt.formatType(sym.atom_index, fmt, options, writer, 0);
try writer.writeAll(", ");
try std.fmt.formatType(sym.sym_index, fmt, options, writer, 0);
try writer.writeByte(')');
}
};
pub const Memory = struct {
base: Base,
mod: Mod,
pub const Base = union(enum(u2)) {
none,
reg: Register,
frame: FrameIndex,
reloc: Symbol,
pub const Tag = @typeInfo(Base).Union.tag_type.?;
pub fn isExtended(self: Base) bool {
return switch (self) {
.none, .frame => false, // neither rsp nor rbp are extended
.none, .frame, .reloc => false, // rsp, rbp, and rip are not extended
.reg => |reg| reg.isExtended(),
};
}
};
pub const ScaleIndex = struct {
scale: u4,
index: Register,
const none = ScaleIndex{ .scale = 0, .index = undefined };
pub const Mod = union(enum(u1)) {
rm: struct {
size: Size,
index: Register = .none,
scale: Scale = .@"1",
disp: i32 = 0,
},
off: u64,
};
pub const PtrSize = enum {
pub const Size = enum(u4) {
none,
byte,
word,
@ -480,7 +506,7 @@ pub const Memory = union(enum) {
yword,
zword,
pub fn fromSize(size: u32) PtrSize {
pub fn fromSize(size: u32) Size {
return switch (size) {
1...1 => .byte,
2...2 => .word,
@ -493,7 +519,7 @@ pub const Memory = union(enum) {
};
}
pub fn fromBitSize(bit_size: u64) PtrSize {
pub fn fromBitSize(bit_size: u64) Size {
return switch (bit_size) {
8 => .byte,
16 => .word,
@ -507,7 +533,7 @@ pub const Memory = union(enum) {
};
}
pub fn bitSize(s: PtrSize) u64 {
pub fn bitSize(s: Size) u64 {
return switch (s) {
.none => 0,
.byte => 8,
@ -522,7 +548,7 @@ pub const Memory = union(enum) {
}
pub fn format(
s: PtrSize,
s: Size,
comptime _: []const u8,
_: std.fmt.FormatOptions,
writer: anytype,
@ -533,79 +559,7 @@ pub const Memory = union(enum) {
}
};
pub const Sib = struct {
ptr_size: PtrSize,
base: Base,
scale_index: ScaleIndex,
disp: i32,
};
pub const Rip = struct {
ptr_size: PtrSize,
disp: i32,
};
pub const Moffs = struct {
seg: Register,
offset: u64,
};
pub fn moffs(reg: Register, offset: u64) Memory {
assert(reg.class() == .segment);
return .{ .moffs = .{ .seg = reg, .offset = offset } };
}
pub fn sib(ptr_size: PtrSize, args: struct {
disp: i32 = 0,
base: Base = .none,
scale_index: ?ScaleIndex = null,
}) Memory {
if (args.scale_index) |si| assert(std.math.isPowerOfTwo(si.scale));
return .{ .sib = .{
.base = args.base,
.disp = args.disp,
.ptr_size = ptr_size,
.scale_index = if (args.scale_index) |si| si else ScaleIndex.none,
} };
}
pub fn rip(ptr_size: PtrSize, disp: i32) Memory {
return .{ .rip = .{ .ptr_size = ptr_size, .disp = disp } };
}
pub fn isSegmentRegister(mem: Memory) bool {
return switch (mem) {
.moffs => true,
.rip => false,
.sib => |s| switch (s.base) {
.none, .frame => false,
.reg => |reg| reg.class() == .segment,
},
};
}
pub fn base(mem: Memory) Base {
return switch (mem) {
.moffs => |m| .{ .reg = m.seg },
.sib => |s| s.base,
.rip => .none,
};
}
pub fn scaleIndex(mem: Memory) ?ScaleIndex {
return switch (mem) {
.moffs, .rip => null,
.sib => |s| if (s.scale_index.scale > 0) s.scale_index else null,
};
}
pub fn bitSize(mem: Memory) u64 {
return switch (mem) {
.rip => |r| r.ptr_size.bitSize(),
.sib => |s| s.ptr_size.bitSize(),
.moffs => 64,
};
}
pub const Scale = enum(u2) { @"1", @"2", @"4", @"8" };
};
pub const Immediate = union(enum) {

View File

@ -6,9 +6,10 @@ const testing = std.testing;
const bits = @import("bits.zig");
const Encoding = @import("Encoding.zig");
const FrameIndex = bits.FrameIndex;
const Immediate = bits.Immediate;
const Memory = bits.Memory;
const Register = bits.Register;
const Symbol = bits.Symbol;
pub const Instruction = struct {
prefix: Prefix = .none,
@ -27,6 +28,97 @@ pub const Instruction = struct {
repnz,
};
pub const Memory = union(enum) {
sib: Sib,
rip: Rip,
moffs: Moffs,
pub const Base = bits.Memory.Base;
pub const ScaleIndex = struct {
scale: u4,
index: Register,
const none = ScaleIndex{ .scale = 0, .index = undefined };
};
pub const PtrSize = bits.Memory.Size;
pub const Sib = struct {
ptr_size: PtrSize,
base: Base,
scale_index: ScaleIndex,
disp: i32,
};
pub const Rip = struct {
ptr_size: PtrSize,
disp: i32,
};
pub const Moffs = struct {
seg: Register,
offset: u64,
};
pub fn moffs(reg: Register, offset: u64) Memory {
assert(reg.class() == .segment);
return .{ .moffs = .{ .seg = reg, .offset = offset } };
}
pub fn sib(ptr_size: PtrSize, args: struct {
disp: i32 = 0,
base: Base = .none,
scale_index: ?ScaleIndex = null,
}) Memory {
if (args.scale_index) |si| assert(std.math.isPowerOfTwo(si.scale));
return .{ .sib = .{
.base = args.base,
.disp = args.disp,
.ptr_size = ptr_size,
.scale_index = if (args.scale_index) |si| si else ScaleIndex.none,
} };
}
pub fn rip(ptr_size: PtrSize, disp: i32) Memory {
return .{ .rip = .{ .ptr_size = ptr_size, .disp = disp } };
}
pub fn isSegmentRegister(mem: Memory) bool {
return switch (mem) {
.moffs => true,
.rip => false,
.sib => |s| switch (s.base) {
.none, .frame, .reloc => false,
.reg => |reg| reg.class() == .segment,
},
};
}
pub fn base(mem: Memory) Base {
return switch (mem) {
.moffs => |m| .{ .reg = m.seg },
.sib => |s| s.base,
.rip => .none,
};
}
pub fn scaleIndex(mem: Memory) ?ScaleIndex {
return switch (mem) {
.moffs, .rip => null,
.sib => |s| if (s.scale_index.scale > 0) s.scale_index else null,
};
}
pub fn bitSize(mem: Memory) u64 {
return switch (mem) {
.rip => |r| r.ptr_size.bitSize(),
.sib => |s| s.ptr_size.bitSize(),
.moffs => 64,
};
}
};
pub const Operand = union(enum) {
none,
reg: Register,
@ -125,8 +217,8 @@ pub const Instruction = struct {
try writer.print("{s}", .{@tagName(reg)});
any = true;
},
.frame => |frame| {
try writer.print("{}", .{frame});
inline .frame, .reloc => |payload| {
try writer.print("{}", .{payload});
any = true;
},
}
@ -498,7 +590,11 @@ pub const Instruction = struct {
}
}
},
.frame => if (@TypeOf(encoder).options.allow_frame_loc) {
.frame => if (@TypeOf(encoder).options.allow_frame_locs) {
try encoder.modRm_indirectDisp32(operand_enc, undefined);
try encoder.disp32(undefined);
} else return error.CannotEncode,
.reloc => if (@TypeOf(encoder).options.allow_symbols) {
try encoder.modRm_indirectDisp32(operand_enc, undefined);
try encoder.disp32(undefined);
} else return error.CannotEncode,
@ -570,7 +666,7 @@ pub const LegacyPrefixes = packed struct {
}
};
pub const Options = struct { allow_frame_loc: bool = false };
pub const Options = struct { allow_frame_locs: bool = false, allow_symbols: bool = false };
fn Encoder(comptime T: type, comptime opts: Options) type {
return struct {
@ -1085,7 +1181,7 @@ test "lower MI encoding" {
try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.byte, .{ .base = .r12 }) },
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) },
.{ .imm = Immediate.u(0x10) },
});
try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10");
@ -1109,13 +1205,13 @@ test "lower MI encoding" {
try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.dword, .{ .base = .r11 }) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.rip(.qword, 0x10) },
.{ .mem = Instruction.Memory.rip(.qword, 0x10) },
.{ .imm = Immediate.u(0x10) },
});
try expectEqualHexStrings(
@ -1125,25 +1221,25 @@ test "lower MI encoding" {
);
try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -8 }) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -2 }) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.sib(.byte, .{ .base = .rbp, .disp = -1 }) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.sib(.qword, .{
.{ .mem = Instruction.Memory.sib(.qword, .{
.base = .ds,
.disp = 0x10000000,
.scale_index = .{ .scale = 2, .index = .rcx },
@ -1157,13 +1253,13 @@ test "lower MI encoding" {
);
try enc.encode(.adc, &.{
.{ .mem = Memory.sib(.byte, .{ .base = .rbp, .disp = -0x10 }) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.rip(.qword, 0) },
.{ .mem = Instruction.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");
@ -1175,7 +1271,7 @@ test "lower MI encoding" {
try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
try enc.encode(.add, &.{
.{ .mem = Memory.sib(.dword, .{ .base = .rdx, .disp = -8 }) },
.{ .mem = Instruction.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");
@ -1187,13 +1283,13 @@ test "lower MI encoding" {
try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
try enc.encode(.add, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -0x10 }) },
.{ .mem = Instruction.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", &.{
.{ .mem = Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) },
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) },
.{ .imm = Immediate.u(0x10) },
});
try expectEqualHexStrings(
@ -1203,7 +1299,7 @@ test "lower MI encoding" {
);
try enc.encode(.@"and", &.{
.{ .mem = Memory.sib(.dword, .{ .base = .es, .disp = 0x10000000 }) },
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .es, .disp = 0x10000000 }) },
.{ .imm = Immediate.u(0x10) },
});
try expectEqualHexStrings(
@ -1213,7 +1309,7 @@ test "lower MI encoding" {
);
try enc.encode(.@"and", &.{
.{ .mem = Memory.sib(.dword, .{ .base = .r12, .disp = 0x10000000 }) },
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r12, .disp = 0x10000000 }) },
.{ .imm = Immediate.u(0x10) },
});
try expectEqualHexStrings(
@ -1223,7 +1319,7 @@ test "lower MI encoding" {
);
try enc.encode(.sub, &.{
.{ .mem = Memory.sib(.dword, .{ .base = .r11, .disp = 0x10000000 }) },
.{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r11, .disp = 0x10000000 }) },
.{ .imm = Immediate.u(0x10) },
});
try expectEqualHexStrings(
@ -1238,25 +1334,25 @@ test "lower RM encoding" {
try enc.encode(.mov, &.{
.{ .reg = .rax },
.{ .mem = Memory.sib(.qword, .{ .base = .r11 }) },
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r11 }) },
});
try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]");
try enc.encode(.mov, &.{
.{ .reg = .rbx },
.{ .mem = Memory.sib(.qword, .{ .base = .ds, .disp = 0x10 }) },
.{ .mem = Instruction.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, &.{
.{ .reg = .rax },
.{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) },
.{ .mem = Instruction.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, &.{
.{ .reg = .rax },
.{ .mem = Memory.sib(.qword, .{
.{ .mem = Instruction.Memory.sib(.qword, .{
.base = .rbp,
.scale_index = .{ .scale = 1, .index = .rcx },
.disp = -8,
@ -1266,7 +1362,7 @@ test "lower RM encoding" {
try enc.encode(.mov, &.{
.{ .reg = .eax },
.{ .mem = Memory.sib(.dword, .{
.{ .mem = Instruction.Memory.sib(.dword, .{
.base = .rbp,
.scale_index = .{ .scale = 4, .index = .rdx },
.disp = -4,
@ -1276,7 +1372,7 @@ test "lower RM encoding" {
try enc.encode(.mov, &.{
.{ .reg = .rax },
.{ .mem = Memory.sib(.qword, .{
.{ .mem = Instruction.Memory.sib(.qword, .{
.base = .rbp,
.scale_index = .{ .scale = 8, .index = .rcx },
.disp = -8,
@ -1286,7 +1382,7 @@ test "lower RM encoding" {
try enc.encode(.mov, &.{
.{ .reg = .r8b },
.{ .mem = Memory.sib(.byte, .{
.{ .mem = Instruction.Memory.sib(.byte, .{
.base = .rsi,
.scale_index = .{ .scale = 1, .index = .rcx },
.disp = -24,
@ -1302,7 +1398,7 @@ test "lower RM encoding" {
try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs");
try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -16 }) },
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -16 }) },
.{ .reg = .fs },
});
try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], fs");
@ -1314,7 +1410,7 @@ test "lower RM encoding" {
try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs");
try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
.{ .reg = .fs },
});
try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs");
@ -1339,19 +1435,19 @@ test "lower RM encoding" {
try enc.encode(.movsx, &.{
.{ .reg = .eax },
.{ .mem = Memory.sib(.word, .{ .base = .rbp }) },
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp }) },
});
try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]");
try enc.encode(.movsx, &.{
.{ .reg = .eax },
.{ .mem = Memory.sib(.byte, .{ .scale_index = .{ .index = .rax, .scale = 2 } }) },
.{ .mem = Instruction.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, &.{
.{ .reg = .ax },
.{ .mem = Memory.rip(.byte, 0x10) },
.{ .mem = Instruction.Memory.rip(.byte, 0x10) },
});
try expectEqualHexStrings("\x66\x0F\xBE\x05\x10\x00\x00\x00", enc.code(), "movsx ax, BYTE PTR [rip + 0x10]");
@ -1369,37 +1465,37 @@ test "lower RM encoding" {
try enc.encode(.lea, &.{
.{ .reg = .rax },
.{ .mem = Memory.rip(.qword, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .reg = .rax },
.{ .mem = Memory.rip(.dword, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .reg = .eax },
.{ .mem = Memory.rip(.dword, 0x10) },
.{ .mem = Instruction.Memory.rip(.dword, 0x10) },
});
try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]");
try enc.encode(.lea, &.{
.{ .reg = .eax },
.{ .mem = Memory.rip(.word, 0x10) },
.{ .mem = Instruction.Memory.rip(.word, 0x10) },
});
try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]");
try enc.encode(.lea, &.{
.{ .reg = .ax },
.{ .mem = Memory.rip(.byte, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .reg = .rsi },
.{ .mem = Memory.sib(.qword, .{
.{ .mem = Instruction.Memory.sib(.qword, .{
.base = .rbp,
.scale_index = .{ .scale = 1, .index = .rcx },
}) },
@ -1408,31 +1504,31 @@ test "lower RM encoding" {
try enc.encode(.add, &.{
.{ .reg = .r11 },
.{ .mem = Memory.sib(.qword, .{ .base = .ds, .disp = 0x10000000 }) },
.{ .mem = Instruction.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, &.{
.{ .reg = .r12b },
.{ .mem = Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) },
.{ .mem = Instruction.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, &.{
.{ .reg = .r12b },
.{ .mem = Memory.sib(.byte, .{ .base = .fs, .disp = 0x10000000 }) },
.{ .mem = Instruction.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, &.{
.{ .reg = .r11 },
.{ .mem = Memory.sib(.qword, .{ .base = .r13, .disp = 0x10000000 }) },
.{ .mem = Instruction.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, &.{
.{ .reg = .r11 },
.{ .mem = Memory.sib(.qword, .{ .base = .r12, .disp = 0x10000000 }) },
.{ .mem = Instruction.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]");
@ -1455,7 +1551,7 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{
.{ .reg = .r11 },
.{ .mem = Memory.rip(.qword, -16) },
.{ .mem = Instruction.Memory.rip(.qword, -16) },
.{ .imm = Immediate.s(-1024) },
});
try expectEqualHexStrings(
@ -1466,7 +1562,7 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{
.{ .reg = .bx },
.{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
.{ .imm = Immediate.s(-1024) },
});
try expectEqualHexStrings(
@ -1477,7 +1573,7 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{
.{ .reg = .bx },
.{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
.{ .imm = Immediate.u(1024) },
});
try expectEqualHexStrings(
@ -1497,19 +1593,19 @@ test "lower MR encoding" {
try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx");
try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.rip(.qword, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.sib(.qword, .{
.{ .mem = Instruction.Memory.sib(.qword, .{
.base = .r11,
.scale_index = .{ .scale = 2, .index = .r12 },
.disp = 0x10,
@ -1519,13 +1615,13 @@ test "lower MR encoding" {
try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13");
try enc.encode(.mov, &.{
.{ .mem = Memory.rip(.word, -0x10) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.sib(.byte, .{
.{ .mem = Instruction.Memory.sib(.byte, .{
.base = .r11,
.scale_index = .{ .scale = 2, .index = .r12 },
.disp = 0x10,
@ -1535,25 +1631,25 @@ test "lower MR encoding" {
try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b");
try enc.encode(.add, &.{
.{ .mem = Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.sib(.dword, .{ .base = .gs, .disp = 0x10000000 }) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .r11, .disp = 0x10000000 }) },
.{ .mem = Instruction.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");
@ -1568,12 +1664,12 @@ test "lower M encoding" {
try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12");
try enc.encode(.call, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .r12 }) },
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r12 }) },
});
try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]");
try enc.encode(.call, &.{
.{ .mem = Memory.sib(.qword, .{
.{ .mem = Instruction.Memory.sib(.qword, .{
.base = null,
.scale_index = .{ .index = .r11, .scale = 2 },
}) },
@ -1581,7 +1677,7 @@ test "lower M encoding" {
try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]");
try enc.encode(.call, &.{
.{ .mem = Memory.sib(.qword, .{
.{ .mem = Instruction.Memory.sib(.qword, .{
.base = null,
.scale_index = .{ .index = .r12, .scale = 2 },
}) },
@ -1589,7 +1685,7 @@ test "lower M encoding" {
try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]");
try enc.encode(.call, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .gs }) },
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .gs }) },
});
try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0");
@ -1599,22 +1695,22 @@ test "lower M encoding" {
try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
try enc.encode(.push, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .rbp }) },
.{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp }) },
});
try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
try enc.encode(.push, &.{
.{ .mem = Memory.sib(.word, .{ .base = .rbp }) },
.{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp }) },
});
try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
try enc.encode(.pop, &.{
.{ .mem = Memory.rip(.qword, 0) },
.{ .mem = Instruction.Memory.rip(.qword, 0) },
});
try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]");
try enc.encode(.pop, &.{
.{ .mem = Memory.rip(.word, 0) },
.{ .mem = Instruction.Memory.rip(.word, 0) },
});
try expectEqualHexStrings("\x66\x8F\x05\x00\x00\x00\x00", enc.code(), "pop WORD PTR [rbp]");
@ -1695,48 +1791,48 @@ test "lower FD/TD encoding" {
try enc.encode(.mov, &.{
.{ .reg = .rax },
.{ .mem = Memory.moffs(.cs, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .reg = .eax },
.{ .mem = Memory.moffs(.fs, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .reg = .ax },
.{ .mem = Memory.moffs(.gs, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .reg = .al },
.{ .mem = Memory.moffs(.ds, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.moffs(.cs, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.moffs(.fs, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.moffs(.gs, 0x10) },
.{ .mem = Instruction.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, &.{
.{ .mem = Memory.moffs(.ds, 0x10) },
.{ .mem = Instruction.Memory.moffs(.ds, 0x10) },
.{ .reg = .al },
});
try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, al");
@ -1774,16 +1870,16 @@ test "invalid instruction" {
.{ .reg = .al },
});
try invalidInstruction(.call, &.{
.{ .mem = Memory.rip(.dword, 0) },
.{ .mem = Instruction.Memory.rip(.dword, 0) },
});
try invalidInstruction(.call, &.{
.{ .mem = Memory.rip(.word, 0) },
.{ .mem = Instruction.Memory.rip(.word, 0) },
});
try invalidInstruction(.call, &.{
.{ .mem = Memory.rip(.byte, 0) },
.{ .mem = Instruction.Memory.rip(.byte, 0) },
});
try invalidInstruction(.mov, &.{
.{ .mem = Memory.rip(.word, 0x10) },
.{ .mem = Instruction.Memory.rip(.word, 0x10) },
.{ .reg = .r12 },
});
try invalidInstruction(.lea, &.{
@ -1792,7 +1888,7 @@ test "invalid instruction" {
});
try invalidInstruction(.lea, &.{
.{ .reg = .al },
.{ .mem = Memory.rip(.byte, 0) },
.{ .mem = Instruction.Memory.rip(.byte, 0) },
});
try invalidInstruction(.pop, &.{
.{ .reg = .r12b },
@ -1817,7 +1913,7 @@ fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand
test "cannot encode" {
try cannotEncode(.@"test", &.{
.{ .mem = Memory.sib(.byte, .{ .base = .r12 }) },
.{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) },
.{ .reg = .ah },
});
try cannotEncode(.@"test", &.{
@ -2149,8 +2245,8 @@ const Assembler = struct {
return null;
}
fn parseMemory(as: *Assembler) ParseError!Memory {
const ptr_size: ?Memory.PtrSize = blk: {
fn parseMemory(as: *Assembler) ParseError!Instruction.Memory {
const ptr_size: ?Instruction.Memory.PtrSize = blk: {
const pos = as.it.pos;
const ptr_size = as.parsePtrSize() catch |err| switch (err) {
error.UnexpectedToken => {
@ -2194,7 +2290,7 @@ const Assembler = struct {
if (res.rip) {
if (res.base != null or res.scale_index != null or res.offset != null)
return error.InvalidMemoryOperand;
return Memory.rip(ptr_size orelse .qword, res.disp orelse 0);
return Instruction.Memory.rip(ptr_size orelse .qword, res.disp orelse 0);
}
if (res.base) |base| {
if (res.rip)
@ -2202,9 +2298,9 @@ const Assembler = struct {
if (res.offset) |offset| {
if (res.scale_index != null or res.disp != null)
return error.InvalidMemoryOperand;
return Memory.moffs(base, offset);
return Instruction.Memory.moffs(base, offset);
}
return Memory.sib(ptr_size orelse .qword, .{
return Instruction.Memory.sib(ptr_size orelse .qword, .{
.base = base,
.scale_index = res.scale_index,
.disp = res.disp orelse 0,
@ -2222,12 +2318,12 @@ const Assembler = struct {
const MemoryParseResult = struct {
rip: bool = false,
base: ?Register = null,
scale_index: ?Memory.ScaleIndex = null,
scale_index: ?Instruction.Memory.ScaleIndex = null,
disp: ?i32 = null,
offset: ?u64 = null,
};
fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!MemoryParseResult {
fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!Instruction.MemoryParseResult {
var res: MemoryParseResult = .{};
inline for (rule, 0..) |cond, i| {
if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) {
@ -2294,7 +2390,7 @@ const Assembler = struct {
return res;
}
fn parsePtrSize(as: *Assembler) ParseError!Memory.PtrSize {
fn parsePtrSize(as: *Assembler) ParseError!Instruction.Memory.PtrSize {
const size = try as.expect(.string);
try as.skip(1, .{.space});
const ptr = try as.expect(.string);