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 Immediate = bits.Immediate;
const Instruction = encoder.Instruction; const Instruction = encoder.Instruction;
const LegacyPrefixes = encoder.LegacyPrefixes; const LegacyPrefixes = encoder.LegacyPrefixes;
const Memory = bits.Memory; const Memory = Instruction.Memory;
const Register = bits.Register; const Register = bits.Register;
const Rex = encoder.Rex; const Rex = encoder.Rex;

View File

@ -1,7 +1,6 @@
//! This file contains the functionality for emitting x86_64 MIR as machine code //! This file contains the functionality for emitting x86_64 MIR as machine code
lower: Lower, lower: Lower,
bin_file: *link.File,
debug_output: DebugInfoOutput, debug_output: DebugInfoOutput,
code: *std.ArrayList(u8), code: *std.ArrayList(u8),
@ -41,7 +40,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.offset = end_offset - 4, .offset = end_offset - 4,
.length = @intCast(end_offset - start_offset), .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. // Add relocation to the decl.
const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?; const atom_ptr = elf_file.symbol(symbol.atom_index).atom(elf_file).?;
try atom_ptr.addReloc(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_info = (@as(u64, @intCast(symbol.sym_index)) << 32) | std.elf.R_X86_64_PLT32,
.r_addend = -4, .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. // 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); const target = macho_file.getGlobalByIndex(symbol.sym_index);
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
.type = .branch, .type = .branch,
@ -61,7 +61,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.pcrel = true, .pcrel = true,
.length = 2, .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. // Add relocation to the decl.
const atom_index = coff_file.getAtomIndexForSymbol( const atom_index = coff_file.getAtomIndexForSymbol(
.{ .sym_index = symbol.atom_index, .file = null }, .{ .sym_index = symbol.atom_index, .file = null },
@ -76,12 +76,12 @@ pub fn emitMir(emit: *Emit) Error!void {
.length = 2, .length = 2,
}); });
} else return emit.fail("TODO implement extern reloc for {s}", .{ } 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 atom = elf_file.symbol(data.atom_index).atom(elf_file).?;
const sym = elf_file.symbol(elf_file.zigModulePtr().symbol(data.sym_index)); 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) const r_type: u32 = if (sym.flags.has_zig_got)
link.File.Elf.R_X86_64_ZIG_GOTPCREL link.File.Elf.R_X86_64_ZIG_GOTPCREL
else if (sym.flags.needs_got) else if (sym.flags.needs_got)
@ -111,10 +111,11 @@ pub fn emitMir(emit: *Emit) Error!void {
.linker_direct, .linker_direct,
.linker_import, .linker_import,
.linker_tlv, .linker_tlv,
=> |symbol| if (emit.bin_file.cast(link.File.Elf)) |_| { => |symbol| if (emit.lower.bin_file.cast(link.File.Elf)) |_| {
unreachable; unreachable;
} else if (emit.bin_file.cast(link.File.MachO)) |macho_file| { } else if (emit.lower.bin_file.cast(link.File.MachO)) |macho_file| {
const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?; const atom_index =
macho_file.getAtomIndexForSymbol(.{ .sym_index = symbol.atom_index }).?;
try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
.type = switch (lowered_relocs[0].target) { .type = switch (lowered_relocs[0].target) {
.linker_got => .got, .linker_got => .got,
@ -128,7 +129,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.pcrel = true, .pcrel = true,
.length = 2, .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(.{ const atom_index = coff_file.getAtomIndexForSymbol(.{
.sym_index = symbol.atom_index, .sym_index = symbol.atom_index,
.file = null, .file = null,
@ -152,7 +153,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.pcrel = true, .pcrel = true,
.length = 2, .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; 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 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 .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, .type = .pcrel,
}); });
} else return emit.fail("TODO implement linker reloc for {s}", .{ } 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); @memcpy(inst.ops[0..ops.len], ops);
var cwriter = std.io.countingWriter(std.io.null_writer); 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)); return @as(usize, @intCast(cwriter.bytes_written));
} }

View File

@ -50,12 +50,12 @@ pub const Reloc = struct {
const Target = union(enum) { const Target = union(enum) {
inst: Mir.Inst.Index, inst: Mir.Inst.Index,
linker_reloc: Mir.Reloc, linker_reloc: bits.Symbol,
linker_extern_fn: Mir.Reloc, linker_extern_fn: bits.Symbol,
linker_got: Mir.Reloc, linker_got: bits.Symbol,
linker_direct: Mir.Reloc, linker_direct: bits.Symbol,
linker_import: Mir.Reloc, linker_import: bits.Symbol,
linker_tlv: Mir.Reloc, linker_tlv: bits.Symbol,
}; };
}; };
@ -99,17 +99,15 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
.{ .reg = inst.data.rr.r2 }, .{ .reg = inst.data.rr.r2 },
}); });
}, },
.pseudo_cmov_nz_or_p_rm_sib, .pseudo_cmov_nz_or_p_rm => {
.pseudo_cmov_nz_or_p_rm_rip,
=> {
assert(inst.data.rx.fixes == ._); assert(inst.data.rx.fixes == ._);
try lower.emit(.none, .cmovnz, &.{ try lower.emit(.none, .cmovnz, &.{
.{ .reg = inst.data.rx.r1 }, .{ .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, &.{ try lower.emit(.none, .cmovp, &.{
.{ .reg = inst.data.rx.r1 }, .{ .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 => { .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 }, .{ .reg = inst.data.rr.r2 },
}); });
}, },
.pseudo_set_z_and_np_m_sib, .pseudo_set_z_and_np_m => {
.pseudo_set_z_and_np_m_rip,
=> {
assert(inst.data.rx.fixes == ._); assert(inst.data.rx.fixes == ._);
try lower.emit(.none, .setz, &.{ 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, &.{ try lower.emit(.none, .setnp, &.{
.{ .reg = inst.data.rx.r1 }, .{ .reg = inst.data.rx.r1 },
}); });
try lower.emit(.none, .@"and", &.{ 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 }, .{ .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 }, .{ .reg = inst.data.rr.r2 },
}); });
}, },
.pseudo_set_nz_or_p_m_sib, .pseudo_set_nz_or_p_m => {
.pseudo_set_nz_or_p_m_rip,
=> {
assert(inst.data.rx.fixes == ._); assert(inst.data.rx.fixes == ._);
try lower.emit(.none, .setnz, &.{ 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, &.{ try lower.emit(.none, .setp, &.{
.{ .reg = inst.data.rx.r1 }, .{ .reg = inst.data.rx.r1 },
}); });
try lower.emit(.none, .@"or", &.{ 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 }, .{ .reg = inst.data.rx.r1 },
}); });
}, },
@ -289,28 +283,20 @@ fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
.rri_s, .rri_s,
.ri_s, .ri_s,
.i_s, .i_s,
.mi_sib_s, .mi_s,
.mi_rip_s, .rmi_s,
.rmi_sib_s,
.rmi_rip_s,
=> Immediate.s(@bitCast(i)), => Immediate.s(@bitCast(i)),
.rrri, .rrri,
.rri_u, .rri_u,
.ri_u, .ri_u,
.i_u, .i_u,
.mi_sib_u, .mi_u,
.mi_rip_u, .rmi,
.rmi_sib, .rmi_u,
.rmi_rip, .mri,
.rmi_sib_u, .rrm,
.rmi_rip_u, .rrmi,
.mri_sib,
.mri_rip,
.rrm_sib,
.rrm_rip,
.rrmi_sib,
.rrmi_rip,
=> Immediate.u(i), => Immediate.u(i),
.ri64 => Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()), .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 { fn mem(lower: Lower, payload: u32) Memory {
return lower.mir.resolveFrameLoc(switch (ops) { return lower.mir.resolveFrameLoc(lower.mir.extraData(Mir.Memory, payload).data).decode();
.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 reloc(lower: *Lower, target: Reloc.Target) Immediate { 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 { 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; lower.result_insts_len += 1;
} }
@ -391,74 +370,13 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
.rrri => inst.data.rrri.fixes, .rrri => inst.data.rrri.fixes,
.rri_s, .rri_u => inst.data.rri.fixes, .rri_s, .rri_u => inst.data.rri.fixes,
.ri_s, .ri_u => inst.data.ri.fixes, .ri_s, .ri_u => inst.data.ri.fixes,
.ri64, .ri64, .rm, .rmi_s, .mr => inst.data.rx.fixes,
.rm_sib, .mrr, .rrm => inst.data.rrx.fixes,
.rm_rip, .rmi, .mri => inst.data.rix.fixes,
.rmi_sib_s, .rrmi => inst.data.rrix.fixes,
.rmi_sib_u, .mi_u, .mi_s => inst.data.x.fixes,
.rmi_rip_s, .m => inst.data.x.fixes,
.rmi_rip_u, .extern_fn_reloc, .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ._,
.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;
},
else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}), else => return lower.fail("TODO lower .{s}", .{@tagName(inst.ops)}),
}; };
try lower.emit(switch (fixes) { try lower.emit(switch (fixes) {
@ -527,73 +445,64 @@ fn generic(lower: *Lower, inst: Mir.Inst) Error!void {
.{ .reg = inst.data.rri.r2 }, .{ .reg = inst.data.rri.r2 },
.{ .imm = lower.imm(inst.ops, inst.data.rri.i) }, .{ .imm = lower.imm(inst.ops, inst.data.rri.i) },
}, },
.m_sib, .m_rip => &.{ .m => &.{
.{ .mem = lower.mem(inst.ops, inst.data.x.payload) }, .{ .mem = lower.mem(inst.data.x.payload) },
}, },
.mi_sib_s, .mi_sib_u, .mi_rip_s, .mi_rip_u => &.{ .mi_s, .mi_u => &.{
.{ .mem = lower.mem(inst.ops, inst.data.x.payload + 1) }, .{ .mem = lower.mem(inst.data.x.payload + 1) },
.{ .imm = lower.imm( .{ .imm = lower.imm(
inst.ops, inst.ops,
lower.mir.extraData(Mir.Imm32, inst.data.x.payload).data.imm, lower.mir.extraData(Mir.Imm32, inst.data.x.payload).data.imm,
) }, ) },
}, },
.rm_sib, .rm_rip => &.{ .rm => &.{
.{ .reg = inst.data.rx.r1 }, .{ .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 }, .{ .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) }, .{ .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 }, .{ .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( .{ .imm = lower.imm(
inst.ops, inst.ops,
lower.mir.extraData(Mir.Imm32, inst.data.rx.payload).data.imm, lower.mir.extraData(Mir.Imm32, inst.data.rx.payload).data.imm,
) }, ) },
}, },
.mr_sib, .mr_rip => &.{ .mr => &.{
.{ .mem = lower.mem(inst.ops, inst.data.rx.payload) }, .{ .mem = lower.mem(inst.data.rx.payload) },
.{ .reg = inst.data.rx.r1 }, .{ .reg = inst.data.rx.r1 },
}, },
.mrr_sib, .mrr_rip => &.{ .mrr => &.{
.{ .mem = lower.mem(inst.ops, inst.data.rrx.payload) }, .{ .mem = lower.mem(inst.data.rrx.payload) },
.{ .reg = inst.data.rrx.r1 }, .{ .reg = inst.data.rrx.r1 },
.{ .reg = inst.data.rrx.r2 }, .{ .reg = inst.data.rrx.r2 },
}, },
.mri_sib, .mri_rip => &.{ .mri => &.{
.{ .mem = lower.mem(inst.ops, inst.data.rix.payload) }, .{ .mem = lower.mem(inst.data.rix.payload) },
.{ .reg = inst.data.rix.r1 }, .{ .reg = inst.data.rix.r1 },
.{ .imm = lower.imm(inst.ops, inst.data.rix.i) }, .{ .imm = lower.imm(inst.ops, inst.data.rix.i) },
}, },
.rrm_sib, .rrm_rip => &.{ .rrm => &.{
.{ .reg = inst.data.rrx.r1 }, .{ .reg = inst.data.rrx.r1 },
.{ .reg = inst.data.rrx.r2 }, .{ .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.r1 },
.{ .reg = inst.data.rrix.r2 }, .{ .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) }, .{ .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 => &.{ .extern_fn_reloc => &.{
.{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) }, .{ .imm = lower.reloc(.{ .linker_extern_fn = inst.data.reloc }) },
}, },
.linker_reloc => unreachable,
.got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: { .got_reloc, .direct_reloc, .import_reloc, .tlv_reloc => ops: {
const reg = inst.data.rx.r1; 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) { _ = lower.reloc(switch (inst.ops) {
.got_reloc => .{ .linker_got = extra }, .got_reloc => .{ .linker_got = extra },
.direct_reloc => .{ .linker_direct = extra }, .direct_reloc => .{ .linker_direct = extra },
@ -635,7 +544,7 @@ const ErrorMsg = Module.ErrorMsg;
const Immediate = bits.Immediate; const Immediate = bits.Immediate;
const Instruction = encoder.Instruction; const Instruction = encoder.Instruction;
const Lower = @This(); const Lower = @This();
const Memory = bits.Memory; const Memory = Instruction.Memory;
const Mir = @import("Mir.zig"); const Mir = @import("Mir.zig");
const Mnemonic = Instruction.Mnemonic; const Mnemonic = Instruction.Mnemonic;
const Module = @import("../../Module.zig"); const Module = @import("../../Module.zig");

View File

@ -17,7 +17,6 @@ const encoder = @import("encoder.zig");
const Air = @import("../../Air.zig"); const Air = @import("../../Air.zig");
const CodeGen = @import("CodeGen.zig"); const CodeGen = @import("CodeGen.zig");
const IntegerBitSet = std.bit_set.IntegerBitSet; const IntegerBitSet = std.bit_set.IntegerBitSet;
const Memory = bits.Memory;
const Register = bits.Register; const Register = bits.Register;
instructions: std.MultiArrayList(Inst).Slice, instructions: std.MultiArrayList(Inst).Slice,
@ -767,84 +766,42 @@ pub const Inst = struct {
/// Relative displacement operand. /// Relative displacement operand.
/// Uses `imm` payload. /// Uses `imm` payload.
rel, rel,
/// Register, memory (SIB) operands. /// Register, memory operands.
/// Uses `rx` payload. /// Uses `rx` payload.
rm_sib, rm,
/// Register, memory (RIP) operands. /// Register, memory, immediate (word) operands.
/// Uses `rx` payload. /// Uses `rix` payload with extra data of type `Memory`.
rm_rip, rmi,
/// Register, memory (SIB), immediate (word) operands. /// Register, memory, immediate (signed) operands.
/// Uses `rix` payload with extra data of type `MemorySib`. /// Uses `rx` payload with extra data of type `Imm32` followed by `Memory`.
rmi_sib, rmi_s,
/// Register, memory (RIP), immediate (word) operands. /// Register, memory, immediate (unsigned) operands.
/// Uses `rix` payload with extra data of type `MemoryRip`. /// Uses `rx` payload with extra data of type `Imm32` followed by `Memory`.
rmi_rip, rmi_u,
/// Register, memory (SIB), immediate (signed) operands. /// Register, register, memory.
/// Uses `rx` payload with extra data of type `Imm32` followed by `MemorySib`. /// Uses `rrix` payload with extra data of type `Memory`.
rmi_sib_s, rrm,
/// Register, memory (SIB), immediate (unsigned) operands. /// Register, register, memory, immediate (byte) operands.
/// Uses `rx` payload with extra data of type `Imm32` followed by `MemorySib`. /// Uses `rrix` payload with extra data of type `Memory`.
rmi_sib_u, rrmi,
/// Register, memory (RIP), immediate (signed) operands. /// Single memory operand.
/// Uses `rx` payload with extra data of type `Imm32` followed by `MemoryRip`. /// Uses `x` with extra data of type `Memory`.
rmi_rip_s, m,
/// Register, memory (RIP), immediate (unsigned) operands. /// Memory, immediate (sign-extend) operands.
/// Uses `rx` payload with extra data of type `Imm32` followed by `MemoryRip`. /// Uses `x` payload with extra data of type `Imm32` followed by `Memory`.
rmi_rip_u, mi_s,
/// Register, register, memory (RIP). /// Memory, immediate (unsigned) operands.
/// Uses `rrix` payload with extra data of type `MemoryRip`. /// Uses `x` payload with extra data of type `Imm32` followed by `Memory`.
rrm_rip, mi_u,
/// Register, register, memory (SIB). /// Memory, register operands.
/// Uses `rrix` payload with extra data of type `MemorySib`. /// Uses `rx` payload with extra data of type `Memory`.
rrm_sib, mr,
/// Register, register, memory (RIP), immediate (byte) operands. /// Memory, register, register operands.
/// Uses `rrix` payload with extra data of type `MemoryRip`. /// Uses `rrx` payload with extra data of type `Memory`.
rrmi_rip, mrr,
/// Register, register, memory (SIB), immediate (byte) operands. /// Memory, register, immediate (word) operands.
/// Uses `rrix` payload with extra data of type `MemorySib`. /// Uses `rix` payload with extra data of type `Memory`.
rrmi_sib, mri,
/// 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,
/// References another Mir instruction directly. /// References another Mir instruction directly.
/// Uses `inst` payload. /// Uses `inst` payload.
inst, inst,
@ -852,20 +809,17 @@ pub const Inst = struct {
/// Uses `reloc` payload. /// Uses `reloc` payload.
extern_fn_reloc, extern_fn_reloc,
/// Linker relocation - GOT indirection. /// 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, got_reloc,
/// Linker relocation - direct reference. /// 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, direct_reloc,
/// Linker relocation - imports table indirection (binding). /// 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, import_reloc,
/// Linker relocation - threadlocal variable via GOT indirection. /// 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, tlv_reloc,
/// Linker relocation.
/// Uses `rx` payload with extra data of type `Reloc`.
linker_reloc,
// Pseudo instructions: // Pseudo instructions:
@ -878,10 +832,7 @@ pub const Inst = struct {
pseudo_cmov_nz_or_p_rr, pseudo_cmov_nz_or_p_rr,
/// Conditional move if zero flag not set or parity flag set /// Conditional move if zero flag not set or parity flag set
/// Uses `rx` payload. /// Uses `rx` payload.
pseudo_cmov_nz_or_p_rm_sib, pseudo_cmov_nz_or_p_rm,
/// Conditional move if zero flag not set or parity flag set
/// Uses `rx` payload.
pseudo_cmov_nz_or_p_rm_rip,
/// Set byte if zero flag set and parity flag not set /// Set byte if zero flag set and parity flag not set
/// Requires a scratch register! /// Requires a scratch register!
/// Uses `rr` payload. /// Uses `rr` payload.
@ -889,11 +840,7 @@ pub const Inst = struct {
/// Set byte if zero flag set and parity flag not set /// Set byte if zero flag set and parity flag not set
/// Requires a scratch register! /// Requires a scratch register!
/// Uses `rx` payload. /// Uses `rx` payload.
pseudo_set_z_and_np_m_sib, pseudo_set_z_and_np_m,
/// 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,
/// Set byte if zero flag not set or parity flag set /// Set byte if zero flag not set or parity flag set
/// Requires a scratch register! /// Requires a scratch register!
/// Uses `rr` payload. /// Uses `rr` payload.
@ -901,11 +848,7 @@ pub const Inst = struct {
/// Set byte if zero flag not set or parity flag set /// Set byte if zero flag not set or parity flag set
/// Requires a scratch register! /// Requires a scratch register!
/// Uses `rx` payload. /// Uses `rx` payload.
pseudo_set_nz_or_p_m_sib, pseudo_set_nz_or_p_m,
/// 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,
/// Jump if zero flag set and parity flag not set /// Jump if zero flag set and parity flag not set
/// Uses `inst` payload. /// Uses `inst` payload.
pseudo_j_z_and_np_inst, pseudo_j_z_and_np_inst,
@ -1036,7 +979,7 @@ pub const Inst = struct {
/// Relocation for the linker where: /// Relocation for the linker where:
/// * `atom_index` is the index of the source /// * `atom_index` is the index of the source
/// * `sym_index` is the index of the target /// * `sym_index` is the index of the target
reloc: Reloc, reloc: bits.Symbol,
/// Debug line and column position /// Debug line and column position
line_column: struct { line_column: struct {
line: u32, 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. /// Used in conjunction with payload to transfer a list of used registers in a compact manner.
pub const RegisterList = struct { pub const RegisterList = struct {
bitset: BitSet = BitSet.initEmpty(), bitset: BitSet = BitSet.initEmpty(),
@ -1123,100 +1058,94 @@ pub const Imm64 = struct {
} }
}; };
// TODO this can be further compacted using packed struct pub const Memory = struct {
pub const MemorySib = struct { info: Info,
/// 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
base: u32, base: u32,
/// Scale starting at bit 0 and index register starting at bit 4. off: u32,
scale_index: u32, extra: u32,
/// Displacement value.
disp: i32,
pub fn encode(mem: Memory) MemorySib { pub const Info = packed struct(u32) {
const sib = mem.sib; base: @typeInfo(bits.Memory.Base).Union.tag_type.?,
assert(sib.scale_index.scale == 0 or std.math.isPowerOfTwo(sib.scale_index.scale)); 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 .{ return .{
.ptr_size = @intFromEnum(sib.ptr_size), .info = .{
.base_tag = @intFromEnum(sib.base), .base = mem.base,
.base = switch (sib.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, .none => undefined,
.reg => |r| @intFromEnum(r), .reg => |reg| @intFromEnum(reg),
.frame => |fi| @intFromEnum(fi), .frame => |frame_index| @intFromEnum(frame_index),
.reloc => |symbol| symbol.sym_index,
}, },
.scale_index = @as(u32, sib.scale_index.scale) << 0 | .off = switch (mem.mod) {
@as(u32, if (sib.scale_index.scale > 0) .rm => |rm| @bitCast(rm.disp),
@intFromEnum(sib.scale_index.index) .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 else
undefined) << 4, undefined,
.disp = sib.disp,
}; };
} }
pub fn decode(msib: MemorySib) Memory { pub fn decode(mem: Memory) encoder.Instruction.Memory {
const scale: u4 = @truncate(msib.scale_index); switch (mem.info.mod) {
assert(scale == 0 or std.math.isPowerOfTwo(scale)); .rm => {
return .{ .sib = .{ if (mem.info.base == .reg and @as(Register, @enumFromInt(mem.base)) == .rip) {
.ptr_size = @enumFromInt(msib.ptr_size), assert(mem.info.index == .none and mem.info.scale == .@"1");
.base = switch (@as(Memory.Base.Tag, @enumFromInt(msib.base_tag))) { return encoder.Instruction.Memory.rip(mem.info.size, @bitCast(mem.off));
.none => .none, }
.reg => .{ .reg = @enumFromInt(msib.base) }, return encoder.Instruction.Memory.sib(mem.info.size, .{
.frame => .{ .frame = @enumFromInt(msib.base) }, .disp = @bitCast(mem.off),
.base = switch (mem.info.base) {
.none => .none,
.reg => .{ .reg = @enumFromInt(mem.base) },
.frame => .{ .frame = @enumFromInt(mem.base) },
.reloc => .{ .reloc = .{ .atom_index = mem.extra, .sym_index = mem.base } },
},
.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 },
},
});
}, },
.scale_index = .{ .off => {
.scale = scale, assert(mem.info.base == .reg);
.index = if (scale > 0) @enumFromInt(msib.scale_index >> 4) else undefined, 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| { inline for (fields) |field| {
@field(result, field.name) = switch (field.type) { @field(result, field.name) = switch (field.type) {
u32 => mir.extra[i], u32 => mir.extra[i],
i32 => @bitCast(mir.extra[i]), i32, Memory.Info => @bitCast(mir.extra[i]),
else => @compileError("bad field type"), else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)),
}; };
i += 1; i += 1;
} }
@ -1251,15 +1180,19 @@ pub const FrameLoc = struct {
}; };
pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory { pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory {
return switch (mem) { return switch (mem.info.base) {
.sib => |sib| switch (sib.base) { .none, .reg, .reloc => mem,
.none, .reg => mem, .frame => if (mir.frame_locs.len > 0) Memory{
.frame => |index| if (mir.frame_locs.len > 0) Memory.sib(sib.ptr_size, .{ .info = .{
.base = .{ .reg = mir.frame_locs.items(.base)[@intFromEnum(index)] }, .base = .reg,
.disp = mir.frame_locs.items(.disp)[@intFromEnum(index)] + sib.disp, .mod = mem.info.mod,
.scale_index = mem.scaleIndex(), .size = mem.info.size,
}) else mem, .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, es, cs, ss, ds, fs, gs,
rip, eip, ip,
none, none,
// zig fmt: on // zig fmt: on
@ -442,34 +444,58 @@ pub const FrameIndex = enum(u32) {
} }
}; };
pub const Memory = union(enum) { /// A linker symbol not yet allocated in VM.
sib: Sib, pub const Symbol = struct {
rip: Rip, /// Index of the containing atom.
moffs: Moffs, 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, none,
reg: Register, reg: Register,
frame: FrameIndex, frame: FrameIndex,
reloc: Symbol,
pub const Tag = @typeInfo(Base).Union.tag_type.?; pub const Tag = @typeInfo(Base).Union.tag_type.?;
pub fn isExtended(self: Base) bool { pub fn isExtended(self: Base) bool {
return switch (self) { 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(), .reg => |reg| reg.isExtended(),
}; };
} }
}; };
pub const ScaleIndex = struct { pub const Mod = union(enum(u1)) {
scale: u4, rm: struct {
index: Register, size: Size,
index: Register = .none,
const none = ScaleIndex{ .scale = 0, .index = undefined }; scale: Scale = .@"1",
disp: i32 = 0,
},
off: u64,
}; };
pub const PtrSize = enum { pub const Size = enum(u4) {
none, none,
byte, byte,
word, word,
@ -480,7 +506,7 @@ pub const Memory = union(enum) {
yword, yword,
zword, zword,
pub fn fromSize(size: u32) PtrSize { pub fn fromSize(size: u32) Size {
return switch (size) { return switch (size) {
1...1 => .byte, 1...1 => .byte,
2...2 => .word, 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) { return switch (bit_size) {
8 => .byte, 8 => .byte,
16 => .word, 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) { return switch (s) {
.none => 0, .none => 0,
.byte => 8, .byte => 8,
@ -522,7 +548,7 @@ pub const Memory = union(enum) {
} }
pub fn format( pub fn format(
s: PtrSize, s: Size,
comptime _: []const u8, comptime _: []const u8,
_: std.fmt.FormatOptions, _: std.fmt.FormatOptions,
writer: anytype, writer: anytype,
@ -533,79 +559,7 @@ pub const Memory = union(enum) {
} }
}; };
pub const Sib = struct { pub const Scale = enum(u2) { @"1", @"2", @"4", @"8" };
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 Immediate = union(enum) { pub const Immediate = union(enum) {

View File

@ -6,9 +6,10 @@ const testing = std.testing;
const bits = @import("bits.zig"); const bits = @import("bits.zig");
const Encoding = @import("Encoding.zig"); const Encoding = @import("Encoding.zig");
const FrameIndex = bits.FrameIndex;
const Immediate = bits.Immediate; const Immediate = bits.Immediate;
const Memory = bits.Memory;
const Register = bits.Register; const Register = bits.Register;
const Symbol = bits.Symbol;
pub const Instruction = struct { pub const Instruction = struct {
prefix: Prefix = .none, prefix: Prefix = .none,
@ -27,6 +28,97 @@ pub const Instruction = struct {
repnz, 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) { pub const Operand = union(enum) {
none, none,
reg: Register, reg: Register,
@ -125,8 +217,8 @@ pub const Instruction = struct {
try writer.print("{s}", .{@tagName(reg)}); try writer.print("{s}", .{@tagName(reg)});
any = true; any = true;
}, },
.frame => |frame| { inline .frame, .reloc => |payload| {
try writer.print("{}", .{frame}); try writer.print("{}", .{payload});
any = true; 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.modRm_indirectDisp32(operand_enc, undefined);
try encoder.disp32(undefined); try encoder.disp32(undefined);
} else return error.CannotEncode, } 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 { fn Encoder(comptime T: type, comptime opts: Options) type {
return struct { 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 expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.byte, .{ .base = .r12 }) }, .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 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 expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.dword, .{ .base = .r11 }) }, .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .r11 }) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.rip(.qword, 0x10) }, .{ .mem = Instruction.Memory.rip(.qword, 0x10) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
@ -1125,25 +1221,25 @@ test "lower MI encoding" {
); );
try enc.encode(.mov, &.{ 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) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10");
try enc.encode(.mov, &.{ 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) }, .{ .imm = Immediate.s(-16) },
}); });
try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16");
try enc.encode(.mov, &.{ 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) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.qword, .{ .{ .mem = Instruction.Memory.sib(.qword, .{
.base = .ds, .base = .ds,
.disp = 0x10000000, .disp = 0x10000000,
.scale_index = .{ .scale = 2, .index = .rcx }, .scale_index = .{ .scale = 2, .index = .rcx },
@ -1157,13 +1253,13 @@ test "lower MI encoding" {
); );
try enc.encode(.adc, &.{ 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) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10");
try enc.encode(.adc, &.{ try enc.encode(.adc, &.{
.{ .mem = Memory.rip(.qword, 0) }, .{ .mem = Instruction.Memory.rip(.qword, 0) },
.{ .imm = Immediate.u(0x10) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 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 expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10");
try enc.encode(.add, &.{ 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) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 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 expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10");
try enc.encode(.add, &.{ 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) }, .{ .imm = Immediate.s(-0x10) },
}); });
try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10");
try enc.encode(.@"and", &.{ 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) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
@ -1203,7 +1299,7 @@ test "lower MI encoding" {
); );
try enc.encode(.@"and", &.{ 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) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
@ -1213,7 +1309,7 @@ test "lower MI encoding" {
); );
try enc.encode(.@"and", &.{ 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) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
@ -1223,7 +1319,7 @@ test "lower MI encoding" {
); );
try enc.encode(.sub, &.{ 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) }, .{ .imm = Immediate.u(0x10) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
@ -1238,25 +1334,25 @@ test "lower RM encoding" {
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .rax }, .{ .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 expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .rbx }, .{ .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 expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .rax }, .{ .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 expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .rax }, .{ .reg = .rax },
.{ .mem = Memory.sib(.qword, .{ .{ .mem = Instruction.Memory.sib(.qword, .{
.base = .rbp, .base = .rbp,
.scale_index = .{ .scale = 1, .index = .rcx }, .scale_index = .{ .scale = 1, .index = .rcx },
.disp = -8, .disp = -8,
@ -1266,7 +1362,7 @@ test "lower RM encoding" {
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .eax }, .{ .reg = .eax },
.{ .mem = Memory.sib(.dword, .{ .{ .mem = Instruction.Memory.sib(.dword, .{
.base = .rbp, .base = .rbp,
.scale_index = .{ .scale = 4, .index = .rdx }, .scale_index = .{ .scale = 4, .index = .rdx },
.disp = -4, .disp = -4,
@ -1276,7 +1372,7 @@ test "lower RM encoding" {
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .rax }, .{ .reg = .rax },
.{ .mem = Memory.sib(.qword, .{ .{ .mem = Instruction.Memory.sib(.qword, .{
.base = .rbp, .base = .rbp,
.scale_index = .{ .scale = 8, .index = .rcx }, .scale_index = .{ .scale = 8, .index = .rcx },
.disp = -8, .disp = -8,
@ -1286,7 +1382,7 @@ test "lower RM encoding" {
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .r8b }, .{ .reg = .r8b },
.{ .mem = Memory.sib(.byte, .{ .{ .mem = Instruction.Memory.sib(.byte, .{
.base = .rsi, .base = .rsi,
.scale_index = .{ .scale = 1, .index = .rcx }, .scale_index = .{ .scale = 1, .index = .rcx },
.disp = -24, .disp = -24,
@ -1302,7 +1398,7 @@ test "lower RM encoding" {
try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs"); try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -16 }) }, .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -16 }) },
.{ .reg = .fs }, .{ .reg = .fs },
}); });
try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], 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 expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
.{ .reg = .fs }, .{ .reg = .fs },
}); });
try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], 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, &.{ try enc.encode(.movsx, &.{
.{ .reg = .eax }, .{ .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 expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]");
try enc.encode(.movsx, &.{ try enc.encode(.movsx, &.{
.{ .reg = .eax }, .{ .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 expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]");
try enc.encode(.movsx, &.{ try enc.encode(.movsx, &.{
.{ .reg = .ax }, .{ .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]"); 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, &.{ try enc.encode(.lea, &.{
.{ .reg = .rax }, .{ .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 expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]");
try enc.encode(.lea, &.{ try enc.encode(.lea, &.{
.{ .reg = .rax }, .{ .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 expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]");
try enc.encode(.lea, &.{ try enc.encode(.lea, &.{
.{ .reg = .eax }, .{ .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 expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]");
try enc.encode(.lea, &.{ try enc.encode(.lea, &.{
.{ .reg = .eax }, .{ .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 expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]");
try enc.encode(.lea, &.{ try enc.encode(.lea, &.{
.{ .reg = .ax }, .{ .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 expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]");
try enc.encode(.lea, &.{ try enc.encode(.lea, &.{
.{ .reg = .rsi }, .{ .reg = .rsi },
.{ .mem = Memory.sib(.qword, .{ .{ .mem = Instruction.Memory.sib(.qword, .{
.base = .rbp, .base = .rbp,
.scale_index = .{ .scale = 1, .index = .rcx }, .scale_index = .{ .scale = 1, .index = .rcx },
}) }, }) },
@ -1408,31 +1504,31 @@ test "lower RM encoding" {
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .reg = .r11 }, .{ .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 expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000");
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .reg = .r12b }, .{ .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 expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000");
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .reg = .r12b }, .{ .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 expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000");
try enc.encode(.sub, &.{ try enc.encode(.sub, &.{
.{ .reg = .r11 }, .{ .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 expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]");
try enc.encode(.sub, &.{ try enc.encode(.sub, &.{
.{ .reg = .r11 }, .{ .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]"); 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, &.{ try enc.encode(.imul, &.{
.{ .reg = .r11 }, .{ .reg = .r11 },
.{ .mem = Memory.rip(.qword, -16) }, .{ .mem = Instruction.Memory.rip(.qword, -16) },
.{ .imm = Immediate.s(-1024) }, .{ .imm = Immediate.s(-1024) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
@ -1466,7 +1562,7 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{ try enc.encode(.imul, &.{
.{ .reg = .bx }, .{ .reg = .bx },
.{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
.{ .imm = Immediate.s(-1024) }, .{ .imm = Immediate.s(-1024) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
@ -1477,7 +1573,7 @@ test "lower RMI encoding" {
try enc.encode(.imul, &.{ try enc.encode(.imul, &.{
.{ .reg = .bx }, .{ .reg = .bx },
.{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, .{ .mem = Instruction.Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) },
.{ .imm = Immediate.u(1024) }, .{ .imm = Immediate.u(1024) },
}); });
try expectEqualHexStrings( try expectEqualHexStrings(
@ -1497,19 +1593,19 @@ test "lower MR encoding" {
try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx"); try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) }, .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) },
.{ .reg = .r11 }, .{ .reg = .r11 },
}); });
try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11"); try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.rip(.qword, 0x10) }, .{ .mem = Instruction.Memory.rip(.qword, 0x10) },
.{ .reg = .r12 }, .{ .reg = .r12 },
}); });
try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12"); try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.qword, .{ .{ .mem = Instruction.Memory.sib(.qword, .{
.base = .r11, .base = .r11,
.scale_index = .{ .scale = 2, .index = .r12 }, .scale_index = .{ .scale = 2, .index = .r12 },
.disp = 0x10, .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 expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.rip(.word, -0x10) }, .{ .mem = Instruction.Memory.rip(.word, -0x10) },
.{ .reg = .r12w }, .{ .reg = .r12w },
}); });
try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w"); try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.sib(.byte, .{ .{ .mem = Instruction.Memory.sib(.byte, .{
.base = .r11, .base = .r11,
.scale_index = .{ .scale = 2, .index = .r12 }, .scale_index = .{ .scale = 2, .index = .r12 },
.disp = 0x10, .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 expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b");
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .mem = Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) }, .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) },
.{ .reg = .r12b }, .{ .reg = .r12b },
}); });
try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b"); try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b");
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .mem = Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) }, .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) },
.{ .reg = .r12d }, .{ .reg = .r12d },
}); });
try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d"); try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d");
try enc.encode(.add, &.{ try enc.encode(.add, &.{
.{ .mem = Memory.sib(.dword, .{ .base = .gs, .disp = 0x10000000 }) }, .{ .mem = Instruction.Memory.sib(.dword, .{ .base = .gs, .disp = 0x10000000 }) },
.{ .reg = .r12d }, .{ .reg = .r12d },
}); });
try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d"); try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d");
try enc.encode(.sub, &.{ try enc.encode(.sub, &.{
.{ .mem = Memory.sib(.qword, .{ .base = .r11, .disp = 0x10000000 }) }, .{ .mem = Instruction.Memory.sib(.qword, .{ .base = .r11, .disp = 0x10000000 }) },
.{ .reg = .r12 }, .{ .reg = .r12 },
}); });
try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], 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 expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12");
try enc.encode(.call, &.{ 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 expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]");
try enc.encode(.call, &.{ try enc.encode(.call, &.{
.{ .mem = Memory.sib(.qword, .{ .{ .mem = Instruction.Memory.sib(.qword, .{
.base = null, .base = null,
.scale_index = .{ .index = .r11, .scale = 2 }, .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 expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]");
try enc.encode(.call, &.{ try enc.encode(.call, &.{
.{ .mem = Memory.sib(.qword, .{ .{ .mem = Instruction.Memory.sib(.qword, .{
.base = null, .base = null,
.scale_index = .{ .index = .r12, .scale = 2 }, .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 expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]");
try enc.encode(.call, &.{ 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"); 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 expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0");
try enc.encode(.push, &.{ 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 expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
try enc.encode(.push, &.{ 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 expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]");
try enc.encode(.pop, &.{ 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 expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]");
try enc.encode(.pop, &.{ 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]"); 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, &.{ try enc.encode(.mov, &.{
.{ .reg = .rax }, .{ .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 expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .eax }, .{ .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 expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .ax }, .{ .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 expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .reg = .al }, .{ .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 expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.moffs(.cs, 0x10) }, .{ .mem = Instruction.Memory.moffs(.cs, 0x10) },
.{ .reg = .rax }, .{ .reg = .rax },
}); });
try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax"); try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.moffs(.fs, 0x10) }, .{ .mem = Instruction.Memory.moffs(.fs, 0x10) },
.{ .reg = .eax }, .{ .reg = .eax },
}); });
try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax"); try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.moffs(.gs, 0x10) }, .{ .mem = Instruction.Memory.moffs(.gs, 0x10) },
.{ .reg = .ax }, .{ .reg = .ax },
}); });
try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax"); try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax");
try enc.encode(.mov, &.{ try enc.encode(.mov, &.{
.{ .mem = Memory.moffs(.ds, 0x10) }, .{ .mem = Instruction.Memory.moffs(.ds, 0x10) },
.{ .reg = .al }, .{ .reg = .al },
}); });
try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, 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 }, .{ .reg = .al },
}); });
try invalidInstruction(.call, &.{ try invalidInstruction(.call, &.{
.{ .mem = Memory.rip(.dword, 0) }, .{ .mem = Instruction.Memory.rip(.dword, 0) },
}); });
try invalidInstruction(.call, &.{ try invalidInstruction(.call, &.{
.{ .mem = Memory.rip(.word, 0) }, .{ .mem = Instruction.Memory.rip(.word, 0) },
}); });
try invalidInstruction(.call, &.{ try invalidInstruction(.call, &.{
.{ .mem = Memory.rip(.byte, 0) }, .{ .mem = Instruction.Memory.rip(.byte, 0) },
}); });
try invalidInstruction(.mov, &.{ try invalidInstruction(.mov, &.{
.{ .mem = Memory.rip(.word, 0x10) }, .{ .mem = Instruction.Memory.rip(.word, 0x10) },
.{ .reg = .r12 }, .{ .reg = .r12 },
}); });
try invalidInstruction(.lea, &.{ try invalidInstruction(.lea, &.{
@ -1792,7 +1888,7 @@ test "invalid instruction" {
}); });
try invalidInstruction(.lea, &.{ try invalidInstruction(.lea, &.{
.{ .reg = .al }, .{ .reg = .al },
.{ .mem = Memory.rip(.byte, 0) }, .{ .mem = Instruction.Memory.rip(.byte, 0) },
}); });
try invalidInstruction(.pop, &.{ try invalidInstruction(.pop, &.{
.{ .reg = .r12b }, .{ .reg = .r12b },
@ -1817,7 +1913,7 @@ fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand
test "cannot encode" { test "cannot encode" {
try cannotEncode(.@"test", &.{ try cannotEncode(.@"test", &.{
.{ .mem = Memory.sib(.byte, .{ .base = .r12 }) }, .{ .mem = Instruction.Memory.sib(.byte, .{ .base = .r12 }) },
.{ .reg = .ah }, .{ .reg = .ah },
}); });
try cannotEncode(.@"test", &.{ try cannotEncode(.@"test", &.{
@ -2149,8 +2245,8 @@ const Assembler = struct {
return null; return null;
} }
fn parseMemory(as: *Assembler) ParseError!Memory { fn parseMemory(as: *Assembler) ParseError!Instruction.Memory {
const ptr_size: ?Memory.PtrSize = blk: { const ptr_size: ?Instruction.Memory.PtrSize = blk: {
const pos = as.it.pos; const pos = as.it.pos;
const ptr_size = as.parsePtrSize() catch |err| switch (err) { const ptr_size = as.parsePtrSize() catch |err| switch (err) {
error.UnexpectedToken => { error.UnexpectedToken => {
@ -2194,7 +2290,7 @@ const Assembler = struct {
if (res.rip) { if (res.rip) {
if (res.base != null or res.scale_index != null or res.offset != null) if (res.base != null or res.scale_index != null or res.offset != null)
return error.InvalidMemoryOperand; 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.base) |base| {
if (res.rip) if (res.rip)
@ -2202,9 +2298,9 @@ const Assembler = struct {
if (res.offset) |offset| { if (res.offset) |offset| {
if (res.scale_index != null or res.disp != null) if (res.scale_index != null or res.disp != null)
return error.InvalidMemoryOperand; 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, .base = base,
.scale_index = res.scale_index, .scale_index = res.scale_index,
.disp = res.disp orelse 0, .disp = res.disp orelse 0,
@ -2222,12 +2318,12 @@ const Assembler = struct {
const MemoryParseResult = struct { const MemoryParseResult = struct {
rip: bool = false, rip: bool = false,
base: ?Register = null, base: ?Register = null,
scale_index: ?Memory.ScaleIndex = null, scale_index: ?Instruction.Memory.ScaleIndex = null,
disp: ?i32 = null, disp: ?i32 = null,
offset: ?u64 = null, offset: ?u64 = null,
}; };
fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!MemoryParseResult { fn parseMemoryRule(as: *Assembler, rule: anytype) ParseError!Instruction.MemoryParseResult {
var res: MemoryParseResult = .{}; var res: MemoryParseResult = .{};
inline for (rule, 0..) |cond, i| { inline for (rule, 0..) |cond, i| {
if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) { if (@typeInfo(@TypeOf(cond)) != .EnumLiteral) {
@ -2294,7 +2390,7 @@ const Assembler = struct {
return res; return res;
} }
fn parsePtrSize(as: *Assembler) ParseError!Memory.PtrSize { fn parsePtrSize(as: *Assembler) ParseError!Instruction.Memory.PtrSize {
const size = try as.expect(.string); const size = try as.expect(.string);
try as.skip(1, .{.space}); try as.skip(1, .{.space});
const ptr = try as.expect(.string); const ptr = try as.expect(.string);