From 86625c5a752796b64966541187a5843e11add615 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sat, 25 Mar 2023 23:06:54 -0400 Subject: [PATCH 1/9] x86_64: enable mem dst bin ops, and fix uncovered bugs --- src/arch/x86_64/CodeGen.zig | 220 +++++++++++++++++++----------------- 1 file changed, 119 insertions(+), 101 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 08b6a9950fbd..b32d7ef21422 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1628,7 +1628,7 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { const reg_bits = self.regBitSize(ty); const cc: Condition = if (ty.isSignedInt()) cc: { try self.genSetReg(ty, limit_reg, dst_mcv); - try self.genBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); + try self.genShiftBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); try self.genBinOpMir(.xor, ty, limit_mcv, .{ .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1, }); @@ -1681,7 +1681,7 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { const reg_bits = self.regBitSize(ty); const cc: Condition = if (ty.isSignedInt()) cc: { try self.genSetReg(ty, limit_reg, dst_mcv); - try self.genBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); + try self.genShiftBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); try self.genBinOpMir(.xor, ty, limit_mcv, .{ .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1, }); @@ -1735,7 +1735,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { const cc: Condition = if (ty.isSignedInt()) cc: { try self.genSetReg(ty, limit_reg, lhs_mcv); try self.genBinOpMir(.xor, ty, limit_mcv, rhs_mcv); - try self.genBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); + try self.genShiftBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); try self.genBinOpMir(.xor, ty, limit_mcv, .{ .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1, }); @@ -2509,16 +2509,13 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const operand = try self.resolveInst(ty_op.operand); - const dst_mcv: MCValue = blk: { - switch (operand) { - .stack_offset => |off| { - break :blk MCValue{ .stack_offset = off }; - }, - else => return self.fail("TODO implement slice_ptr for {}", .{operand}), - } - }; + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const src_mcv = try self.resolveInst(ty_op.operand); + if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv; + + const dst_mcv = try self.allocRegOrMem(inst, true); + const dst_ty = self.air.typeOfIndex(inst); + try self.setRegOrMem(dst_ty, dst_mcv, src_mcv); break :result dst_mcv; }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); @@ -4402,7 +4399,7 @@ fn genBinOp( else => {}, } - const is_commutative: bool = switch (tag) { + const is_commutative = switch (tag) { .add, .addwrap, .bool_or, @@ -4416,6 +4413,20 @@ fn genBinOp( else => false, }; + const needs_reg_dst = switch (tag) { + .add, + .addwrap, + .sub, + .subwrap, + .mul, + .div_float, + .div_exact, + .div_trunc, + .div_floor, + => lhs_ty.isRuntimeFloat(), + + else => false, + }; const lhs_lock: ?RegisterLock = switch (lhs) { .register => |reg| self.register_manager.lockRegAssumeUnused(reg), @@ -4432,10 +4443,10 @@ fn genBinOp( var flipped: bool = false; const dst_mcv: MCValue = blk: { if (maybe_inst) |inst| { - if (lhs.isRegister() and self.reuseOperand(inst, lhs_air, 0, lhs)) { + if ((!needs_reg_dst or lhs.isRegister()) and self.reuseOperand(inst, lhs_air, 0, lhs)) { break :blk lhs; } - if (is_commutative and rhs.isRegister() and self.reuseOperand(inst, rhs_air, 1, rhs)) { + if (is_commutative and (!needs_reg_dst or rhs.isRegister()) and self.reuseOperand(inst, rhs_air, 1, rhs)) { flipped = true; break :blk rhs; } @@ -4485,33 +4496,37 @@ fn genBinOp( .div_float, .div_exact, - => try self.genBinOpMir(switch (lhs_ty.tag()) { - .f32 => .divss, - .f64 => .divsd, - else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }), - }, lhs_ty, dst_mcv, src_mcv), - .div_trunc, .div_floor, => { try self.genBinOpMir(switch (lhs_ty.tag()) { .f32 => .divss, .f64 => .divsd, - else => return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?) }), + else => return self.fail("TODO implement genBinOp for {s} {}", .{ + @tagName(tag), lhs_ty.fmt(self.bin_file.options.module.?), + }), }, lhs_ty, dst_mcv, src_mcv); - if (Target.x86.featureSetHas(self.target.cpu.features, .sse4_1)) { - const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*)); - const dst_alias = registerAlias(dst_mcv.register, abi_size); - try self.asmRegisterRegisterImmediate(switch (lhs_ty.tag()) { - .f32 => .roundss, - .f64 => .roundsd, - else => unreachable, - }, dst_alias, dst_alias, Immediate.u(switch (tag) { - .div_trunc => 0b1_0_11, - .div_floor => 0b1_0_01, - else => unreachable, - })); - } else return self.fail("TODO implement round without sse4_1", .{}); + switch (tag) { + .div_float, + .div_exact, + => {}, + .div_trunc, + .div_floor, + => if (Target.x86.featureSetHas(self.target.cpu.features, .sse4_1)) { + const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*)); + const dst_alias = registerAlias(dst_mcv.register, abi_size); + try self.asmRegisterRegisterImmediate(switch (lhs_ty.tag()) { + .f32 => .roundss, + .f64 => .roundsd, + else => unreachable, + }, dst_alias, dst_alias, Immediate.u(switch (tag) { + .div_trunc => 0b1_0_11, + .div_floor => 0b1_0_01, + else => unreachable, + })); + } else return self.fail("TODO implement round without sse4_1", .{}), + else => unreachable, + } }, .ptr_add, @@ -4568,7 +4583,13 @@ fn genBinOp( }; const abi_size = @intCast(u32, lhs_ty.abiSize(self.target.*)); - switch (dst_mcv) { + const tmp_reg = switch (dst_mcv) { + .register => |reg| reg, + else => try self.copyToTmpRegister(lhs_ty, dst_mcv), + }; + const tmp_lock = self.register_manager.lockReg(tmp_reg); + defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock); + switch (mat_src_mcv) { .none, .undef, .dead, @@ -4576,57 +4597,44 @@ fn genBinOp( .immediate, .eflags, .register_overflow, - .stack_offset, .ptr_stack_offset, - .memory, - .linker_load, => unreachable, - .register => |dst_reg| switch (mat_src_mcv) { - .none, - .undef, - .dead, - .unreach, - .immediate, - .eflags, - .register_overflow, - .ptr_stack_offset, - => unreachable, - .register => |src_reg| try self.asmCmovccRegisterRegister( - registerAlias(dst_reg, abi_size), - registerAlias(src_reg, abi_size), - cc, - ), - .stack_offset => |off| try self.asmCmovccRegisterMemory( - registerAlias(dst_reg, abi_size), - Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ - .base = .rbp, - .disp = -off, - }), - cc, - ), - .memory, .linker_load => { - const addr_reg = (try self.register_manager.allocReg(null, gp)).to64(); - const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); - defer self.register_manager.unlockReg(addr_reg_lock); + .register => |src_reg| try self.asmCmovccRegisterRegister( + registerAlias(tmp_reg, abi_size), + registerAlias(src_reg, abi_size), + cc, + ), + .stack_offset => |off| try self.asmCmovccRegisterMemory( + registerAlias(tmp_reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ + .base = .rbp, + .disp = -off, + }), + cc, + ), + .memory, .linker_load => { + const addr_reg = (try self.register_manager.allocReg(null, gp)).to64(); + const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); + defer self.register_manager.unlockReg(addr_reg_lock); - try self.loadMemPtrIntoRegister(addr_reg, Type.usize, dst_mcv); + try self.loadMemPtrIntoRegister(addr_reg, Type.usize, mat_src_mcv); - // To get the actual address of the value we want to modify we - // we have to go through the GOT - try self.asmRegisterMemory( - .mov, - addr_reg, - Memory.sib(.qword, .{ .base = addr_reg }), - ); + // To get the actual address of the value we want to modify we + // we have to go through the GOT + try self.asmRegisterMemory( + .mov, + addr_reg, + Memory.sib(.qword, .{ .base = addr_reg }), + ); - try self.asmCmovccRegisterMemory( - registerAlias(dst_reg, abi_size), - Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = addr_reg }), - cc, - ); - }, + try self.asmCmovccRegisterMemory( + registerAlias(tmp_reg, abi_size), + Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = addr_reg }), + cc, + ); }, } + try self.setRegOrMem(lhs_ty, dst_mcv, .{ .register = tmp_reg }); }, .Float => try self.genBinOpMir(switch (lhs_ty.tag()) { .f32 => switch (tag) { @@ -4649,8 +4657,8 @@ fn genBinOp( return dst_mcv; } -fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { - const abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); +fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { + const abi_size = @intCast(u32, ty.abiSize(self.target.*)); switch (dst_mcv) { .none => unreachable, .undef => unreachable, @@ -4667,12 +4675,12 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu const dst_reg_lock = self.register_manager.lockReg(dst_reg); defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); - const reg = try self.copyToTmpRegister(dst_ty, src_mcv); - return self.genBinOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); + const reg = try self.copyToTmpRegister(ty, src_mcv); + return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg }); }, - .register => |src_reg| switch (dst_ty.zigTypeTag()) { + .register => |src_reg| switch (ty.zigTypeTag()) { .Float => { - if (intrinsicsAllowed(self.target.*, dst_ty)) { + if (intrinsicsAllowed(self.target.*, ty)) { return self.asmRegisterRegister(mir_tag, dst_reg.to128(), src_reg.to128()); } @@ -4685,7 +4693,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu ), }, .immediate => |imm| { - switch (self.regBitSize(dst_ty)) { + switch (self.regBitSize(ty)) { 8, 16, 32 => { try self.asmRegisterImmediate( mir_tag, @@ -4704,7 +4712,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu try self.asmRegisterRegister( mir_tag, registerAlias(dst_reg, abi_size), - registerAlias(try self.copyToTmpRegister(dst_ty, src_mcv), abi_size), + registerAlias(try self.copyToTmpRegister(ty, src_mcv), abi_size), ); } }, @@ -4719,8 +4727,8 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu const dst_reg_lock = self.register_manager.lockReg(dst_reg); defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); - const reg = try self.copyToTmpRegister(dst_ty, src_mcv); - return self.genBinOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg }); + const reg = try self.copyToTmpRegister(ty, src_mcv); + return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg }); }, .stack_offset => |off| { if (off > math.maxInt(i32)) { @@ -4754,7 +4762,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu }), registerAlias(src_reg, abi_size)); }, .immediate => |imm| { - switch (self.regBitSize(dst_ty)) { + switch (self.regBitSize(ty)) { 8, 16, 32 => { try self.asmMemoryImmediate( mir_tag, @@ -4785,7 +4793,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu .base = .rbp, .disp = -off, }), - registerAlias(try self.copyToTmpRegister(dst_ty, src_mcv), abi_size), + registerAlias(try self.copyToTmpRegister(ty, src_mcv), abi_size), ); } }, @@ -4793,16 +4801,18 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu } }, .memory, + .linker_load, .stack_offset, .ptr_stack_offset, + .eflags, => { - return self.fail("TODO implement x86 genBinOpMir source memory", .{}); - }, - .linker_load => { - return self.fail("TODO implement x86 genBinOpMir source symbol at index in linker", .{}); - }, - .eflags => { - return self.fail("TODO implement x86 genBinOpMir source eflags", .{}); + assert(abi_size <= 8); + + const tmp_reg = try self.copyToTmpRegister(ty, src_mcv); + const tmp_lock = self.register_manager.lockReg(tmp_reg); + defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock); + + return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = tmp_reg }); }, } }, @@ -7151,7 +7161,15 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; - const result = try self.resolveInst(un_op); + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const src_mcv = try self.resolveInst(un_op); + if (self.reuseOperand(inst, un_op, 0, src_mcv)) break :result src_mcv; + + const dst_mcv = try self.allocRegOrMem(inst, true); + const dst_ty = self.air.typeOfIndex(inst); + try self.setRegOrMem(dst_ty, dst_mcv, src_mcv); + break :result dst_mcv; + }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } From 0e5e001278465e508130eb159327e6aa11895f1c Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 26 Mar 2023 03:27:35 -0400 Subject: [PATCH 2/9] std.MultiArrayList: add set and get to Slice --- lib/std/multi_array_list.zig | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/std/multi_array_list.zig b/lib/std/multi_array_list.zig index f97e42cc89ef..b9624692fc91 100644 --- a/lib/std/multi_array_list.zig +++ b/lib/std/multi_array_list.zig @@ -49,6 +49,20 @@ pub fn MultiArrayList(comptime S: type) type { return casted_ptr[0..self.len]; } + pub fn set(self: Slice, index: usize, elem: S) void { + inline for (fields) |field_info| { + self.items(@field(Field, field_info.name))[index] = @field(elem, field_info.name); + } + } + + pub fn get(self: Slice, index: usize) S { + var elem: S = undefined; + inline for (fields) |field_info| { + @field(elem, field_info.name) = self.items(@field(Field, field_info.name))[index]; + } + return elem; + } + pub fn toMultiArrayList(self: Slice) Self { if (self.ptrs.len == 0) { return .{}; @@ -156,20 +170,12 @@ pub fn MultiArrayList(comptime S: type) type { /// Overwrite one array element with new data. pub fn set(self: *Self, index: usize, elem: S) void { - const slices = self.slice(); - inline for (fields, 0..) |field_info, i| { - slices.items(@intToEnum(Field, i))[index] = @field(elem, field_info.name); - } + return self.slice().set(index, elem); } /// Obtain all the data for one array element. pub fn get(self: Self, index: usize) S { - const slices = self.slice(); - var result: S = undefined; - inline for (fields, 0..) |field_info, i| { - @field(result, field_info.name) = slices.items(@intToEnum(Field, i))[index]; - } - return result; + return self.slice().get(index); } /// Extend the list by 1 element. Allocates more memory as necessary. From abb37a7cb8d4bfae2da6d2cb9e9e6305ef6e52c4 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 26 Mar 2023 21:33:56 -0400 Subject: [PATCH 3/9] x86_64: factor out lowering from emitting --- src/arch/x86_64/CodeGen.zig | 21 +- src/arch/x86_64/Emit.zig | 905 ++++-------------- src/arch/x86_64/Encoding.zig | 294 +++--- src/arch/x86_64/Lower.zig | 465 +++++++++ src/arch/x86_64/Mir.zig | 19 +- src/arch/x86_64/bits.zig | 2 +- src/arch/x86_64/encoder.zig | 919 ++++++++++-------- src/arch/x86_64/encodings.zig | 1669 +++++++++++++++++---------------- 8 files changed, 2167 insertions(+), 2127 deletions(-) create mode 100644 src/arch/x86_64/Lower.zig diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index b32d7ef21422..5ab0e64615a3 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -341,19 +341,22 @@ pub fn generate( defer mir.deinit(bin_file.allocator); var emit = Emit{ - .mir = mir, + .lower = .{ + .allocator = bin_file.allocator, + .mir = mir, + .target = &bin_file.options.target, + .src_loc = src_loc, + }, .bin_file = bin_file, .debug_output = debug_output, - .target = &bin_file.options.target, - .src_loc = src_loc, .code = code, .prev_di_pc = 0, .prev_di_line = module_fn.lbrace_line, .prev_di_column = module_fn.lbrace_column, }; defer emit.deinit(); - emit.lowerMir() catch |err| switch (err) { - error.EmitFail => return Result{ .fail = emit.err_msg.? }, + emit.emitMir() catch |err| switch (err) { + error.LowerFail, error.EmitFail => return Result{ .fail = emit.lower.err_msg.? }, error.InvalidInstruction, error.CannotEncode => |e| { const msg = switch (e) { error.InvalidInstruction => "CodeGen failed to find a viable instruction.", @@ -7070,16 +7073,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void if (reg.to64() == .rax) { // If this is RAX, we can use a direct load. // Otherwise, we need to load the address, then indirectly load the value. - var moffs: Mir.MemoryMoffs = .{ - .seg = @enumToInt(Register.ds), - .msb = undefined, - .lsb = undefined, - }; - moffs.encodeOffset(x); _ = try self.addInst(.{ .tag = .mov_moffs, .ops = .rax_moffs, - .data = .{ .payload = try self.addExtra(moffs) }, + .data = .{ .payload = try self.addExtra(Mir.MemoryMoffs.encode(.ds, x)) }, }); } else { // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index b65d22807aff..052139a4e569 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -1,40 +1,8 @@ -//! This file contains the functionality for lowering x86_64 MIR into -//! machine code +//! This file contains the functionality for emitting x86_64 MIR as machine code -const Emit = @This(); - -const std = @import("std"); -const assert = std.debug.assert; -const bits = @import("bits.zig"); -const abi = @import("abi.zig"); -const encoder = @import("encoder.zig"); -const link = @import("../../link.zig"); -const log = std.log.scoped(.codegen); -const math = std.math; -const mem = std.mem; -const testing = std.testing; - -const Air = @import("../../Air.zig"); -const Allocator = mem.Allocator; -const CodeGen = @import("CodeGen.zig"); -const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; -const Encoder = bits.Encoder; -const ErrorMsg = Module.ErrorMsg; -const Immediate = bits.Immediate; -const Instruction = encoder.Instruction; -const MCValue = @import("CodeGen.zig").MCValue; -const Memory = bits.Memory; -const Mir = @import("Mir.zig"); -const Module = @import("../../Module.zig"); -const Register = bits.Register; -const Type = @import("../../type.zig").Type; - -mir: Mir, +lower: Lower, bin_file: *link.File, debug_output: DebugInfoOutput, -target: *const std.Target, -err_msg: ?*ErrorMsg = null, -src_loc: Module.SrcLoc, code: *std.ArrayList(u8), prev_di_line: u32, @@ -45,166 +13,176 @@ prev_di_pc: usize, code_offset_mapping: std.AutoHashMapUnmanaged(Mir.Inst.Index, usize) = .{}, relocs: std.ArrayListUnmanaged(Reloc) = .{}, -const InnerError = error{ - OutOfMemory, - EmitFail, - InvalidInstruction, - CannotEncode, -}; - -const Reloc = struct { - /// Offset of the instruction. - source: usize, - /// Target of the relocation. - target: Mir.Inst.Index, - /// Offset of the relocation within the instruction. - offset: usize, - /// Length of the instruction. - length: u5, -}; - -pub fn lowerMir(emit: *Emit) InnerError!void { - const mir_tags = emit.mir.instructions.items(.tag); - - for (mir_tags, 0..) |tag, index| { - const inst = @intCast(u32, index); - try emit.code_offset_mapping.putNoClobber(emit.bin_file.allocator, inst, emit.code.items.len); - switch (tag) { - .adc, - .add, - .@"and", - .bsf, - .bsr, - .bswap, - .bt, - .btc, - .btr, - .bts, - .call, - .cbw, - .cwde, - .cdqe, - .cwd, - .cdq, - .cqo, - .cmp, - .cmpxchg, - .div, - .fisttp, - .fld, - .idiv, - .imul, - .int3, - .jmp, - .lea, - .lfence, - .lzcnt, - .mfence, - .mov, - .movbe, - .movzx, - .mul, - .neg, - .nop, - .not, - .@"or", - .pop, - .popcnt, - .push, - .rcl, - .rcr, - .ret, - .rol, - .ror, - .sal, - .sar, - .sbb, - .sfence, - .shl, - .shld, - .shr, - .shrd, - .sub, - .syscall, - .@"test", - .tzcnt, - .ud2, - .xadd, - .xchg, - .xor, - - .addss, - .cmpss, - .divss, - .maxss, - .minss, - .movss, - .mulss, - .roundss, - .subss, - .ucomiss, - .addsd, - .cmpsd, - .divsd, - .maxsd, - .minsd, - .movsd, - .mulsd, - .roundsd, - .subsd, - .ucomisd, - => try emit.mirEncodeGeneric(tag, inst), - - .cmps, - .lods, - .movs, - .scas, - .stos, - => try emit.mirString(tag, inst), - - .cmpxchgb => try emit.mirCmpxchgBytes(inst), - - .jmp_reloc => try emit.mirJmpReloc(inst), - - .call_extern => try emit.mirCallExtern(inst), - - .lea_linker => try emit.mirLeaLinker(inst), - - .mov_moffs => try emit.mirMovMoffs(inst), - - .movsx => try emit.mirMovsx(inst), - .cmovcc => try emit.mirCmovcc(inst), - .setcc => try emit.mirSetcc(inst), - .jcc => try emit.mirJcc(inst), +pub const Error = Lower.Error || error{EmitFail}; + +pub fn emitMir(emit: *Emit) Error!void { + for (0..emit.lower.mir.instructions.len) |i| { + const index = @intCast(Mir.Inst.Index, i); + const inst = emit.lower.mir.instructions.get(index); + + const start_offset = @intCast(u32, emit.code.items.len); + try emit.code_offset_mapping.putNoClobber(emit.lower.allocator, index, start_offset); + for (try emit.lower.lowerMir(inst)) |lower_inst| try lower_inst.encode(emit.code.writer()); + const end_offset = @intCast(u32, emit.code.items.len); + + switch (inst.tag) { + else => {}, + + .jmp_reloc => try emit.relocs.append(emit.lower.allocator, .{ + .source = start_offset, + .target = inst.data.inst, + .offset = end_offset - 4, + .length = 5, + }), + + .call_extern => if (emit.bin_file.cast(link.File.MachO)) |macho_file| { + // Add relocation to the decl. + const atom_index = macho_file.getAtomIndexForSymbol( + .{ .sym_index = inst.data.relocation.atom_index, .file = null }, + ).?; + const target = macho_file.getGlobalByIndex(inst.data.relocation.sym_index); + try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ + .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), + .target = target, + .offset = end_offset - 4, + .addend = 0, + .pcrel = true, + .length = 2, + }); + } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { + // Add relocation to the decl. + const atom_index = coff_file.getAtomIndexForSymbol( + .{ .sym_index = inst.data.relocation.atom_index, .file = null }, + ).?; + const target = coff_file.getGlobalByIndex(inst.data.relocation.sym_index); + try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ + .type = .direct, + .target = target, + .offset = end_offset - 4, + .addend = 0, + .pcrel = true, + .length = 2, + }); + } else return emit.fail("TODO implement {} for {}", .{ inst.tag, emit.bin_file.tag }), + + .lea_linker => if (emit.bin_file.cast(link.File.MachO)) |macho_file| { + const metadata = + emit.lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data; + const reloc_type = switch (inst.ops) { + .got_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), + .direct_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), + else => unreachable, + }; + const atom_index = macho_file.getAtomIndexForSymbol(.{ + .sym_index = metadata.atom_index, + .file = null, + }).?; + try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ + .type = reloc_type, + .target = .{ .sym_index = metadata.sym_index, .file = null }, + .offset = @intCast(u32, end_offset - 4), + .addend = 0, + .pcrel = true, + .length = 2, + }); + } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { + const metadata = + emit.lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data; + const atom_index = coff_file.getAtomIndexForSymbol(.{ + .sym_index = metadata.atom_index, + .file = null, + }).?; + try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ + .type = switch (inst.ops) { + .got_reloc => .got, + .direct_reloc => .direct, + .import_reloc => .import, + else => unreachable, + }, + .target = switch (inst.ops) { + .got_reloc, + .direct_reloc, + => .{ .sym_index = metadata.sym_index, .file = null }, + .import_reloc => coff_file.getGlobalByIndex(metadata.sym_index), + else => unreachable, + }, + .offset = @intCast(u32, end_offset - 4), + .addend = 0, + .pcrel = true, + .length = 2, + }); + } else return emit.fail("TODO implement {} for {}", .{ inst.tag, emit.bin_file.tag }), + + .jcc => try emit.relocs.append(emit.lower.allocator, .{ + .source = start_offset, + .target = inst.data.inst_cc.inst, + .offset = end_offset - 4, + .length = 6, + }), - .dbg_line => try emit.mirDbgLine(inst), - .dbg_prologue_end => try emit.mirDbgPrologueEnd(inst), - .dbg_epilogue_begin => try emit.mirDbgEpilogueBegin(inst), + .dbg_line => { + const dbg_line_column = + emit.lower.mir.extraData(Mir.DbgLineColumn, inst.data.payload).data; + try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column); + }, - .push_regs => try emit.mirPushPopRegisterList(.push, inst), - .pop_regs => try emit.mirPushPopRegisterList(.pop, inst), + .dbg_prologue_end => { + switch (emit.debug_output) { + .dwarf => |dw| { + try dw.setPrologueEnd(); + log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ + emit.prev_di_line, emit.prev_di_column, + }); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } + }, - .dead => {}, + .dbg_epilogue_begin => { + switch (emit.debug_output) { + .dwarf => |dw| { + try dw.setEpilogueBegin(); + log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ + emit.prev_di_line, emit.prev_di_column, + }); + try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); + }, + .plan9 => {}, + .none => {}, + } + }, } } - try emit.fixupRelocs(); } pub fn deinit(emit: *Emit) void { - emit.relocs.deinit(emit.bin_file.allocator); - emit.code_offset_mapping.deinit(emit.bin_file.allocator); + emit.relocs.deinit(emit.lower.allocator); + emit.code_offset_mapping.deinit(emit.lower.allocator); emit.* = undefined; } -fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { - @setCold(true); - assert(emit.err_msg == null); - emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); - return error.EmitFail; +fn fail(emit: *Emit, comptime format: []const u8, args: anytype) Error { + return switch (emit.lower.fail(format, args)) { + error.LowerFail => error.EmitFail, + else => |e| e, + }; } -fn fixupRelocs(emit: *Emit) InnerError!void { +const Reloc = struct { + /// Offset of the instruction. + source: usize, + /// Target of the relocation. + target: Mir.Inst.Index, + /// Offset of the relocation within the instruction. + offset: usize, + /// Length of the instruction. + length: u5, +}; + +fn fixupRelocs(emit: *Emit) Error!void { // TODO this function currently assumes all relocs via JMP/CALL instructions are 32bit in size. // This should be reversed like it is done in aarch64 MIR emit code: start with the smallest // possible resolution, i.e., 8bit, and iteratively converge on the minimum required resolution @@ -217,532 +195,7 @@ fn fixupRelocs(emit: *Emit) InnerError!void { } } -fn encode(emit: *Emit, mnemonic: Instruction.Mnemonic, ops: Instruction.Init) InnerError!void { - const inst = try Instruction.new(mnemonic, ops); - return inst.encode(emit.code.writer()); -} - -fn mirEncodeGeneric(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const mnemonic = inline for (@typeInfo(Instruction.Mnemonic).Enum.fields) |field| { - if (mem.eql(u8, field.name, @tagName(tag))) break @field(Instruction.Mnemonic, field.name); - } else unreachable; - - const ops = emit.mir.instructions.items(.ops)[inst]; - const data = emit.mir.instructions.items(.data)[inst]; - - const prefix: Instruction.Prefix = switch (ops) { - .lock_m_sib, - .lock_m_rip, - .lock_mi_sib_u, - .lock_mi_rip_u, - .lock_mi_sib_s, - .lock_mi_rip_s, - .lock_mr_sib, - .lock_mr_rip, - .lock_moffs_rax, - => .lock, - else => .none, - }; - - var op1: Instruction.Operand = .none; - var op2: Instruction.Operand = .none; - var op3: Instruction.Operand = .none; - var op4: Instruction.Operand = .none; - - switch (ops) { - .none => {}, - .i_s => op1 = .{ .imm = Immediate.s(@bitCast(i32, data.i)) }, - .i_u => op1 = .{ .imm = Immediate.u(data.i) }, - .r => op1 = .{ .reg = data.r }, - .rr => { - op1 = .{ .reg = data.rr.r1 }; - op2 = .{ .reg = data.rr.r2 }; - }, - .rrr => { - op1 = .{ .reg = data.rrr.r1 }; - op2 = .{ .reg = data.rrr.r2 }; - op3 = .{ .reg = data.rrr.r3 }; - }, - .ri_s, .ri_u => { - const imm = switch (ops) { - .ri_s => Immediate.s(@bitCast(i32, data.ri.i)), - .ri_u => Immediate.u(data.ri.i), - else => unreachable, - }; - op1 = .{ .reg = data.ri.r }; - op2 = .{ .imm = imm }; - }, - .ri64 => { - const imm64 = emit.mir.extraData(Mir.Imm64, data.rx.payload).data; - op1 = .{ .reg = data.rx.r }; - op2 = .{ .imm = Immediate.u(Mir.Imm64.decode(imm64)) }; - }, - .rri_s, .rri_u => { - const imm = switch (ops) { - .rri_s => Immediate.s(@bitCast(i32, data.rri.i)), - .rri_u => Immediate.u(data.rri.i), - else => unreachable, - }; - op1 = .{ .reg = data.rri.r1 }; - op2 = .{ .reg = data.rri.r2 }; - op3 = .{ .imm = imm }; - }, - .m_sib, .lock_m_sib => { - const msib = emit.mir.extraData(Mir.MemorySib, data.payload).data; - op1 = .{ .mem = Mir.MemorySib.decode(msib) }; - }, - .m_rip, .lock_m_rip => { - const mrip = emit.mir.extraData(Mir.MemoryRip, data.payload).data; - op1 = .{ .mem = Mir.MemoryRip.decode(mrip) }; - }, - .mi_sib_s, .mi_sib_u, .lock_mi_sib_s, .lock_mi_sib_u => { - const msib = emit.mir.extraData(Mir.MemorySib, data.ix.payload).data; - const imm = switch (ops) { - .mi_sib_s, .lock_mi_sib_s => Immediate.s(@bitCast(i32, data.ix.i)), - .mi_sib_u, .lock_mi_sib_u => Immediate.u(data.ix.i), - else => unreachable, - }; - op1 = .{ .mem = Mir.MemorySib.decode(msib) }; - op2 = .{ .imm = imm }; - }, - .mi_rip_u, .mi_rip_s, .lock_mi_rip_u, .lock_mi_rip_s => { - const mrip = emit.mir.extraData(Mir.MemoryRip, data.ix.payload).data; - const imm = switch (ops) { - .mi_rip_s, .lock_mi_rip_s => Immediate.s(@bitCast(i32, data.ix.i)), - .mi_rip_u, .lock_mi_rip_u => Immediate.u(data.ix.i), - else => unreachable, - }; - op1 = .{ .mem = Mir.MemoryRip.decode(mrip) }; - op2 = .{ .imm = imm }; - }, - .rm_sib, .mr_sib, .lock_mr_sib => { - const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data; - const op_r = .{ .reg = data.rx.r }; - const op_m = .{ .mem = Mir.MemorySib.decode(msib) }; - switch (ops) { - .rm_sib => { - op1 = op_r; - op2 = op_m; - }, - .mr_sib, .lock_mr_sib => { - op1 = op_m; - op2 = op_r; - }, - else => unreachable, - } - }, - .rm_rip, .mr_rip, .lock_mr_rip => { - const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data; - const op_r = .{ .reg = data.rx.r }; - const op_m = .{ .mem = Mir.MemoryRip.decode(mrip) }; - switch (ops) { - .rm_rip => { - op1 = op_r; - op2 = op_m; - }, - .mr_rip, .lock_mr_rip => { - op1 = op_m; - op2 = op_r; - }, - else => unreachable, - } - }, - .mrr_sib => { - const msib = emit.mir.extraData(Mir.MemorySib, data.rrx.payload).data; - op1 = .{ .mem = Mir.MemorySib.decode(msib) }; - op2 = .{ .reg = data.rrx.r1 }; - op2 = .{ .reg = data.rrx.r2 }; - }, - .mrr_rip => { - const mrip = emit.mir.extraData(Mir.MemoryRip, data.rrx.payload).data; - op1 = .{ .mem = Mir.MemoryRip.decode(mrip) }; - op2 = .{ .reg = data.rrx.r1 }; - op2 = .{ .reg = data.rrx.r2 }; - }, - .mri_sib => { - const msib = emit.mir.extraData(Mir.MemorySib, data.rix.payload).data; - op1 = .{ .mem = Mir.MemorySib.decode(msib) }; - op2 = .{ .reg = data.rix.r }; - op3 = .{ .imm = Immediate.u(data.rix.i) }; - }, - .mri_rip => { - const mrip = emit.mir.extraData(Mir.MemoryRip, data.rix.payload).data; - op1 = .{ .mem = Mir.MemoryRip.decode(mrip) }; - op2 = .{ .reg = data.rix.r }; - op3 = .{ .imm = Immediate.u(data.rix.i) }; - }, - else => return emit.fail("TODO handle generic encoding: {s}, {s}", .{ - @tagName(mnemonic), - @tagName(ops), - }), - } - - return emit.encode(mnemonic, .{ - .prefix = prefix, - .op1 = op1, - .op2 = op2, - .op3 = op3, - .op4 = op4, - }); -} - -fn mirString(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst]; - switch (ops) { - .string => { - const data = emit.mir.instructions.items(.data)[inst].string; - const mnemonic = switch (tag) { - inline .cmps, .lods, .movs, .scas, .stos => |comptime_tag| switch (data.width) { - inline else => |comptime_width| @field( - Instruction.Mnemonic, - @tagName(comptime_tag) ++ @tagName(comptime_width), - ), - }, - else => unreachable, - }; - return emit.encode(mnemonic, .{ .prefix = switch (data.repeat) { - inline else => |comptime_repeat| @field(Instruction.Prefix, @tagName(comptime_repeat)), - } }); - }, - else => unreachable, - } -} - -fn mirCmpxchgBytes(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst]; - const data = emit.mir.instructions.items(.data)[inst]; - - var op1: Instruction.Operand = .none; - switch (ops) { - .m_sib, .lock_m_sib => { - const sib = emit.mir.extraData(Mir.MemorySib, data.payload).data; - op1 = .{ .mem = Mir.MemorySib.decode(sib) }; - }, - .m_rip, .lock_m_rip => { - const rip = emit.mir.extraData(Mir.MemoryRip, data.payload).data; - op1 = .{ .mem = Mir.MemoryRip.decode(rip) }; - }, - else => unreachable, - } - - const mnemonic: Instruction.Mnemonic = switch (op1.mem.bitSize()) { - 64 => .cmpxchg8b, - 128 => .cmpxchg16b, - else => unreachable, - }; - - return emit.encode(mnemonic, .{ - .prefix = switch (ops) { - .m_sib, .m_rip => .none, - .lock_m_sib, .lock_m_rip => .lock, - else => unreachable, - }, - .op1 = op1, - }); -} - -fn mirMovMoffs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst]; - const payload = emit.mir.instructions.items(.data)[inst].payload; - const moffs = emit.mir.extraData(Mir.MemoryMoffs, payload).data; - const seg = @intToEnum(Register, moffs.seg); - const offset = moffs.decodeOffset(); - switch (ops) { - .rax_moffs => { - try emit.encode(.mov, .{ - .op1 = .{ .reg = .rax }, - .op2 = .{ .mem = Memory.moffs(seg, offset) }, - }); - }, - .moffs_rax, .lock_moffs_rax => { - try emit.encode(.mov, .{ - .prefix = switch (ops) { - .moffs_rax => .none, - .lock_moffs_rax => .lock, - else => unreachable, - }, - .op1 = .{ .mem = Memory.moffs(seg, offset) }, - .op2 = .{ .reg = .rax }, - }); - }, - else => unreachable, - } -} - -fn mirMovsx(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst]; - const data = emit.mir.instructions.items(.data)[inst]; - - var op1: Instruction.Operand = .none; - var op2: Instruction.Operand = .none; - switch (ops) { - .rr => { - op1 = .{ .reg = data.rr.r1 }; - op2 = .{ .reg = data.rr.r2 }; - }, - .rm_sib => { - const msib = emit.mir.extraData(Mir.MemorySib, data.rx.payload).data; - op1 = .{ .reg = data.rx.r }; - op2 = .{ .mem = Mir.MemorySib.decode(msib) }; - }, - .rm_rip => { - const mrip = emit.mir.extraData(Mir.MemoryRip, data.rx.payload).data; - op1 = .{ .reg = data.rx.r }; - op2 = .{ .mem = Mir.MemoryRip.decode(mrip) }; - }, - else => unreachable, // TODO - } - - const mnemonic: Instruction.Mnemonic = switch (op1.bitSize()) { - 32, 64 => if (op2.bitSize() == 32) .movsxd else .movsx, - else => .movsx, - }; - - return emit.encode(mnemonic, .{ - .op1 = op1, - .op2 = op2, - }); -} - -fn mnemonicFromConditionCode(comptime basename: []const u8, cc: bits.Condition) Instruction.Mnemonic { - return switch (cc) { - inline else => |comptime_cc| @field(Instruction.Mnemonic, basename ++ @tagName(comptime_cc)), - }; -} - -fn mirCmovcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst]; - switch (ops) { - .rr_cc => { - const data = emit.mir.instructions.items(.data)[inst].rr_cc; - const mnemonic = mnemonicFromConditionCode("cmov", data.cc); - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = data.r1 }, - .op2 = .{ .reg = data.r2 }, - }); - }, - .rm_sib_cc => { - const data = emit.mir.instructions.items(.data)[inst].rx_cc; - const extra = emit.mir.extraData(Mir.MemorySib, data.payload).data; - const mnemonic = mnemonicFromConditionCode("cmov", data.cc); - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = data.r }, - .op2 = .{ .mem = Mir.MemorySib.decode(extra) }, - }); - }, - .rm_rip_cc => { - const data = emit.mir.instructions.items(.data)[inst].rx_cc; - const extra = emit.mir.extraData(Mir.MemoryRip, data.payload).data; - const mnemonic = mnemonicFromConditionCode("cmov", data.cc); - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = data.r }, - .op2 = .{ .mem = Mir.MemoryRip.decode(extra) }, - }); - }, - else => unreachable, - } -} - -fn mirSetcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst]; - switch (ops) { - .r_cc => { - const data = emit.mir.instructions.items(.data)[inst].r_cc; - const mnemonic = mnemonicFromConditionCode("set", data.cc); - return emit.encode(mnemonic, .{ - .op1 = .{ .reg = data.r }, - }); - }, - .m_sib_cc => { - const data = emit.mir.instructions.items(.data)[inst].x_cc; - const extra = emit.mir.extraData(Mir.MemorySib, data.payload).data; - const mnemonic = mnemonicFromConditionCode("set", data.cc); - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Mir.MemorySib.decode(extra) }, - }); - }, - .m_rip_cc => { - const data = emit.mir.instructions.items(.data)[inst].x_cc; - const extra = emit.mir.extraData(Mir.MemoryRip, data.payload).data; - const mnemonic = mnemonicFromConditionCode("set", data.cc); - return emit.encode(mnemonic, .{ - .op1 = .{ .mem = Mir.MemoryRip.decode(extra) }, - }); - }, - else => unreachable, // TODO - } -} - -fn mirJcc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst]; - switch (ops) { - .inst_cc => { - const data = emit.mir.instructions.items(.data)[inst].inst_cc; - const mnemonic = mnemonicFromConditionCode("j", data.cc); - const source = emit.code.items.len; - try emit.encode(mnemonic, .{ - .op1 = .{ .imm = Immediate.s(0) }, - }); - try emit.relocs.append(emit.bin_file.allocator, .{ - .source = source, - .target = data.inst, - .offset = emit.code.items.len - 4, - .length = 6, - }); - }, - else => unreachable, // TODO - } -} - -fn mirJmpReloc(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const target = emit.mir.instructions.items(.data)[inst].inst; - const source = emit.code.items.len; - try emit.encode(.jmp, .{ - .op1 = .{ .imm = Immediate.s(0) }, - }); - try emit.relocs.append(emit.bin_file.allocator, .{ - .source = source, - .target = target, - .offset = emit.code.items.len - 4, - .length = 5, - }); -} - -fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const relocation = emit.mir.instructions.items(.data)[inst].relocation; - - const offset = blk: { - try emit.encode(.call, .{ - .op1 = .{ .imm = Immediate.s(0) }, - }); - break :blk @intCast(u32, emit.code.items.len) - 4; - }; - - if (emit.bin_file.cast(link.File.MachO)) |macho_file| { - // Add relocation to the decl. - const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; - const target = macho_file.getGlobalByIndex(relocation.sym_index); - try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ - .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), - .target = target, - .offset = offset, - .addend = 0, - .pcrel = true, - .length = 2, - }); - } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { - // Add relocation to the decl. - const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; - const target = coff_file.getGlobalByIndex(relocation.sym_index); - try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ - .type = .direct, - .target = target, - .offset = offset, - .addend = 0, - .pcrel = true, - .length = 2, - }); - } else { - return emit.fail("TODO implement call_extern for linking backends different than MachO and COFF", .{}); - } -} - -fn mirPushPopRegisterList(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { - const payload = emit.mir.instructions.items(.data)[inst].payload; - const save_reg_list = emit.mir.extraData(Mir.SaveRegisterList, payload).data; - const base = @intToEnum(Register, save_reg_list.base_reg); - var disp: i32 = -@intCast(i32, save_reg_list.stack_end); - const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list); - const callee_preserved_regs = abi.getCalleePreservedRegs(emit.target.*); - for (callee_preserved_regs) |reg| { - if (reg_list.isSet(callee_preserved_regs, reg)) { - const op1: Instruction.Operand = .{ .mem = Memory.sib(.qword, .{ - .base = base, - .disp = disp, - }) }; - const op2: Instruction.Operand = .{ .reg = reg }; - switch (tag) { - .push => try emit.encode(.mov, .{ - .op1 = op1, - .op2 = op2, - }), - .pop => try emit.encode(.mov, .{ - .op1 = op2, - .op2 = op1, - }), - else => unreachable, - } - disp += 8; - } - } -} - -fn mirLeaLinker(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const ops = emit.mir.instructions.items(.ops)[inst]; - const payload = emit.mir.instructions.items(.data)[inst].payload; - const metadata = emit.mir.extraData(Mir.LeaRegisterReloc, payload).data; - const reg = @intToEnum(Register, metadata.reg); - - try emit.encode(.lea, .{ - .op1 = .{ .reg = reg }, - .op2 = .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) }, - }); - - const end_offset = emit.code.items.len; - - if (emit.bin_file.cast(link.File.MachO)) |macho_file| { - const reloc_type = switch (ops) { - .got_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), - .direct_reloc => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), - else => unreachable, - }; - const atom_index = macho_file.getAtomIndexForSymbol(.{ - .sym_index = metadata.atom_index, - .file = null, - }).?; - try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ - .type = reloc_type, - .target = .{ .sym_index = metadata.sym_index, .file = null }, - .offset = @intCast(u32, end_offset - 4), - .addend = 0, - .pcrel = true, - .length = 2, - }); - } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { - const atom_index = coff_file.getAtomIndexForSymbol(.{ - .sym_index = metadata.atom_index, - .file = null, - }).?; - try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ - .type = switch (ops) { - .got_reloc => .got, - .direct_reloc => .direct, - .import_reloc => .import, - else => unreachable, - }, - .target = switch (ops) { - .got_reloc, .direct_reloc => .{ .sym_index = metadata.sym_index, .file = null }, - .import_reloc => coff_file.getGlobalByIndex(metadata.sym_index), - else => unreachable, - }, - .offset = @intCast(u32, end_offset - 4), - .addend = 0, - .pcrel = true, - .length = 2, - }); - } else { - return emit.fail("TODO implement lea reg, [rip + reloc] for linking backends different than MachO", .{}); - } -} - -fn mirDbgLine(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - const payload = emit.mir.instructions.items(.data)[inst].payload; - const dbg_line_column = emit.mir.extraData(Mir.DbgLineColumn, payload).data; - log.debug("mirDbgLine", .{}); - try emit.dbgAdvancePCAndLine(dbg_line_column.line, dbg_line_column.column); -} - -fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { +fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void { const delta_line = @intCast(i32, line) - @intCast(i32, emit.prev_di_line); const delta_pc: usize = emit.code.items.len - emit.prev_di_pc; log.debug(" (advance pc={d} and line={d})", .{ delta_line, delta_pc }); @@ -756,7 +209,7 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { .plan9 => |dbg_out| { if (delta_pc <= 0) return; // only do this when the pc changes // we have already checked the target in the linker to make sure it is compatable - const quant = @import("../../link/Plan9/aout.zig").getPCQuant(emit.target.cpu.arch) catch unreachable; + const quant = @import("../../link/Plan9/aout.zig").getPCQuant(emit.lower.target.cpu.arch) catch unreachable; // increasing the line number try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line); @@ -792,34 +245,12 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) InnerError!void { } } -fn mirDbgPrologueEnd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - _ = inst; - switch (emit.debug_output) { - .dwarf => |dw| { - try dw.setPrologueEnd(); - log.debug("mirDbgPrologueEnd (line={d}, col={d})", .{ - emit.prev_di_line, - emit.prev_di_column, - }); - try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); - }, - .plan9 => {}, - .none => {}, - } -} +const link = @import("../../link.zig"); +const log = std.log.scoped(.emit); +const mem = std.mem; +const std = @import("std"); -fn mirDbgEpilogueBegin(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { - _ = inst; - switch (emit.debug_output) { - .dwarf => |dw| { - try dw.setEpilogueBegin(); - log.debug("mirDbgEpilogueBegin (line={d}, col={d})", .{ - emit.prev_di_line, - emit.prev_di_column, - }); - try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column); - }, - .plan9 => {}, - .none => {}, - } -} +const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; +const Emit = @This(); +const Lower = @import("Lower.zig"); +const Mir = @import("Mir.zig"); diff --git a/src/arch/x86_64/Encoding.zig b/src/arch/x86_64/Encoding.zig index de669a9f8d5b..1361570405e6 100644 --- a/src/arch/x86_64/Encoding.zig +++ b/src/arch/x86_64/Encoding.zig @@ -7,30 +7,32 @@ const math = std.math; const bits = @import("bits.zig"); const encoder = @import("encoder.zig"); const Instruction = encoder.Instruction; +const Operand = Instruction.Operand; +const Prefix = Instruction.Prefix; const Register = bits.Register; const Rex = encoder.Rex; const LegacyPrefixes = encoder.LegacyPrefixes; -const table = @import("encodings.zig").table; - mnemonic: Mnemonic, -op_en: OpEn, -op1: Op, -op2: Op, -op3: Op, -op4: Op, -opc_len: u3, -opc: [7]u8, -modrm_ext: u3, -mode: Mode, - -pub fn findByMnemonic(mnemonic: Mnemonic, args: Instruction.Init) !?Encoding { - const input_op1 = Op.fromOperand(args.op1); - const input_op2 = Op.fromOperand(args.op2); - const input_op3 = Op.fromOperand(args.op3); - const input_op4 = Op.fromOperand(args.op4); - - const ops = &[_]Instruction.Operand{ args.op1, args.op2, args.op3, args.op4 }; +data: Data, + +const Data = struct { + op_en: OpEn, + ops: [4]Op, + opc_len: u3, + opc: [7]u8, + modrm_ext: u3, + mode: Mode, +}; + +pub fn findByMnemonic( + prefix: Instruction.Prefix, + mnemonic: Mnemonic, + ops: []const Instruction.Operand, +) !?Encoding { + var input_ops = [1]Op{.none} ** 4; + for (input_ops[0..ops.len], ops) |*input_op, op| input_op.* = Op.fromOperand(op); + const rex_required = for (ops) |op| switch (op) { .reg => |r| switch (r) { .spl, .bpl, .sil, .dil => break true, @@ -60,88 +62,29 @@ pub fn findByMnemonic(mnemonic: Mnemonic, args: Instruction.Init) !?Encoding { if ((rex_required or rex_extended) and rex_invalid) return error.CannotEncode; - // TODO work out what is the maximum number of variants we can actually find in one swoop. - var candidates: [10]Encoding = undefined; - var count: usize = 0; - for (table) |entry| { - var enc = Encoding{ - .mnemonic = entry[0], - .op_en = entry[1], - .op1 = entry[2], - .op2 = entry[3], - .op3 = entry[4], - .op4 = entry[5], - .opc_len = @intCast(u3, entry[6].len), - .opc = undefined, - .modrm_ext = entry[7], - .mode = entry[8], - }; - std.mem.copy(u8, &enc.opc, entry[6]); - if (enc.mnemonic == mnemonic and - input_op1.isSubset(enc.op1, enc.mode) and - input_op2.isSubset(enc.op2, enc.mode) and - input_op3.isSubset(enc.op3, enc.mode) and - input_op4.isSubset(enc.op4, enc.mode)) - { - if (rex_required) { - switch (enc.mode) { - .rex, .long => { - candidates[count] = enc; - count += 1; - }, - else => {}, - } - } else { - if (enc.mode != .rex) { - candidates[count] = enc; - count += 1; - } - } + var shortest_enc: ?Encoding = null; + var shortest_len: ?usize = null; + next: for (mnemonic_to_encodings_map[@enumToInt(mnemonic)]) |data| { + switch (data.mode) { + .rex => if (!rex_required) continue, + .long => {}, + else => if (rex_required) continue, } + for (input_ops, data.ops) |input_op, data_op| + if (!input_op.isSubset(data_op, data.mode)) continue :next; + + const enc = Encoding{ .mnemonic = mnemonic, .data = data }; + if (shortest_enc) |previous_shortest_enc| { + const len = estimateInstructionLength(prefix, enc, ops); + const previous_shortest_len = shortest_len orelse + estimateInstructionLength(prefix, previous_shortest_enc, ops); + if (len < previous_shortest_len) { + shortest_enc = enc; + shortest_len = len; + } else shortest_len = previous_shortest_len; + } else shortest_enc = enc; } - - if (count == 0) return null; - if (count == 1) return candidates[0]; - - const EncodingLength = struct { - fn estimate(encoding: Encoding, params: Instruction.Init) usize { - var inst = Instruction{ - .op1 = params.op1, - .op2 = params.op2, - .op3 = params.op3, - .op4 = params.op4, - .prefix = params.prefix, - .encoding = encoding, - }; - var cwriter = std.io.countingWriter(std.io.null_writer); - inst.encode(cwriter.writer()) catch unreachable; // Not allowed to fail here unless OOM. - return @intCast(usize, cwriter.bytes_written); - } - }; - - var shortest_encoding: ?struct { - index: usize, - len: usize, - } = null; - var i: usize = 0; - while (i < count) : (i += 1) { - const candidate = candidates[i]; - switch (candidate.mode) { - .long, .rex => if (rex_invalid) return error.CannotEncode, - else => {}, - } - - const len = EncodingLength.estimate(candidate, args); - const current = shortest_encoding orelse { - shortest_encoding = .{ .index = i, .len = len }; - continue; - }; - if (len < current.len) { - shortest_encoding = .{ .index = i, .len = len }; - } - } - - return candidates[shortest_encoding.?.index]; + return shortest_enc; } /// Returns first matching encoding by opcode. @@ -149,57 +92,45 @@ pub fn findByOpcode(opc: []const u8, prefixes: struct { legacy: LegacyPrefixes, rex: Rex, }, modrm_ext: ?u3) ?Encoding { - for (table) |entry| { - const enc = Encoding{ - .mnemonic = entry[0], - .op_en = entry[1], - .op1 = entry[2], - .op2 = entry[3], - .op3 = entry[4], - .op4 = entry[5], - .opc_len = entry[6], - .opc = .{ entry[7], entry[8], entry[9] }, - .modrm_ext = entry[10], - .mode = entry[11], - }; - const match = match: { - if (modrm_ext) |ext| { - break :match ext == enc.modrm_ext and std.mem.eql(u8, enc.opcode(), opc); + for (mnemonic_to_encodings_map, 0..) |encs, mnemonic_int| for (encs) |data| { + const enc = Encoding{ .mnemonic = @intToEnum(Mnemonic, mnemonic_int), .data = data }; + if (modrm_ext) |ext| if (ext != data.modrm_ext) continue; + if (!std.mem.eql(u8, opc, enc.opcode())) continue; + if (prefixes.rex.w) { + switch (data.mode) { + .short, .fpu, .sse, .sse2, .sse4_1, .none => continue, + .long, .rex => {}, } - break :match std.mem.eql(u8, enc.opcode(), opc); - }; - if (match) { - if (prefixes.rex.w) { - switch (enc.mode) { - .fpu, .sse, .sse2, .sse4_1, .none => {}, - .long, .rex => return enc, - } - } else if (prefixes.rex.present and !prefixes.rex.isSet()) { - if (enc.mode == .rex) return enc; - } else if (prefixes.legacy.prefix_66) { - switch (enc.operandBitSize()) { - 16 => return enc, + } else if (prefixes.rex.present and !prefixes.rex.isSet()) { + switch (data.mode) { + .rex => {}, + else => continue, + } + } else if (prefixes.legacy.prefix_66) { + switch (enc.operandBitSize()) { + 16 => {}, + else => continue, + } + } else { + switch (data.mode) { + .none => switch (enc.operandBitSize()) { + 16 => continue, else => {}, - } - } else { - if (enc.mode == .none) { - switch (enc.operandBitSize()) { - 16 => {}, - else => return enc, - } - } + }, + else => continue, } } - } + return enc; + }; return null; } pub fn opcode(encoding: *const Encoding) []const u8 { - return encoding.opc[0..encoding.opc_len]; + return encoding.data.opc[0..encoding.data.opc_len]; } pub fn mandatoryPrefix(encoding: *const Encoding) ?u8 { - const prefix = encoding.opc[0]; + const prefix = encoding.data.opc[0]; return switch (prefix) { 0x66, 0xf2, 0xf3 => prefix, else => null, @@ -207,27 +138,27 @@ pub fn mandatoryPrefix(encoding: *const Encoding) ?u8 { } pub fn modRmExt(encoding: Encoding) u3 { - return switch (encoding.op_en) { - .m, .mi, .m1, .mc => encoding.modrm_ext, + return switch (encoding.data.op_en) { + .m, .mi, .m1, .mc => encoding.data.modrm_ext, else => unreachable, }; } pub fn operandBitSize(encoding: Encoding) u64 { - switch (encoding.mode) { + switch (encoding.data.mode) { .short => return 16, .long => return 64, else => {}, } - const bit_size: u64 = switch (encoding.op_en) { - .np => switch (encoding.op1) { + const bit_size: u64 = switch (encoding.data.op_en) { + .np => switch (encoding.data.ops[0]) { .o16 => 16, .o32 => 32, .o64 => 64, else => 32, }, - .td => encoding.op2.bitSize(), - else => encoding.op1.bitSize(), + .td => encoding.data.ops[1].bitSize(), + else => encoding.data.ops[0].bitSize(), }; return bit_size; } @@ -240,7 +171,7 @@ pub fn format( ) !void { _ = options; _ = fmt; - switch (encoding.mode) { + switch (encoding.data.mode) { .long => try writer.writeAll("REX.W + "), else => {}, } @@ -249,10 +180,10 @@ pub fn format( try writer.print("{x:0>2} ", .{byte}); } - switch (encoding.op_en) { + switch (encoding.data.op_en) { .np, .fd, .td, .i, .zi, .d => {}, .o, .oi => { - const tag = switch (encoding.op1) { + const tag = switch (encoding.data.ops[0]) { .r8 => "rb", .r16 => "rw", .r32 => "rd", @@ -265,12 +196,12 @@ pub fn format( .mr, .rm, .rmi, .mri, .mrc => try writer.writeAll("/r "), } - switch (encoding.op_en) { + switch (encoding.data.op_en) { .i, .d, .zi, .oi, .mi, .rmi, .mri => { - const op = switch (encoding.op_en) { - .i, .d => encoding.op1, - .zi, .oi, .mi => encoding.op2, - .rmi, .mri => encoding.op3, + const op = switch (encoding.data.op_en) { + .i, .d => encoding.data.ops[0], + .zi, .oi, .mi => encoding.data.ops[1], + .rmi, .mri => encoding.data.ops[2], else => unreachable, }; const tag = switch (op) { @@ -290,13 +221,12 @@ pub fn format( try writer.print("{s} ", .{@tagName(encoding.mnemonic)}); - const ops = &[_]Op{ encoding.op1, encoding.op2, encoding.op3, encoding.op4 }; - for (ops) |op| switch (op) { + for (encoding.data.ops) |op| switch (op) { .none, .o16, .o32, .o64 => break, else => try writer.print("{s} ", .{@tagName(op)}), }; - const op_en = switch (encoding.op_en) { + const op_en = switch (encoding.data.op_en) { .zi => .i, else => |op_en| op_en, }; @@ -604,3 +534,53 @@ pub const Mode = enum { sse2, sse4_1, }; + +fn estimateInstructionLength(prefix: Prefix, encoding: Encoding, ops: []const Operand) usize { + var inst = Instruction{ + .prefix = prefix, + .encoding = encoding, + .ops = [1]Operand{.none} ** 4, + }; + std.mem.copy(Operand, &inst.ops, ops); + + var cwriter = std.io.countingWriter(std.io.null_writer); + inst.encode(cwriter.writer()) catch unreachable; // Not allowed to fail here unless OOM. + return @intCast(usize, cwriter.bytes_written); +} + +const mnemonic_to_encodings_map = init: { + @setEvalBranchQuota(100_000); + const encodings = @import("encodings.zig"); + var entries = encodings.table; + std.sort.sort(encodings.Entry, &entries, {}, struct { + fn lessThan(_: void, lhs: encodings.Entry, rhs: encodings.Entry) bool { + return @enumToInt(lhs[0]) < @enumToInt(rhs[0]); + } + }.lessThan); + var data_storage: [entries.len]Data = undefined; + var mnemonic_map: [@typeInfo(Mnemonic).Enum.fields.len][]const Data = undefined; + var mnemonic_int = 0; + var mnemonic_start = 0; + for (&data_storage, entries, 0..) |*data, entry, data_index| { + data.* = .{ + .op_en = entry[1], + .ops = undefined, + .opc_len = entry[3].len, + .opc = undefined, + .modrm_ext = entry[4], + .mode = entry[5], + }; + std.mem.copy(Op, &data.ops, entry[2]); + std.mem.copy(u8, &data.opc, entry[3]); + + while (mnemonic_int < @enumToInt(entry[0])) : (mnemonic_int += 1) { + mnemonic_map[mnemonic_int] = data_storage[mnemonic_start..data_index]; + mnemonic_start = data_index; + } + } + while (mnemonic_int < mnemonic_map.len) : (mnemonic_int += 1) { + mnemonic_map[mnemonic_int] = data_storage[mnemonic_start..]; + mnemonic_start = data_storage.len; + } + break :init mnemonic_map; +}; diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig new file mode 100644 index 000000000000..f4d9db57e826 --- /dev/null +++ b/src/arch/x86_64/Lower.zig @@ -0,0 +1,465 @@ +//! This file contains the functionality for lowering x86_64 MIR to Instructions + +allocator: Allocator, +mir: Mir, +target: *const std.Target, +err_msg: ?*ErrorMsg = null, +src_loc: Module.SrcLoc, +result: [ + std.mem.max(usize, &.{ + abi.Win64.callee_preserved_regs.len, + abi.SysV.callee_preserved_regs.len, + }) +]Instruction = undefined, +result_len: usize = undefined, + +pub const Error = error{ + OutOfMemory, + LowerFail, + InvalidInstruction, + CannotEncode, +}; + +/// The returned slice is overwritten by the next call to lowerMir. +pub fn lowerMir(lower: *Lower, inst: Mir.Inst) Error![]const Instruction { + lower.result = undefined; + errdefer lower.result = undefined; + lower.result_len = 0; + defer lower.result_len = undefined; + + switch (inst.tag) { + .adc, + .add, + .@"and", + .bsf, + .bsr, + .bswap, + .bt, + .btc, + .btr, + .bts, + .call, + .cbw, + .cwde, + .cdqe, + .cwd, + .cdq, + .cqo, + .cmp, + .cmpxchg, + .div, + .fisttp, + .fld, + .idiv, + .imul, + .int3, + .jmp, + .lea, + .lfence, + .lzcnt, + .mfence, + .mov, + .movbe, + .movzx, + .mul, + .neg, + .nop, + .not, + .@"or", + .pop, + .popcnt, + .push, + .rcl, + .rcr, + .ret, + .rol, + .ror, + .sal, + .sar, + .sbb, + .sfence, + .shl, + .shld, + .shr, + .shrd, + .sub, + .syscall, + .@"test", + .tzcnt, + .ud2, + .xadd, + .xchg, + .xor, + + .addss, + .cmpss, + .divss, + .maxss, + .minss, + .movss, + .mulss, + .roundss, + .subss, + .ucomiss, + .addsd, + .cmpsd, + .divsd, + .maxsd, + .minsd, + .movsd, + .mulsd, + .roundsd, + .subsd, + .ucomisd, + => try lower.mirGeneric(inst), + + .cmps, + .lods, + .movs, + .scas, + .stos, + => try lower.mirString(inst), + + .cmpxchgb => try lower.mirCmpxchgBytes(inst), + + .jmp_reloc => try lower.emit(.none, .jmp, &.{.{ .imm = Immediate.s(0) }}), + + .call_extern => try lower.emit(.none, .call, &.{.{ .imm = Immediate.s(0) }}), + + .lea_linker => try lower.mirLeaLinker(inst), + + .mov_moffs => try lower.mirMovMoffs(inst), + + .movsx => try lower.mirMovsx(inst), + .cmovcc => try lower.mirCmovcc(inst), + .setcc => try lower.mirSetcc(inst), + .jcc => try lower.emit(.none, mnem_cc(.j, inst.data.inst_cc.cc), &.{.{ .imm = Immediate.s(0) }}), + + .push_regs, .pop_regs => try lower.mirPushPopRegisterList(inst), + + .dbg_line, + .dbg_prologue_end, + .dbg_epilogue_begin, + .dead, + => {}, + } + + return lower.result[0..lower.result_len]; +} + +pub fn fail(lower: *Lower, comptime format: []const u8, args: anytype) Error { + @setCold(true); + assert(lower.err_msg == null); + lower.err_msg = try ErrorMsg.create(lower.allocator, lower.src_loc, format, args); + return error.LowerFail; +} + +fn mnem_cc(comptime base: @Type(.EnumLiteral), cc: bits.Condition) Mnemonic { + return switch (cc) { + inline else => |c| @field(Mnemonic, @tagName(base) ++ @tagName(c)), + }; +} + +fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate { + return switch (ops) { + .rri_s, + .ri_s, + .i_s, + .mi_sib_s, + .mi_rip_s, + .lock_mi_sib_s, + .lock_mi_rip_s, + => Immediate.s(@bitCast(i32, i)), + + .rri_u, + .ri_u, + .i_u, + .mi_sib_u, + .mi_rip_u, + .lock_mi_sib_u, + .lock_mi_rip_u, + .mri_sib, + .mri_rip, + => Immediate.u(i), + + .ri64 => Immediate.u(lower.mir.extraData(Mir.Imm64, i).data.decode()), + + else => unreachable, + }; +} + +fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory { + return switch (ops) { + .rm_sib, + .rm_sib_cc, + .m_sib, + .m_sib_cc, + .mi_sib_u, + .mi_sib_s, + .mr_sib, + .mrr_sib, + .mri_sib, + .lock_m_sib, + .lock_mi_sib_u, + .lock_mi_sib_s, + .lock_mr_sib, + => lower.mir.extraData(Mir.MemorySib, payload).data.decode(), + + .rm_rip, + .rm_rip_cc, + .m_rip, + .m_rip_cc, + .mi_rip_u, + .mi_rip_s, + .mr_rip, + .mrr_rip, + .mri_rip, + .lock_m_rip, + .lock_mi_rip_u, + .lock_mi_rip_s, + .lock_mr_rip, + => lower.mir.extraData(Mir.MemoryRip, payload).data.decode(), + + .rax_moffs, + .moffs_rax, + .lock_moffs_rax, + => lower.mir.extraData(Mir.MemoryMoffs, payload).data.decode(), + + else => unreachable, + }; +} + +fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void { + lower.result[lower.result_len] = try Instruction.new(prefix, mnemonic, ops); + lower.result_len += 1; +} + +fn mirGeneric(lower: *Lower, inst: Mir.Inst) Error!void { + try lower.emit(switch (inst.ops) { + else => .none, + .lock_m_sib, + .lock_m_rip, + .lock_mi_sib_u, + .lock_mi_rip_u, + .lock_mi_sib_s, + .lock_mi_rip_s, + .lock_mr_sib, + .lock_mr_rip, + .lock_moffs_rax, + => .lock, + }, switch (inst.tag) { + inline else => |tag| if (@hasField(Mnemonic, @tagName(tag))) + @field(Mnemonic, @tagName(tag)) + else + unreachable, + }, switch (inst.ops) { + .none => &.{}, + .i_s, .i_u => &.{ + .{ .imm = lower.imm(inst.ops, inst.data.i) }, + }, + .r => &.{ + .{ .reg = inst.data.r }, + }, + .rr => &.{ + .{ .reg = inst.data.rr.r1 }, + .{ .reg = inst.data.rr.r2 }, + }, + .rrr => &.{ + .{ .reg = inst.data.rrr.r1 }, + .{ .reg = inst.data.rrr.r2 }, + .{ .reg = inst.data.rrr.r3 }, + }, + .ri_s, .ri_u => &.{ + .{ .reg = inst.data.ri.r }, + .{ .imm = lower.imm(inst.ops, inst.data.ri.i) }, + }, + .ri64 => &.{ + .{ .reg = inst.data.rx.r }, + .{ .imm = lower.imm(inst.ops, inst.data.rx.payload) }, + }, + .rri_s, .rri_u => &.{ + .{ .reg = inst.data.rri.r1 }, + .{ .reg = inst.data.rri.r2 }, + .{ .imm = lower.imm(inst.ops, inst.data.rri.i) }, + }, + .m_sib, .lock_m_sib, .m_rip, .lock_m_rip => &.{ + .{ .mem = lower.mem(inst.ops, inst.data.payload) }, + }, + .mi_sib_s, + .lock_mi_sib_s, + .mi_sib_u, + .lock_mi_sib_u, + .mi_rip_u, + .lock_mi_rip_u, + .mi_rip_s, + .lock_mi_rip_s, + => &.{ + .{ .mem = lower.mem(inst.ops, inst.data.ix.payload) }, + .{ .imm = lower.imm(inst.ops, inst.data.ix.i) }, + }, + .rm_sib, .rm_rip => &.{ + .{ .reg = inst.data.rx.r }, + .{ .mem = lower.mem(inst.ops, inst.data.rx.payload) }, + }, + .mr_sib, .lock_mr_sib, .mr_rip, .lock_mr_rip => &.{ + .{ .mem = lower.mem(inst.ops, inst.data.rx.payload) }, + .{ .reg = inst.data.rx.r }, + }, + .mrr_sib, .mrr_rip => &.{ + .{ .mem = lower.mem(inst.ops, 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) }, + .{ .reg = inst.data.rix.r }, + .{ .imm = lower.imm(inst.ops, inst.data.rix.i) }, + }, + else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), + }); +} + +fn mirString(lower: *Lower, inst: Mir.Inst) Error!void { + switch (inst.ops) { + .string => try lower.emit(switch (inst.data.string.repeat) { + inline else => |repeat| @field(Prefix, @tagName(repeat)), + }, switch (inst.tag) { + inline .cmps, .lods, .movs, .scas, .stos => |tag| switch (inst.data.string.width) { + inline else => |width| @field(Mnemonic, @tagName(tag) ++ @tagName(width)), + }, + else => unreachable, + }, &.{}), + else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), + } +} + +fn mirCmpxchgBytes(lower: *Lower, inst: Mir.Inst) Error!void { + const ops: [1]Operand = switch (inst.ops) { + .m_sib, .lock_m_sib, .m_rip, .lock_m_rip => .{ + .{ .mem = lower.mem(inst.ops, inst.data.payload) }, + }, + else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), + }; + try lower.emit(switch (inst.ops) { + .m_sib, .m_rip => .none, + .lock_m_sib, .lock_m_rip => .lock, + else => unreachable, + }, switch (@divExact(ops[0].bitSize(), 8)) { + 8 => .cmpxchg8b, + 16 => .cmpxchg16b, + else => return lower.fail("invalid operand for {s}", .{@tagName(inst.tag)}), + }, &ops); +} + +fn mirMovMoffs(lower: *Lower, inst: Mir.Inst) Error!void { + try lower.emit(switch (inst.ops) { + .rax_moffs, .moffs_rax => .none, + .lock_moffs_rax => .lock, + else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), + }, .mov, switch (inst.ops) { + .rax_moffs => &.{ + .{ .reg = .rax }, + .{ .mem = lower.mem(inst.ops, inst.data.payload) }, + }, + .moffs_rax, .lock_moffs_rax => &.{ + .{ .mem = lower.mem(inst.ops, inst.data.payload) }, + .{ .reg = .rax }, + }, + else => unreachable, + }); +} + +fn mirMovsx(lower: *Lower, inst: Mir.Inst) Error!void { + const ops: [2]Operand = switch (inst.ops) { + .rr => .{ + .{ .reg = inst.data.rr.r1 }, + .{ .reg = inst.data.rr.r2 }, + }, + .rm_sib, .rm_rip => .{ + .{ .reg = inst.data.rx.r }, + .{ .mem = lower.mem(inst.ops, inst.data.rx.payload) }, + }, + else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), + }; + try lower.emit(.none, switch (ops[0].bitSize()) { + 32, 64 => switch (ops[1].bitSize()) { + 32 => .movsxd, + else => .movsx, + }, + else => .movsx, + }, &ops); +} + +fn mirCmovcc(lower: *Lower, inst: Mir.Inst) Error!void { + switch (inst.ops) { + .rr_cc => try lower.emit(.none, mnem_cc(.cmov, inst.data.rr_cc.cc), &.{ + .{ .reg = inst.data.rr_cc.r1 }, + .{ .reg = inst.data.rr_cc.r2 }, + }), + .rm_sib_cc, .rm_rip_cc => try lower.emit(.none, mnem_cc(.cmov, inst.data.rx_cc.cc), &.{ + .{ .reg = inst.data.rx_cc.r }, + .{ .mem = lower.mem(inst.ops, inst.data.rx_cc.payload) }, + }), + else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), + } +} + +fn mirSetcc(lower: *Lower, inst: Mir.Inst) Error!void { + switch (inst.ops) { + .r_cc => try lower.emit(.none, mnem_cc(.set, inst.data.r_cc.cc), &.{ + .{ .reg = inst.data.r_cc.r }, + }), + .m_sib_cc, .m_rip_cc => try lower.emit(.none, mnem_cc(.set, inst.data.x_cc.cc), &.{ + .{ .mem = lower.mem(inst.ops, inst.data.x_cc.payload) }, + }), + else => return lower.fail("TODO lower {s} {s}", .{ @tagName(inst.tag), @tagName(inst.ops) }), + } +} + +fn mirPushPopRegisterList(lower: *Lower, inst: Mir.Inst) Error!void { + const save_reg_list = lower.mir.extraData(Mir.SaveRegisterList, inst.data.payload).data; + const base = @intToEnum(Register, save_reg_list.base_reg); + var disp: i32 = -@intCast(i32, save_reg_list.stack_end); + const reg_list = Mir.RegisterList.fromInt(save_reg_list.register_list); + const callee_preserved_regs = abi.getCalleePreservedRegs(lower.target.*); + for (callee_preserved_regs) |callee_preserved_reg| { + if (!reg_list.isSet(callee_preserved_regs, callee_preserved_reg)) continue; + const reg_op = Operand{ .reg = callee_preserved_reg }; + const mem_op = Operand{ .mem = Memory.sib(.qword, .{ .base = base, .disp = disp }) }; + try lower.emit(.none, .mov, switch (inst.tag) { + .push_regs => &.{ mem_op, reg_op }, + .pop_regs => &.{ reg_op, mem_op }, + else => unreachable, + }); + disp += 8; + } +} + +fn mirLeaLinker(lower: *Lower, inst: Mir.Inst) Error!void { + const metadata = lower.mir.extraData(Mir.LeaRegisterReloc, inst.data.payload).data; + const reg = @intToEnum(Register, metadata.reg); + try lower.emit(.none, .lea, &.{ + .{ .reg = reg }, + .{ .mem = Memory.rip(Memory.PtrSize.fromBitSize(reg.bitSize()), 0) }, + }); +} + +const abi = @import("abi.zig"); +const assert = std.debug.assert; +const bits = @import("bits.zig"); +const encoder = @import("encoder.zig"); +const std = @import("std"); + +const Air = @import("../../Air.zig"); +const Allocator = std.mem.Allocator; +const ErrorMsg = Module.ErrorMsg; +const Immediate = bits.Immediate; +const Instruction = encoder.Instruction; +const Lower = @This(); +const Memory = bits.Memory; +const Mir = @import("Mir.zig"); +const Mnemonic = Instruction.Mnemonic; +const Module = @import("../../Module.zig"); +const Operand = Instruction.Operand; +const Prefix = Instruction.Prefix; +const Register = bits.Register; diff --git a/src/arch/x86_64/Mir.zig b/src/arch/x86_64/Mir.zig index a6a41158143a..0ceee6bac184 100644 --- a/src/arch/x86_64/Mir.zig +++ b/src/arch/x86_64/Mir.zig @@ -655,16 +655,19 @@ pub const MemoryMoffs = struct { msb: u32, lsb: u32, - pub fn encodeOffset(moffs: *MemoryMoffs, v: u64) void { - moffs.msb = @truncate(u32, v >> 32); - moffs.lsb = @truncate(u32, v); + pub fn encode(seg: Register, offset: u64) MemoryMoffs { + return .{ + .seg = @enumToInt(seg), + .msb = @truncate(u32, offset >> 32), + .lsb = @truncate(u32, offset >> 0), + }; } - pub fn decodeOffset(moffs: *const MemoryMoffs) u64 { - var res: u64 = 0; - res |= (@intCast(u64, moffs.msb) << 32); - res |= @intCast(u64, moffs.lsb); - return res; + pub fn decode(moffs: MemoryMoffs) Memory { + return .{ .moffs = .{ + .seg = @intToEnum(Register, moffs.seg), + .offset = @as(u64, moffs.msb) << 32 | @as(u64, moffs.lsb) << 0, + } }; } }; diff --git a/src/arch/x86_64/bits.zig b/src/arch/x86_64/bits.zig index 76ad26a9a003..b2a6b31749d8 100644 --- a/src/arch/x86_64/bits.zig +++ b/src/arch/x86_64/bits.zig @@ -515,7 +515,7 @@ pub const Memory = union(enum) { return switch (mem) { .rip => |r| r.ptr_size.bitSize(), .sib => |s| s.ptr_size.bitSize(), - .moffs => unreachable, + .moffs => 64, }; } }; diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index 05f66062ac1c..e7e231c0634f 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -11,12 +11,9 @@ const Memory = bits.Memory; const Register = bits.Register; pub const Instruction = struct { - op1: Operand = .none, - op2: Operand = .none, - op3: Operand = .none, - op4: Operand = .none, prefix: Prefix = .none, encoding: Encoding, + ops: [4]Operand = .{.none} ** 4, pub const Mnemonic = Encoding.Mnemonic; @@ -107,102 +104,87 @@ pub const Instruction = struct { } }; - pub const Init = struct { - prefix: Prefix = .none, - op1: Operand = .none, - op2: Operand = .none, - op3: Operand = .none, - op4: Operand = .none, - }; - - pub fn new(mnemonic: Mnemonic, args: Init) !Instruction { - const encoding = (try Encoding.findByMnemonic(mnemonic, args)) orelse { + pub fn new(prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) !Instruction { + const encoding = (try Encoding.findByMnemonic(prefix, mnemonic, ops)) orelse { log.err("no encoding found for: {s} {s} {s} {s} {s} {s}", .{ - @tagName(args.prefix), + @tagName(prefix), @tagName(mnemonic), - @tagName(Encoding.Op.fromOperand(args.op1)), - @tagName(Encoding.Op.fromOperand(args.op2)), - @tagName(Encoding.Op.fromOperand(args.op3)), - @tagName(Encoding.Op.fromOperand(args.op4)), + @tagName(if (ops.len > 0) Encoding.Op.fromOperand(ops[0]) else .none), + @tagName(if (ops.len > 1) Encoding.Op.fromOperand(ops[1]) else .none), + @tagName(if (ops.len > 2) Encoding.Op.fromOperand(ops[2]) else .none), + @tagName(if (ops.len > 3) Encoding.Op.fromOperand(ops[3]) else .none), }); return error.InvalidInstruction; }; log.debug("selected encoding: {}", .{encoding}); - return .{ - .prefix = args.prefix, - .op1 = args.op1, - .op2 = args.op2, - .op3 = args.op3, - .op4 = args.op4, + + var inst = Instruction{ + .prefix = prefix, .encoding = encoding, + .ops = [1]Operand{.none} ** 4, }; + std.mem.copy(Operand, &inst.ops, ops); + return inst; } pub fn fmtPrint(inst: Instruction, writer: anytype) !void { if (inst.prefix != .none) try writer.print("{s} ", .{@tagName(inst.prefix)}); try writer.print("{s}", .{@tagName(inst.encoding.mnemonic)}); - const ops = [_]struct { Operand, Encoding.Op }{ - .{ inst.op1, inst.encoding.op1 }, - .{ inst.op2, inst.encoding.op2 }, - .{ inst.op3, inst.encoding.op3 }, - .{ inst.op4, inst.encoding.op4 }, - }; - for (&ops, 0..) |op, i| { - if (op[0] == .none) break; - if (i > 0) { - try writer.writeByte(','); - } + for (inst.ops, inst.encodings.ops, 0..) |op, enc, i| { + if (op == .none) break; + if (i > 0) try writer.writeByte(','); try writer.writeByte(' '); - try op[0].fmtPrint(op[1], writer); + try op.fmtPrint(enc, writer); } } pub fn encode(inst: Instruction, writer: anytype) !void { const encoder = Encoder(@TypeOf(writer)){ .writer = writer }; - const encoding = inst.encoding; + const enc = inst.encoding; + const data = enc.data; try inst.encodeLegacyPrefixes(encoder); try inst.encodeMandatoryPrefix(encoder); try inst.encodeRexPrefix(encoder); try inst.encodeOpcode(encoder); - switch (encoding.op_en) { + switch (data.op_en) { .np, .o => {}, - .i, .d => try encodeImm(inst.op1.imm, encoding.op1, encoder), - .zi, .oi => try encodeImm(inst.op2.imm, encoding.op2, encoder), - .fd => try encoder.imm64(inst.op2.mem.moffs.offset), - .td => try encoder.imm64(inst.op1.mem.moffs.offset), + .i, .d => try encodeImm(inst.ops[0].imm, data.ops[0], encoder), + .zi, .oi => try encodeImm(inst.ops[1].imm, data.ops[1], encoder), + .fd => try encoder.imm64(inst.ops[1].mem.moffs.offset), + .td => try encoder.imm64(inst.ops[0].mem.moffs.offset), else => { - const mem_op = switch (encoding.op_en) { - .m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.op1, - .rm, .rmi => inst.op2, + const mem_op = switch (data.op_en) { + .m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.ops[0], + .rm, .rmi => inst.ops[1], else => unreachable, }; switch (mem_op) { .reg => |reg| { - const rm = switch (encoding.op_en) { - .m, .mi, .m1, .mc => encoding.modRmExt(), - .mr, .mri, .mrc => inst.op2.reg.lowEnc(), - .rm, .rmi => inst.op1.reg.lowEnc(), + const rm = switch (data.op_en) { + .m, .mi, .m1, .mc => enc.modRmExt(), + .mr, .mri, .mrc => inst.ops[1].reg.lowEnc(), + .rm, .rmi => inst.ops[0].reg.lowEnc(), else => unreachable, }; try encoder.modRm_direct(rm, reg.lowEnc()); }, .mem => |mem| { - const op = switch (encoding.op_en) { + const op = switch (data.op_en) { .m, .mi, .m1, .mc => .none, - .mr, .mri, .mrc => inst.op2, - .rm, .rmi => inst.op1, + .mr, .mri, .mrc => inst.ops[1], + .rm, .rmi => inst.ops[0], else => unreachable, }; - try encodeMemory(encoding, mem, op, encoder); + try encodeMemory(enc, mem, op, encoder); }, else => unreachable, } - switch (encoding.op_en) { - .mi => try encodeImm(inst.op2.imm, encoding.op2, encoder), - .rmi, .mri => try encodeImm(inst.op3.imm, encoding.op3, encoder), + switch (data.op_en) { + .mi => try encodeImm(inst.ops[1].imm, data.ops[1], encoder), + .rmi, .mri => try encodeImm(inst.ops[2].imm, data.ops[2], encoder), else => {}, } }, @@ -214,15 +196,16 @@ pub const Instruction = struct { const first = @boolToInt(inst.encoding.mandatoryPrefix() != null); const final = opcode.len - 1; for (opcode[first..final]) |byte| try encoder.opcode_1byte(byte); - switch (inst.encoding.op_en) { - .o, .oi => try encoder.opcode_withReg(opcode[final], inst.op1.reg.lowEnc()), + switch (inst.encoding.data.op_en) { + .o, .oi => try encoder.opcode_withReg(opcode[final], inst.ops[0].reg.lowEnc()), else => try encoder.opcode_1byte(opcode[final]), } } fn encodeLegacyPrefixes(inst: Instruction, encoder: anytype) !void { const enc = inst.encoding; - const op_en = enc.op_en; + const data = enc.data; + const op_en = data.op_en; var legacy = LegacyPrefixes{}; @@ -233,7 +216,7 @@ pub const Instruction = struct { .rep, .repe, .repz => legacy.prefix_f3 = true, } - if (enc.mode == .none) { + if (data.mode == .none) { const bit_size = enc.operandBitSize(); if (bit_size == 16) { legacy.set16BitOverride(); @@ -242,17 +225,17 @@ pub const Instruction = struct { const segment_override: ?Register = switch (op_en) { .i, .zi, .o, .oi, .d, .np => null, - .fd => inst.op2.mem.base().?, - .td => inst.op1.mem.base().?, - .rm, .rmi => if (inst.op2.isSegmentRegister()) blk: { - break :blk switch (inst.op2) { + .fd => inst.ops[1].mem.base().?, + .td => inst.ops[0].mem.base().?, + .rm, .rmi => if (inst.ops[1].isSegmentRegister()) blk: { + break :blk switch (inst.ops[1]) { .reg => |r| r, .mem => |m| m.base().?, else => unreachable, }; } else null, - .m, .mi, .m1, .mc, .mr, .mri, .mrc => if (inst.op1.isSegmentRegister()) blk: { - break :blk switch (inst.op1) { + .m, .mi, .m1, .mc, .mr, .mri, .mrc => if (inst.ops[0].isSegmentRegister()) blk: { + break :blk switch (inst.ops[0]) { .reg => |r| r, .mem => |m| m.base().?, else => unreachable, @@ -267,19 +250,19 @@ pub const Instruction = struct { } fn encodeRexPrefix(inst: Instruction, encoder: anytype) !void { - const op_en = inst.encoding.op_en; + const op_en = inst.encoding.data.op_en; var rex = Rex{}; - rex.present = inst.encoding.mode == .rex; - rex.w = inst.encoding.mode == .long; + rex.present = inst.encoding.data.mode == .rex; + rex.w = inst.encoding.data.mode == .long; switch (op_en) { .np, .i, .zi, .fd, .td, .d => {}, - .o, .oi => rex.b = inst.op1.reg.isExtended(), + .o, .oi => rex.b = inst.ops[0].reg.isExtended(), .m, .mi, .m1, .mc, .mr, .rm, .rmi, .mri, .mrc => { const r_op = switch (op_en) { - .rm, .rmi => inst.op1, - .mr, .mri, .mrc => inst.op2, + .rm, .rmi => inst.ops[0], + .mr, .mri, .mrc => inst.ops[1], else => null, }; if (r_op) |op| { @@ -287,8 +270,8 @@ pub const Instruction = struct { } const b_x_op = switch (op_en) { - .rm, .rmi => inst.op2, - .m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.op1, + .rm, .rmi => inst.ops[1], + .m, .mi, .m1, .mc, .mr, .mri, .mrc => inst.ops[0], else => unreachable, }; switch (b_x_op) { @@ -827,16 +810,14 @@ const TestEncode = struct { buffer: [32]u8 = undefined, index: usize = 0, - fn encode(enc: *TestEncode, mnemonic: Instruction.Mnemonic, args: Instruction.Init) !void { + fn encode( + enc: *TestEncode, + mnemonic: Instruction.Mnemonic, + ops: []const Instruction.Operand, + ) !void { var stream = std.io.fixedBufferStream(&enc.buffer); var count_writer = std.io.countingWriter(stream.writer()); - const inst = try Instruction.new(mnemonic, .{ - .prefix = args.prefix, - .op1 = args.op1, - .op2 = args.op2, - .op3 = args.op3, - .op4 = args.op4, - }); + const inst = try Instruction.new(.none, mnemonic, ops); try inst.encode(count_writer.writer()); enc.index = count_writer.bytes_written; } @@ -850,9 +831,9 @@ test "encode" { var buf = std.ArrayList(u8).init(testing.allocator); defer buf.deinit(); - const inst = try Instruction.new(.mov, .{ - .op1 = .{ .reg = .rbx }, - .op2 = .{ .imm = Immediate.u(4) }, + const inst = try Instruction.new(.none, .mov, &.{ + .{ .reg = .rbx }, + .{ .imm = Immediate.u(4) }, }); try inst.encode(buf.writer()); try testing.expectEqualSlices(u8, &.{ 0x48, 0xc7, 0xc3, 0x4, 0x0, 0x0, 0x0 }, buf.items); @@ -861,61 +842,94 @@ test "encode" { test "lower I encoding" { var enc = TestEncode{}; - try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.push, &.{ + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x6A\x10", enc.code(), "push 0x10"); - try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000) } }); + try enc.encode(.push, &.{ + .{ .imm = Immediate.u(0x1000) }, + }); try expectEqualHexStrings("\x66\x68\x00\x10", enc.code(), "push 0x1000"); - try enc.encode(.push, .{ .op1 = .{ .imm = Immediate.u(0x10000000) } }); + try enc.encode(.push, &.{ + .{ .imm = Immediate.u(0x10000000) }, + }); try expectEqualHexStrings("\x68\x00\x00\x00\x10", enc.code(), "push 0x10000000"); - try enc.encode(.adc, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10000000) } }); + try enc.encode(.adc, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x10000000) }, + }); try expectEqualHexStrings("\x48\x15\x00\x00\x00\x10", enc.code(), "adc rax, 0x10000000"); - try enc.encode(.add, .{ .op1 = .{ .reg = .al }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.add, &.{ + .{ .reg = .al }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x04\x10", enc.code(), "add al, 0x10"); - try enc.encode(.add, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.add, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); - try enc.encode(.sbb, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.sbb, &.{ + .{ .reg = .ax }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x66\x1D\x10\x00", enc.code(), "sbb ax, 0x10"); - try enc.encode(.xor, .{ .op1 = .{ .reg = .al }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.xor, &.{ + .{ .reg = .al }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x34\x10", enc.code(), "xor al, 0x10"); } test "lower MI encoding" { var enc = TestEncode{}; - try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r12 }, + .{ .imm = Immediate.u(0x1000) }, + }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); - try enc.encode(.mov, .{ - .op1 = .{ .mem = Memory.sib(.byte, .{ .base = .r12 }) }, - .op2 = .{ .imm = Immediate.u(0x10) }, + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.byte, .{ .base = .r12 }) }, + .{ .imm = Immediate.u(0x10) }, }); try expectEqualHexStrings("\x41\xC6\x04\x24\x10", enc.code(), "mov BYTE PTR [r12], 0x10"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r12 }, + .{ .imm = Immediate.u(0x1000) }, + }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r12 }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r12 }, + .{ .imm = Immediate.u(0x1000) }, + }); try expectEqualHexStrings("\x49\xC7\xC4\x00\x10\x00\x00", enc.code(), "mov r12, 0x1000"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\xc7\xc0\x10\x00\x00\x00", enc.code(), "mov rax, 0x10"); - try enc.encode(.mov, .{ - .op1 = .{ .mem = Memory.sib(.dword, .{ .base = .r11 }) }, - .op2 = .{ .imm = Immediate.u(0x10) }, + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .r11 }) }, + .{ .imm = Immediate.u(0x10) }, }); try expectEqualHexStrings("\x41\xc7\x03\x10\x00\x00\x00", enc.code(), "mov DWORD PTR [r11], 0x10"); - try enc.encode(.mov, .{ - .op1 = .{ .mem = Memory.rip(.qword, 0x10) }, - .op2 = .{ .imm = Immediate.u(0x10) }, + try enc.encode(.mov, &.{ + .{ .mem = Memory.rip(.qword, 0x10) }, + .{ .imm = Immediate.u(0x10) }, }); try expectEqualHexStrings( "\x48\xC7\x05\x10\x00\x00\x00\x10\x00\x00\x00", @@ -923,99 +937,108 @@ test "lower MI encoding" { "mov QWORD PTR [rip + 0x10], 0x10", ); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .disp = -8, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -8 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\xc7\x45\xf8\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rbp - 8], 0x10"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.word, .{ - .base = .rbp, - .disp = -2, - }) }, .op2 = .{ .imm = Immediate.s(-16) } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -2 }) }, + .{ .imm = Immediate.s(-16) }, + }); try expectEqualHexStrings("\x66\xC7\x45\xFE\xF0\xFF", enc.code(), "mov WORD PTR [rbp - 2], -16"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ - .base = .rbp, - .disp = -1, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.byte, .{ .base = .rbp, .disp = -1 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\xC6\x45\xFF\x10", enc.code(), "mov BYTE PTR [rbp - 1], 0x10"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .ds, - .disp = 0x10000000, - .scale_index = .{ - .scale = 2, - .index = .rcx, - }, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.qword, .{ + .base = .ds, + .disp = 0x10000000, + .scale_index = .{ .scale = 2, .index = .rcx }, + }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings( "\x48\xC7\x04\x4D\x00\x00\x00\x10\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rcx*2 + 0x10000000], 0x10", ); - try enc.encode(.adc, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ - .base = .rbp, - .disp = -0x10, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.adc, &.{ + .{ .mem = Memory.sib(.byte, .{ .base = .rbp, .disp = -0x10 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x80\x55\xF0\x10", enc.code(), "adc BYTE PTR [rbp - 0x10], 0x10"); - try enc.encode(.adc, .{ .op1 = .{ .mem = Memory.rip(.qword, 0) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.adc, &.{ + .{ .mem = Memory.rip(.qword, 0) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\x83\x15\x00\x00\x00\x00\x10", enc.code(), "adc QWORD PTR [rip], 0x10"); - try enc.encode(.adc, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.adc, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\x83\xD0\x10", enc.code(), "adc rax, 0x10"); - try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .rdx, - .disp = -8, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.add, &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .rdx, .disp = -8 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x83\x42\xF8\x10", enc.code(), "add DWORD PTR [rdx - 8], 0x10"); - try enc.encode(.add, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.add, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x48\x83\xC0\x10", enc.code(), "add rax, 0x10"); - try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .disp = -0x10, - }) }, .op2 = .{ .imm = Immediate.s(-0x10) } }); + try enc.encode(.add, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -0x10 }) }, + .{ .imm = Immediate.s(-0x10) }, + }); try expectEqualHexStrings("\x48\x83\x45\xF0\xF0", enc.code(), "add QWORD PTR [rbp - 0x10], -0x10"); - try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .ds, - .disp = 0x10000000, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.@"and", &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings( "\x83\x24\x25\x00\x00\x00\x10\x10", enc.code(), "and DWORD PTR ds:0x10000000, 0x10", ); - try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .es, - .disp = 0x10000000, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.@"and", &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .es, .disp = 0x10000000 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings( "\x26\x83\x24\x25\x00\x00\x00\x10\x10", enc.code(), "and DWORD PTR es:0x10000000, 0x10", ); - try enc.encode(.@"and", .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .r12, - .disp = 0x10000000, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.@"and", &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .r12, .disp = 0x10000000 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings( "\x41\x83\xA4\x24\x00\x00\x00\x10\x10", enc.code(), "and DWORD PTR [r12 + 0x10000000], 0x10", ); - try enc.encode(.sub, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .r11, - .disp = 0x10000000, - }) }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.sub, &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .r11, .disp = 0x10000000 }) }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings( "\x41\x83\xAB\x00\x00\x00\x10\x10", enc.code(), @@ -1026,185 +1049,227 @@ test "lower MI encoding" { test "lower RM encoding" { var enc = TestEncode{}; - try enc.encode(.mov, .{ - .op1 = .{ .reg = .rax }, - .op2 = .{ .mem = Memory.sib(.qword, .{ .base = .r11 }) }, + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.sib(.qword, .{ .base = .r11 }) }, }); try expectEqualHexStrings("\x49\x8b\x03", enc.code(), "mov rax, QWORD PTR [r11]"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .rbx }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .ds, - .disp = 0x10, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rbx }, + .{ .mem = Memory.sib(.qword, .{ .base = .ds, .disp = 0x10 }) }, + }); try expectEqualHexStrings("\x48\x8B\x1C\x25\x10\x00\x00\x00", enc.code(), "mov rbx, QWORD PTR ds:0x10"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .disp = -4, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) }, + }); try expectEqualHexStrings("\x48\x8B\x45\xFC", enc.code(), "mov rax, QWORD PTR [rbp - 4]"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .scale_index = .{ - .scale = 1, - .index = .rcx, - }, - .disp = -8, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .scale_index = .{ .scale = 1, .index = .rcx }, + .disp = -8, + }) }, + }); try expectEqualHexStrings("\x48\x8B\x44\x0D\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*1 - 8]"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.sib(.dword, .{ - .base = .rbp, - .scale_index = .{ - .scale = 4, - .index = .rdx, - }, - .disp = -4, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.sib(.dword, .{ + .base = .rbp, + .scale_index = .{ .scale = 4, .index = .rdx }, + .disp = -4, + }) }, + }); try expectEqualHexStrings("\x8B\x44\x95\xFC", enc.code(), "mov eax, dword ptr [rbp + rdx*4 - 4]"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .scale_index = .{ - .scale = 8, - .index = .rcx, - }, - .disp = -8, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.sib(.qword, .{ + .base = .rbp, + .scale_index = .{ .scale = 8, .index = .rcx }, + .disp = -8, + }) }, + }); try expectEqualHexStrings("\x48\x8B\x44\xCD\xF8", enc.code(), "mov rax, QWORD PTR [rbp + rcx*8 - 8]"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r8b }, .op2 = .{ .mem = Memory.sib(.byte, .{ - .base = .rsi, - .scale_index = .{ - .scale = 1, - .index = .rcx, - }, - .disp = -24, - }) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r8b }, + .{ .mem = Memory.sib(.byte, .{ + .base = .rsi, + .scale_index = .{ .scale = 1, .index = .rcx }, + .disp = -24, + }) }, + }); try expectEqualHexStrings("\x44\x8A\x44\x0E\xE8", enc.code(), "mov r8b, BYTE PTR [rsi + rcx*1 - 24]"); // TODO this mnemonic needs cleanup as some prefixes are obsolete. - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .cs } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .reg = .cs }, + }); try expectEqualHexStrings("\x48\x8C\xC8", enc.code(), "mov rax, cs"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .disp = -16, - }) }, .op2 = .{ .reg = .fs } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -16 }) }, + .{ .reg = .fs }, + }); try expectEqualHexStrings("\x48\x8C\x65\xF0", enc.code(), "mov QWORD PTR [rbp - 16], fs"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r12w }, .op2 = .{ .reg = .cs } }); + try enc.encode(.mov, &.{ + .{ .reg = .r12w }, + .{ .reg = .cs }, + }); try expectEqualHexStrings("\x66\x41\x8C\xCC", enc.code(), "mov r12w, cs"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.word, .{ - .base = .rbp, - .disp = -16, - }) }, .op2 = .{ .reg = .fs } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, + .{ .reg = .fs }, + }); try expectEqualHexStrings("\x66\x8C\x65\xF0", enc.code(), "mov WORD PTR [rbp - 16], fs"); - try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .reg = .bx } }); + try enc.encode(.movsx, &.{ + .{ .reg = .eax }, + .{ .reg = .bx }, + }); try expectEqualHexStrings("\x0F\xBF\xC3", enc.code(), "movsx eax, bx"); - try enc.encode(.movsx, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .reg = .bl } }); + try enc.encode(.movsx, &.{ + .{ .reg = .eax }, + .{ .reg = .bl }, + }); try expectEqualHexStrings("\x0F\xBE\xC3", enc.code(), "movsx eax, bl"); - try enc.encode(.movsx, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .reg = .bl } }); + try enc.encode(.movsx, &.{ + .{ .reg = .ax }, + .{ .reg = .bl }, + }); try expectEqualHexStrings("\x66\x0F\xBE\xC3", enc.code(), "movsx ax, bl"); - try enc.encode(.movsx, .{ - .op1 = .{ .reg = .eax }, - .op2 = .{ .mem = Memory.sib(.word, .{ .base = .rbp }) }, + try enc.encode(.movsx, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.sib(.word, .{ .base = .rbp }) }, }); try expectEqualHexStrings("\x0F\xBF\x45\x00", enc.code(), "movsx eax, BYTE PTR [rbp]"); - try enc.encode(.movsx, .{ - .op1 = .{ .reg = .eax }, - .op2 = .{ .mem = Memory.sib(.byte, .{ .scale_index = .{ .index = .rax, .scale = 2 } }) }, + try enc.encode(.movsx, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.sib(.byte, .{ .scale_index = .{ .index = .rax, .scale = 2 } }) }, }); try expectEqualHexStrings("\x0F\xBE\x04\x45\x00\x00\x00\x00", enc.code(), "movsx eax, BYTE PTR [rax * 2]"); - try enc.encode(.movsx, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.rip(.byte, 0x10) } }); + try enc.encode(.movsx, &.{ + .{ .reg = .ax }, + .{ .mem = Memory.rip(.byte, 0x10) }, + }); try expectEqualHexStrings("\x66\x0F\xBE\x05\x10\x00\x00\x00", enc.code(), "movsx ax, BYTE PTR [rip + 0x10]"); - try enc.encode(.movsx, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .bx } }); + try enc.encode(.movsx, &.{ + .{ .reg = .rax }, + .{ .reg = .bx }, + }); try expectEqualHexStrings("\x48\x0F\xBF\xC3", enc.code(), "movsx rax, bx"); - try enc.encode(.movsxd, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .ebx } }); + try enc.encode(.movsxd, &.{ + .{ .reg = .rax }, + .{ .reg = .ebx }, + }); try expectEqualHexStrings("\x48\x63\xC3", enc.code(), "movsxd rax, ebx"); - try enc.encode(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.rip(.qword, 0x10) } }); + try enc.encode(.lea, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.rip(.qword, 0x10) }, + }); try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, QWORD PTR [rip + 0x10]"); - try enc.encode(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.rip(.dword, 0x10) } }); + try enc.encode(.lea, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.rip(.dword, 0x10) }, + }); try expectEqualHexStrings("\x48\x8D\x05\x10\x00\x00\x00", enc.code(), "lea rax, DWORD PTR [rip + 0x10]"); - try enc.encode(.lea, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.rip(.dword, 0x10) } }); + try enc.encode(.lea, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.rip(.dword, 0x10) }, + }); try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, DWORD PTR [rip + 0x10]"); - try enc.encode(.lea, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.rip(.word, 0x10) } }); + try enc.encode(.lea, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.rip(.word, 0x10) }, + }); try expectEqualHexStrings("\x8D\x05\x10\x00\x00\x00", enc.code(), "lea eax, WORD PTR [rip + 0x10]"); - try enc.encode(.lea, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.rip(.byte, 0x10) } }); + try enc.encode(.lea, &.{ + .{ .reg = .ax }, + .{ .mem = Memory.rip(.byte, 0x10) }, + }); try expectEqualHexStrings("\x66\x8D\x05\x10\x00\x00\x00", enc.code(), "lea ax, BYTE PTR [rip + 0x10]"); - try enc.encode(.lea, .{ - .op1 = .{ .reg = .rsi }, - .op2 = .{ .mem = Memory.sib(.qword, .{ + try enc.encode(.lea, &.{ + .{ .reg = .rsi }, + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .scale_index = .{ .scale = 1, .index = .rcx }, }) }, }); try expectEqualHexStrings("\x48\x8D\x74\x0D\x00", enc.code(), "lea rsi, QWORD PTR [rbp + rcx*1 + 0]"); - try enc.encode(.add, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .ds, - .disp = 0x10000000, - }) } }); + try enc.encode(.add, &.{ + .{ .reg = .r11 }, + .{ .mem = Memory.sib(.qword, .{ .base = .ds, .disp = 0x10000000 }) }, + }); try expectEqualHexStrings("\x4C\x03\x1C\x25\x00\x00\x00\x10", enc.code(), "add r11, QWORD PTR ds:0x10000000"); - try enc.encode(.add, .{ .op1 = .{ .reg = .r12b }, .op2 = .{ .mem = Memory.sib(.byte, .{ - .base = .ds, - .disp = 0x10000000, - }) } }); + try enc.encode(.add, &.{ + .{ .reg = .r12b }, + .{ .mem = Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) }, + }); try expectEqualHexStrings("\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR ds:0x10000000"); - try enc.encode(.add, .{ .op1 = .{ .reg = .r12b }, .op2 = .{ .mem = Memory.sib(.byte, .{ - .base = .fs, - .disp = 0x10000000, - }) } }); + try enc.encode(.add, &.{ + .{ .reg = .r12b }, + .{ .mem = Memory.sib(.byte, .{ .base = .fs, .disp = 0x10000000 }) }, + }); try expectEqualHexStrings("\x64\x44\x02\x24\x25\x00\x00\x00\x10", enc.code(), "add r11b, BYTE PTR fs:0x10000000"); - try enc.encode(.sub, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .r13, - .disp = 0x10000000, - }) } }); + try enc.encode(.sub, &.{ + .{ .reg = .r11 }, + .{ .mem = Memory.sib(.qword, .{ .base = .r13, .disp = 0x10000000 }) }, + }); try expectEqualHexStrings("\x4D\x2B\x9D\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r13 + 0x10000000]"); - try enc.encode(.sub, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .mem = Memory.sib(.qword, .{ - .base = .r12, - .disp = 0x10000000, - }) } }); + try enc.encode(.sub, &.{ + .{ .reg = .r11 }, + .{ .mem = Memory.sib(.qword, .{ .base = .r12, .disp = 0x10000000 }) }, + }); try expectEqualHexStrings("\x4D\x2B\x9C\x24\x00\x00\x00\x10", enc.code(), "sub r11, QWORD PTR [r12 + 0x10000000]"); - try enc.encode(.imul, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .reg = .r12 } }); + try enc.encode(.imul, &.{ + .{ .reg = .r11 }, + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x4D\x0F\xAF\xDC", enc.code(), "mov r11, r12"); } test "lower RMI encoding" { var enc = TestEncode{}; - try enc.encode(.imul, .{ - .op1 = .{ .reg = .r11 }, - .op2 = .{ .reg = .r12 }, - .op3 = .{ .imm = Immediate.s(-2) }, + try enc.encode(.imul, &.{ + .{ .reg = .r11 }, + .{ .reg = .r12 }, + .{ .imm = Immediate.s(-2) }, }); try expectEqualHexStrings("\x4D\x6B\xDC\xFE", enc.code(), "imul r11, r12, -2"); - try enc.encode(.imul, .{ - .op1 = .{ .reg = .r11 }, - .op2 = .{ .mem = Memory.rip(.qword, -16) }, - .op3 = .{ .imm = Immediate.s(-1024) }, + try enc.encode(.imul, &.{ + .{ .reg = .r11 }, + .{ .mem = Memory.rip(.qword, -16) }, + .{ .imm = Immediate.s(-1024) }, }); try expectEqualHexStrings( "\x4C\x69\x1D\xF0\xFF\xFF\xFF\x00\xFC\xFF\xFF", @@ -1212,13 +1277,10 @@ test "lower RMI encoding" { "imul r11, QWORD PTR [rip - 16], -1024", ); - try enc.encode(.imul, .{ - .op1 = .{ .reg = .bx }, - .op2 = .{ .mem = Memory.sib(.word, .{ - .base = .rbp, - .disp = -16, - }) }, - .op3 = .{ .imm = Immediate.s(-1024) }, + try enc.encode(.imul, &.{ + .{ .reg = .bx }, + .{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, + .{ .imm = Immediate.s(-1024) }, }); try expectEqualHexStrings( "\x66\x69\x5D\xF0\x00\xFC", @@ -1226,13 +1288,10 @@ test "lower RMI encoding" { "imul bx, WORD PTR [rbp - 16], -1024", ); - try enc.encode(.imul, .{ - .op1 = .{ .reg = .bx }, - .op2 = .{ .mem = Memory.sib(.word, .{ - .base = .rbp, - .disp = -16, - }) }, - .op3 = .{ .imm = Immediate.u(1024) }, + try enc.encode(.imul, &.{ + .{ .reg = .bx }, + .{ .mem = Memory.sib(.word, .{ .base = .rbp, .disp = -16 }) }, + .{ .imm = Immediate.u(1024) }, }); try expectEqualHexStrings( "\x66\x69\x5D\xF0\x00\x04", @@ -1244,238 +1303,343 @@ test "lower RMI encoding" { test "lower MR encoding" { var enc = TestEncode{}; - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .rbx } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .reg = .rbx }, + }); try expectEqualHexStrings("\x48\x89\xD8", enc.code(), "mov rax, rbx"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .rbp, - .disp = -4, - }) }, .op2 = .{ .reg = .r11 } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .rbp, .disp = -4 }) }, + .{ .reg = .r11 }, + }); try expectEqualHexStrings("\x4c\x89\x5d\xfc", enc.code(), "mov QWORD PTR [rbp - 4], r11"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.rip(.qword, 0x10) }, .op2 = .{ .reg = .r12 } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.rip(.qword, 0x10) }, + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x4C\x89\x25\x10\x00\x00\x00", enc.code(), "mov QWORD PTR [rip + 0x10], r12"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .r11, - .scale_index = .{ - .scale = 2, - .index = .r12, - }, - .disp = 0x10, - }) }, .op2 = .{ .reg = .r13 } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.qword, .{ + .base = .r11, + .scale_index = .{ .scale = 2, .index = .r12 }, + .disp = 0x10, + }) }, + .{ .reg = .r13 }, + }); try expectEqualHexStrings("\x4F\x89\x6C\x63\x10", enc.code(), "mov QWORD PTR [r11 + 2 * r12 + 0x10], r13"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.rip(.word, -0x10) }, .op2 = .{ .reg = .r12w } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.rip(.word, -0x10) }, + .{ .reg = .r12w }, + }); try expectEqualHexStrings("\x66\x44\x89\x25\xF0\xFF\xFF\xFF", enc.code(), "mov WORD PTR [rip - 0x10], r12w"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ - .base = .r11, - .scale_index = .{ - .scale = 2, - .index = .r12, - }, - .disp = 0x10, - }) }, .op2 = .{ .reg = .r13b } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.sib(.byte, .{ + .base = .r11, + .scale_index = .{ .scale = 2, .index = .r12 }, + .disp = 0x10, + }) }, + .{ .reg = .r13b }, + }); try expectEqualHexStrings("\x47\x88\x6C\x63\x10", enc.code(), "mov BYTE PTR [r11 + 2 * r12 + 0x10], r13b"); - try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.byte, .{ - .base = .ds, - .disp = 0x10000000, - }) }, .op2 = .{ .reg = .r12b } }); + try enc.encode(.add, &.{ + .{ .mem = Memory.sib(.byte, .{ .base = .ds, .disp = 0x10000000 }) }, + .{ .reg = .r12b }, + }); try expectEqualHexStrings("\x44\x00\x24\x25\x00\x00\x00\x10", enc.code(), "add BYTE PTR ds:0x10000000, r12b"); - try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .ds, - .disp = 0x10000000, - }) }, .op2 = .{ .reg = .r12d } }); + try enc.encode(.add, &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .ds, .disp = 0x10000000 }) }, + .{ .reg = .r12d }, + }); try expectEqualHexStrings("\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [ds:0x10000000], r12d"); - try enc.encode(.add, .{ .op1 = .{ .mem = Memory.sib(.dword, .{ - .base = .gs, - .disp = 0x10000000, - }) }, .op2 = .{ .reg = .r12d } }); + try enc.encode(.add, &.{ + .{ .mem = Memory.sib(.dword, .{ .base = .gs, .disp = 0x10000000 }) }, + .{ .reg = .r12d }, + }); try expectEqualHexStrings("\x65\x44\x01\x24\x25\x00\x00\x00\x10", enc.code(), "add DWORD PTR [gs:0x10000000], r12d"); - try enc.encode(.sub, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ - .base = .r11, - .disp = 0x10000000, - }) }, .op2 = .{ .reg = .r12 } }); + try enc.encode(.sub, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .r11, .disp = 0x10000000 }) }, + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x4D\x29\xA3\x00\x00\x00\x10", enc.code(), "sub QWORD PTR [r11 + 0x10000000], r12"); } test "lower M encoding" { var enc = TestEncode{}; - try enc.encode(.call, .{ .op1 = .{ .reg = .r12 } }); + try enc.encode(.call, &.{ + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x41\xFF\xD4", enc.code(), "call r12"); - try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ .base = .r12 }) } }); + try enc.encode(.call, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .r12 }) }, + }); try expectEqualHexStrings("\x41\xFF\x14\x24", enc.code(), "call QWORD PTR [r12]"); - try enc.encode(.call, .{ - .op1 = .{ .mem = Memory.sib(.qword, .{ + try enc.encode(.call, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = null, .scale_index = .{ .index = .r11, .scale = 2 }, }) }, }); try expectEqualHexStrings("\x42\xFF\x14\x5D\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r11 * 2]"); - try enc.encode(.call, .{ - .op1 = .{ .mem = Memory.sib(.qword, .{ + try enc.encode(.call, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = null, .scale_index = .{ .index = .r12, .scale = 2 }, }) }, }); try expectEqualHexStrings("\x42\xFF\x14\x65\x00\x00\x00\x00", enc.code(), "call QWORD PTR [r12 * 2]"); - try enc.encode(.call, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ .base = .gs }) } }); + try enc.encode(.call, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .gs }) }, + }); try expectEqualHexStrings("\x65\xFF\x14\x25\x00\x00\x00\x00", enc.code(), "call gs:0x0"); - try enc.encode(.call, .{ .op1 = .{ .imm = Immediate.s(0) } }); + try enc.encode(.call, &.{ + .{ .imm = Immediate.s(0) }, + }); try expectEqualHexStrings("\xE8\x00\x00\x00\x00", enc.code(), "call 0x0"); - try enc.encode(.push, .{ .op1 = .{ .mem = Memory.sib(.qword, .{ .base = .rbp }) } }); + try enc.encode(.push, &.{ + .{ .mem = Memory.sib(.qword, .{ .base = .rbp }) }, + }); try expectEqualHexStrings("\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); - try enc.encode(.push, .{ .op1 = .{ .mem = Memory.sib(.word, .{ .base = .rbp }) } }); + try enc.encode(.push, &.{ + .{ .mem = Memory.sib(.word, .{ .base = .rbp }) }, + }); try expectEqualHexStrings("\x66\xFF\x75\x00", enc.code(), "push QWORD PTR [rbp]"); - try enc.encode(.pop, .{ .op1 = .{ .mem = Memory.rip(.qword, 0) } }); + try enc.encode(.pop, &.{ + .{ .mem = Memory.rip(.qword, 0) }, + }); try expectEqualHexStrings("\x8F\x05\x00\x00\x00\x00", enc.code(), "pop QWORD PTR [rip]"); - try enc.encode(.pop, .{ .op1 = .{ .mem = Memory.rip(.word, 0) } }); + try enc.encode(.pop, &.{ + .{ .mem = Memory.rip(.word, 0) }, + }); try expectEqualHexStrings("\x66\x8F\x05\x00\x00\x00\x00", enc.code(), "pop WORD PTR [rbp]"); - try enc.encode(.imul, .{ .op1 = .{ .reg = .rax } }); + try enc.encode(.imul, &.{ + .{ .reg = .rax }, + }); try expectEqualHexStrings("\x48\xF7\xE8", enc.code(), "imul rax"); - try enc.encode(.imul, .{ .op1 = .{ .reg = .r12 } }); + try enc.encode(.imul, &.{ + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x49\xF7\xEC", enc.code(), "imul r12"); } test "lower O encoding" { var enc = TestEncode{}; - try enc.encode(.push, .{ .op1 = .{ .reg = .rax } }); + try enc.encode(.push, &.{ + .{ .reg = .rax }, + }); try expectEqualHexStrings("\x50", enc.code(), "push rax"); - try enc.encode(.push, .{ .op1 = .{ .reg = .r12w } }); + try enc.encode(.push, &.{ + .{ .reg = .r12w }, + }); try expectEqualHexStrings("\x66\x41\x54", enc.code(), "push r12w"); - try enc.encode(.pop, .{ .op1 = .{ .reg = .r12 } }); + try enc.encode(.pop, &.{ + .{ .reg = .r12 }, + }); try expectEqualHexStrings("\x41\x5c", enc.code(), "pop r12"); } test "lower OI encoding" { var enc = TestEncode{}; - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .imm = Immediate.u(0x1000000000000000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .imm = Immediate.u(0x1000000000000000) }, + }); try expectEqualHexStrings( "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x10", enc.code(), "movabs rax, 0x1000000000000000", ); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r11 }, .op2 = .{ .imm = Immediate.u(0x1000000000000000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r11 }, + .{ .imm = Immediate.u(0x1000000000000000) }, + }); try expectEqualHexStrings( "\x49\xBB\x00\x00\x00\x00\x00\x00\x00\x10", enc.code(), "movabs r11, 0x1000000000000000", ); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r11d }, .op2 = .{ .imm = Immediate.u(0x10000000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r11d }, + .{ .imm = Immediate.u(0x10000000) }, + }); try expectEqualHexStrings("\x41\xBB\x00\x00\x00\x10", enc.code(), "mov r11d, 0x10000000"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r11w }, .op2 = .{ .imm = Immediate.u(0x1000) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r11w }, + .{ .imm = Immediate.u(0x1000) }, + }); try expectEqualHexStrings("\x66\x41\xBB\x00\x10", enc.code(), "mov r11w, 0x1000"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .r11b }, .op2 = .{ .imm = Immediate.u(0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .r11b }, + .{ .imm = Immediate.u(0x10) }, + }); try expectEqualHexStrings("\x41\xB3\x10", enc.code(), "mov r11b, 0x10"); } test "lower FD/TD encoding" { var enc = TestEncode{}; - try enc.encode(.mov, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .mem = Memory.moffs(.cs, 0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .rax }, + .{ .mem = Memory.moffs(.cs, 0x10) }, + }); try expectEqualHexStrings("\x2E\x48\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs rax, cs:0x10"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .eax }, .op2 = .{ .mem = Memory.moffs(.fs, 0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .eax }, + .{ .mem = Memory.moffs(.fs, 0x10) }, + }); try expectEqualHexStrings("\x64\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs eax, fs:0x10"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .ax }, .op2 = .{ .mem = Memory.moffs(.gs, 0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .ax }, + .{ .mem = Memory.moffs(.gs, 0x10) }, + }); try expectEqualHexStrings("\x65\x66\xA1\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ax, gs:0x10"); - try enc.encode(.mov, .{ .op1 = .{ .reg = .al }, .op2 = .{ .mem = Memory.moffs(.ds, 0x10) } }); + try enc.encode(.mov, &.{ + .{ .reg = .al }, + .{ .mem = Memory.moffs(.ds, 0x10) }, + }); try expectEqualHexStrings("\xA0\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs al, ds:0x10"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.cs, 0x10) }, .op2 = .{ .reg = .rax } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.moffs(.cs, 0x10) }, + .{ .reg = .rax }, + }); try expectEqualHexStrings("\x2E\x48\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs cs:0x10, rax"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.fs, 0x10) }, .op2 = .{ .reg = .eax } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.moffs(.fs, 0x10) }, + .{ .reg = .eax }, + }); try expectEqualHexStrings("\x64\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs fs:0x10, eax"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.gs, 0x10) }, .op2 = .{ .reg = .ax } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.moffs(.gs, 0x10) }, + .{ .reg = .ax }, + }); try expectEqualHexStrings("\x65\x66\xA3\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs gs:0x10, ax"); - try enc.encode(.mov, .{ .op1 = .{ .mem = Memory.moffs(.ds, 0x10) }, .op2 = .{ .reg = .al } }); + try enc.encode(.mov, &.{ + .{ .mem = Memory.moffs(.ds, 0x10) }, + .{ .reg = .al }, + }); try expectEqualHexStrings("\xA2\x10\x00\x00\x00\x00\x00\x00\x00", enc.code(), "movabs ds:0x10, al"); } test "lower NP encoding" { var enc = TestEncode{}; - try enc.encode(.int3, .{}); + try enc.encode(.int3, &.{}); try expectEqualHexStrings("\xCC", enc.code(), "int3"); - try enc.encode(.nop, .{}); + try enc.encode(.nop, &.{}); try expectEqualHexStrings("\x90", enc.code(), "nop"); - try enc.encode(.ret, .{}); + try enc.encode(.ret, &.{}); try expectEqualHexStrings("\xC3", enc.code(), "ret"); - try enc.encode(.syscall, .{}); + try enc.encode(.syscall, &.{}); try expectEqualHexStrings("\x0f\x05", enc.code(), "syscall"); } -fn invalidInstruction(mnemonic: Instruction.Mnemonic, args: Instruction.Init) !void { - const err = Instruction.new(mnemonic, args); +fn invalidInstruction(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand) !void { + const err = Instruction.new(.none, mnemonic, ops); try testing.expectError(error.InvalidInstruction, err); } test "invalid instruction" { - try invalidInstruction(.call, .{ .op1 = .{ .reg = .eax } }); - try invalidInstruction(.call, .{ .op1 = .{ .reg = .ax } }); - try invalidInstruction(.call, .{ .op1 = .{ .reg = .al } }); - try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.dword, 0) } }); - try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.word, 0) } }); - try invalidInstruction(.call, .{ .op1 = .{ .mem = Memory.rip(.byte, 0) } }); - try invalidInstruction(.mov, .{ .op1 = .{ .mem = Memory.rip(.word, 0x10) }, .op2 = .{ .reg = .r12 } }); - try invalidInstruction(.lea, .{ .op1 = .{ .reg = .rax }, .op2 = .{ .reg = .rbx } }); - try invalidInstruction(.lea, .{ .op1 = .{ .reg = .al }, .op2 = .{ .mem = Memory.rip(.byte, 0) } }); - try invalidInstruction(.pop, .{ .op1 = .{ .reg = .r12b } }); - try invalidInstruction(.pop, .{ .op1 = .{ .reg = .r12d } }); - try invalidInstruction(.push, .{ .op1 = .{ .reg = .r12b } }); - try invalidInstruction(.push, .{ .op1 = .{ .reg = .r12d } }); - try invalidInstruction(.push, .{ .op1 = .{ .imm = Immediate.u(0x1000000000000000) } }); + try invalidInstruction(.call, &.{ + .{ .reg = .eax }, + }); + try invalidInstruction(.call, &.{ + .{ .reg = .ax }, + }); + try invalidInstruction(.call, &.{ + .{ .reg = .al }, + }); + try invalidInstruction(.call, &.{ + .{ .mem = Memory.rip(.dword, 0) }, + }); + try invalidInstruction(.call, &.{ + .{ .mem = Memory.rip(.word, 0) }, + }); + try invalidInstruction(.call, &.{ + .{ .mem = Memory.rip(.byte, 0) }, + }); + try invalidInstruction(.mov, &.{ + .{ .mem = Memory.rip(.word, 0x10) }, + .{ .reg = .r12 }, + }); + try invalidInstruction(.lea, &.{ + .{ .reg = .rax }, + .{ .reg = .rbx }, + }); + try invalidInstruction(.lea, &.{ + .{ .reg = .al }, + .{ .mem = Memory.rip(.byte, 0) }, + }); + try invalidInstruction(.pop, &.{ + .{ .reg = .r12b }, + }); + try invalidInstruction(.pop, &.{ + .{ .reg = .r12d }, + }); + try invalidInstruction(.push, &.{ + .{ .reg = .r12b }, + }); + try invalidInstruction(.push, &.{ + .{ .reg = .r12d }, + }); + try invalidInstruction(.push, &.{ + .{ .imm = Immediate.u(0x1000000000000000) }, + }); } -fn cannotEncode(mnemonic: Instruction.Mnemonic, args: Instruction.Init) !void { - try testing.expectError(error.CannotEncode, Instruction.new(mnemonic, args)); +fn cannotEncode(mnemonic: Instruction.Mnemonic, ops: []const Instruction.Operand) !void { + try testing.expectError(error.CannotEncode, Instruction.new(.none, mnemonic, ops)); } test "cannot encode" { - try cannotEncode(.@"test", .{ - .op1 = .{ .mem = Memory.sib(.byte, .{ .base = .r12 }) }, - .op2 = .{ .reg = .ah }, + try cannotEncode(.@"test", &.{ + .{ .mem = Memory.sib(.byte, .{ .base = .r12 }) }, + .{ .reg = .ah }, }); - try cannotEncode(.@"test", .{ - .op1 = .{ .reg = .r11b }, - .op2 = .{ .reg = .bh }, + try cannotEncode(.@"test", &.{ + .{ .reg = .r11b }, + .{ .reg = .bh }, }); - try cannotEncode(.mov, .{ - .op1 = .{ .reg = .sil }, - .op2 = .{ .reg = .ah }, + try cannotEncode(.mov, &.{ + .{ .reg = .sil }, + .{ .reg = .ah }, }); } @@ -1645,12 +1809,7 @@ const Assembler = struct { pub fn assemble(as: *Assembler, writer: anytype) !void { while (try as.next()) |parsed_inst| { - const inst = try Instruction.new(parsed_inst.mnemonic, .{ - .op1 = parsed_inst.ops[0], - .op2 = parsed_inst.ops[1], - .op3 = parsed_inst.ops[2], - .op4 = parsed_inst.ops[3], - }); + const inst = try Instruction.new(.none, parsed_inst.mnemonic, &parsed_inst.ops); try inst.encode(writer); } } diff --git a/src/arch/x86_64/encodings.zig b/src/arch/x86_64/encodings.zig index 9683ef991af6..05933b68bba3 100644 --- a/src/arch/x86_64/encodings.zig +++ b/src/arch/x86_64/encodings.zig @@ -6,869 +6,874 @@ const Mode = Encoding.Mode; const modrm_ext = u3; -const Entry = struct { Mnemonic, OpEn, Op, Op, Op, Op, []const u8, modrm_ext, Mode }; +pub const Entry = struct { Mnemonic, OpEn, []const Op, []const u8, modrm_ext, Mode }; // TODO move this into a .zon file when Zig is capable of importing .zon files // zig fmt: off -pub const table = &[_]Entry{ +pub const table = [_]Entry{ // General-purpose - .{ .adc, .zi, .al, .imm8, .none, .none, &.{ 0x14 }, 0, .none }, - .{ .adc, .zi, .ax, .imm16, .none, .none, &.{ 0x15 }, 0, .none }, - .{ .adc, .zi, .eax, .imm32, .none, .none, &.{ 0x15 }, 0, .none }, - .{ .adc, .zi, .rax, .imm32s, .none, .none, &.{ 0x15 }, 0, .long }, - .{ .adc, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 2, .none }, - .{ .adc, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 2, .rex }, - .{ .adc, .mi, .rm16, .imm16, .none, .none, &.{ 0x81 }, 2, .none }, - .{ .adc, .mi, .rm32, .imm32, .none, .none, &.{ 0x81 }, 2, .none }, - .{ .adc, .mi, .rm64, .imm32s, .none, .none, &.{ 0x81 }, 2, .long }, - .{ .adc, .mi, .rm16, .imm8s, .none, .none, &.{ 0x83 }, 2, .none }, - .{ .adc, .mi, .rm32, .imm8s, .none, .none, &.{ 0x83 }, 2, .none }, - .{ .adc, .mi, .rm64, .imm8s, .none, .none, &.{ 0x83 }, 2, .long }, - .{ .adc, .mr, .rm8, .r8, .none, .none, &.{ 0x10 }, 0, .none }, - .{ .adc, .mr, .rm8, .r8, .none, .none, &.{ 0x10 }, 0, .rex }, - .{ .adc, .mr, .rm16, .r16, .none, .none, &.{ 0x11 }, 0, .none }, - .{ .adc, .mr, .rm32, .r32, .none, .none, &.{ 0x11 }, 0, .none }, - .{ .adc, .mr, .rm64, .r64, .none, .none, &.{ 0x11 }, 0, .long }, - .{ .adc, .rm, .r8, .rm8, .none, .none, &.{ 0x12 }, 0, .none }, - .{ .adc, .rm, .r8, .rm8, .none, .none, &.{ 0x12 }, 0, .rex }, - .{ .adc, .rm, .r16, .rm16, .none, .none, &.{ 0x13 }, 0, .none }, - .{ .adc, .rm, .r32, .rm32, .none, .none, &.{ 0x13 }, 0, .none }, - .{ .adc, .rm, .r64, .rm64, .none, .none, &.{ 0x13 }, 0, .long }, - - .{ .add, .zi, .al, .imm8, .none, .none, &.{ 0x04 }, 0, .none }, - .{ .add, .zi, .ax, .imm16, .none, .none, &.{ 0x05 }, 0, .none }, - .{ .add, .zi, .eax, .imm32, .none, .none, &.{ 0x05 }, 0, .none }, - .{ .add, .zi, .rax, .imm32s, .none, .none, &.{ 0x05 }, 0, .long }, - .{ .add, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 0, .none }, - .{ .add, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 0, .rex }, - .{ .add, .mi, .rm16, .imm16, .none, .none, &.{ 0x81 }, 0, .none }, - .{ .add, .mi, .rm32, .imm32, .none, .none, &.{ 0x81 }, 0, .none }, - .{ .add, .mi, .rm64, .imm32s, .none, .none, &.{ 0x81 }, 0, .long }, - .{ .add, .mi, .rm16, .imm8s, .none, .none, &.{ 0x83 }, 0, .none }, - .{ .add, .mi, .rm32, .imm8s, .none, .none, &.{ 0x83 }, 0, .none }, - .{ .add, .mi, .rm64, .imm8s, .none, .none, &.{ 0x83 }, 0, .long }, - .{ .add, .mr, .rm8, .r8, .none, .none, &.{ 0x00 }, 0, .none }, - .{ .add, .mr, .rm8, .r8, .none, .none, &.{ 0x00 }, 0, .rex }, - .{ .add, .mr, .rm16, .r16, .none, .none, &.{ 0x01 }, 0, .none }, - .{ .add, .mr, .rm32, .r32, .none, .none, &.{ 0x01 }, 0, .none }, - .{ .add, .mr, .rm64, .r64, .none, .none, &.{ 0x01 }, 0, .long }, - .{ .add, .rm, .r8, .rm8, .none, .none, &.{ 0x02 }, 0, .none }, - .{ .add, .rm, .r8, .rm8, .none, .none, &.{ 0x02 }, 0, .rex }, - .{ .add, .rm, .r16, .rm16, .none, .none, &.{ 0x03 }, 0, .none }, - .{ .add, .rm, .r32, .rm32, .none, .none, &.{ 0x03 }, 0, .none }, - .{ .add, .rm, .r64, .rm64, .none, .none, &.{ 0x03 }, 0, .long }, - - .{ .@"and", .zi, .al, .imm8, .none, .none, &.{ 0x24 }, 0, .none }, - .{ .@"and", .zi, .ax, .imm16, .none, .none, &.{ 0x25 }, 0, .none }, - .{ .@"and", .zi, .eax, .imm32, .none, .none, &.{ 0x25 }, 0, .none }, - .{ .@"and", .zi, .rax, .imm32s, .none, .none, &.{ 0x25 }, 0, .long }, - .{ .@"and", .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 4, .none }, - .{ .@"and", .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 4, .rex }, - .{ .@"and", .mi, .rm16, .imm16, .none, .none, &.{ 0x81 }, 4, .none }, - .{ .@"and", .mi, .rm32, .imm32, .none, .none, &.{ 0x81 }, 4, .none }, - .{ .@"and", .mi, .rm64, .imm32s, .none, .none, &.{ 0x81 }, 4, .long }, - .{ .@"and", .mi, .rm16, .imm8s, .none, .none, &.{ 0x83 }, 4, .none }, - .{ .@"and", .mi, .rm32, .imm8s, .none, .none, &.{ 0x83 }, 4, .none }, - .{ .@"and", .mi, .rm64, .imm8s, .none, .none, &.{ 0x83 }, 4, .long }, - .{ .@"and", .mr, .rm8, .r8, .none, .none, &.{ 0x20 }, 0, .none }, - .{ .@"and", .mr, .rm8, .r8, .none, .none, &.{ 0x20 }, 0, .rex }, - .{ .@"and", .mr, .rm16, .r16, .none, .none, &.{ 0x21 }, 0, .none }, - .{ .@"and", .mr, .rm32, .r32, .none, .none, &.{ 0x21 }, 0, .none }, - .{ .@"and", .mr, .rm64, .r64, .none, .none, &.{ 0x21 }, 0, .long }, - .{ .@"and", .rm, .r8, .rm8, .none, .none, &.{ 0x22 }, 0, .none }, - .{ .@"and", .rm, .r8, .rm8, .none, .none, &.{ 0x22 }, 0, .rex }, - .{ .@"and", .rm, .r16, .rm16, .none, .none, &.{ 0x23 }, 0, .none }, - .{ .@"and", .rm, .r32, .rm32, .none, .none, &.{ 0x23 }, 0, .none }, - .{ .@"and", .rm, .r64, .rm64, .none, .none, &.{ 0x23 }, 0, .long }, - - .{ .bsf, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0xbc }, 0, .none }, - .{ .bsf, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0xbc }, 0, .none }, - .{ .bsf, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0xbc }, 0, .long }, - - .{ .bsr, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0xbd }, 0, .none }, - .{ .bsr, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0xbd }, 0, .none }, - .{ .bsr, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0xbd }, 0, .long }, - - .{ .bswap, .o, .r32, .none, .none, .none, &.{ 0x0f, 0xc8 }, 0, .none }, - .{ .bswap, .o, .r64, .none, .none, .none, &.{ 0x0f, 0xc8 }, 0, .long }, - - .{ .bt, .mr, .rm16, .r16, .none, .none, &.{ 0x0f, 0xa3 }, 0, .none }, - .{ .bt, .mr, .rm32, .r32, .none, .none, &.{ 0x0f, 0xa3 }, 0, .none }, - .{ .bt, .mr, .rm64, .r64, .none, .none, &.{ 0x0f, 0xa3 }, 0, .long }, - .{ .bt, .mi, .rm16, .imm8, .none, .none, &.{ 0x0f, 0xba }, 4, .none }, - .{ .bt, .mi, .rm32, .imm8, .none, .none, &.{ 0x0f, 0xba }, 4, .none }, - .{ .bt, .mi, .rm64, .imm8, .none, .none, &.{ 0x0f, 0xba }, 4, .long }, - - .{ .btc, .mr, .rm16, .r16, .none, .none, &.{ 0x0f, 0xbb }, 0, .none }, - .{ .btc, .mr, .rm32, .r32, .none, .none, &.{ 0x0f, 0xbb }, 0, .none }, - .{ .btc, .mr, .rm64, .r64, .none, .none, &.{ 0x0f, 0xbb }, 0, .long }, - .{ .btc, .mi, .rm16, .imm8, .none, .none, &.{ 0x0f, 0xba }, 7, .none }, - .{ .btc, .mi, .rm32, .imm8, .none, .none, &.{ 0x0f, 0xba }, 7, .none }, - .{ .btc, .mi, .rm64, .imm8, .none, .none, &.{ 0x0f, 0xba }, 7, .long }, - - .{ .btr, .mr, .rm16, .r16, .none, .none, &.{ 0x0f, 0xb3 }, 0, .none }, - .{ .btr, .mr, .rm32, .r32, .none, .none, &.{ 0x0f, 0xb3 }, 0, .none }, - .{ .btr, .mr, .rm64, .r64, .none, .none, &.{ 0x0f, 0xb3 }, 0, .long }, - .{ .btr, .mi, .rm16, .imm8, .none, .none, &.{ 0x0f, 0xba }, 6, .none }, - .{ .btr, .mi, .rm32, .imm8, .none, .none, &.{ 0x0f, 0xba }, 6, .none }, - .{ .btr, .mi, .rm64, .imm8, .none, .none, &.{ 0x0f, 0xba }, 6, .long }, - - .{ .bts, .mr, .rm16, .r16, .none, .none, &.{ 0x0f, 0xab }, 0, .none }, - .{ .bts, .mr, .rm32, .r32, .none, .none, &.{ 0x0f, 0xab }, 0, .none }, - .{ .bts, .mr, .rm64, .r64, .none, .none, &.{ 0x0f, 0xab }, 0, .long }, - .{ .bts, .mi, .rm16, .imm8, .none, .none, &.{ 0x0f, 0xba }, 5, .none }, - .{ .bts, .mi, .rm32, .imm8, .none, .none, &.{ 0x0f, 0xba }, 5, .none }, - .{ .bts, .mi, .rm64, .imm8, .none, .none, &.{ 0x0f, 0xba }, 5, .long }, + .{ .adc, .zi, &.{ .al, .imm8 }, &.{ 0x14 }, 0, .none }, + .{ .adc, .zi, &.{ .ax, .imm16 }, &.{ 0x15 }, 0, .none }, + .{ .adc, .zi, &.{ .eax, .imm32 }, &.{ 0x15 }, 0, .none }, + .{ .adc, .zi, &.{ .rax, .imm32s }, &.{ 0x15 }, 0, .long }, + .{ .adc, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 2, .none }, + .{ .adc, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 2, .rex }, + .{ .adc, .mi, &.{ .rm16, .imm16 }, &.{ 0x81 }, 2, .none }, + .{ .adc, .mi, &.{ .rm32, .imm32 }, &.{ 0x81 }, 2, .none }, + .{ .adc, .mi, &.{ .rm64, .imm32s }, &.{ 0x81 }, 2, .long }, + .{ .adc, .mi, &.{ .rm16, .imm8s }, &.{ 0x83 }, 2, .none }, + .{ .adc, .mi, &.{ .rm32, .imm8s }, &.{ 0x83 }, 2, .none }, + .{ .adc, .mi, &.{ .rm64, .imm8s }, &.{ 0x83 }, 2, .long }, + .{ .adc, .mr, &.{ .rm8, .r8 }, &.{ 0x10 }, 0, .none }, + .{ .adc, .mr, &.{ .rm8, .r8 }, &.{ 0x10 }, 0, .rex }, + .{ .adc, .mr, &.{ .rm16, .r16 }, &.{ 0x11 }, 0, .none }, + .{ .adc, .mr, &.{ .rm32, .r32 }, &.{ 0x11 }, 0, .none }, + .{ .adc, .mr, &.{ .rm64, .r64 }, &.{ 0x11 }, 0, .long }, + .{ .adc, .rm, &.{ .r8, .rm8 }, &.{ 0x12 }, 0, .none }, + .{ .adc, .rm, &.{ .r8, .rm8 }, &.{ 0x12 }, 0, .rex }, + .{ .adc, .rm, &.{ .r16, .rm16 }, &.{ 0x13 }, 0, .none }, + .{ .adc, .rm, &.{ .r32, .rm32 }, &.{ 0x13 }, 0, .none }, + .{ .adc, .rm, &.{ .r64, .rm64 }, &.{ 0x13 }, 0, .long }, + + .{ .add, .zi, &.{ .al, .imm8 }, &.{ 0x04 }, 0, .none }, + .{ .add, .zi, &.{ .ax, .imm16 }, &.{ 0x05 }, 0, .none }, + .{ .add, .zi, &.{ .eax, .imm32 }, &.{ 0x05 }, 0, .none }, + .{ .add, .zi, &.{ .rax, .imm32s }, &.{ 0x05 }, 0, .long }, + .{ .add, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 0, .none }, + .{ .add, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 0, .rex }, + .{ .add, .mi, &.{ .rm16, .imm16 }, &.{ 0x81 }, 0, .none }, + .{ .add, .mi, &.{ .rm32, .imm32 }, &.{ 0x81 }, 0, .none }, + .{ .add, .mi, &.{ .rm64, .imm32s }, &.{ 0x81 }, 0, .long }, + .{ .add, .mi, &.{ .rm16, .imm8s }, &.{ 0x83 }, 0, .none }, + .{ .add, .mi, &.{ .rm32, .imm8s }, &.{ 0x83 }, 0, .none }, + .{ .add, .mi, &.{ .rm64, .imm8s }, &.{ 0x83 }, 0, .long }, + .{ .add, .mr, &.{ .rm8, .r8 }, &.{ 0x00 }, 0, .none }, + .{ .add, .mr, &.{ .rm8, .r8 }, &.{ 0x00 }, 0, .rex }, + .{ .add, .mr, &.{ .rm16, .r16 }, &.{ 0x01 }, 0, .none }, + .{ .add, .mr, &.{ .rm32, .r32 }, &.{ 0x01 }, 0, .none }, + .{ .add, .mr, &.{ .rm64, .r64 }, &.{ 0x01 }, 0, .long }, + .{ .add, .rm, &.{ .r8, .rm8 }, &.{ 0x02 }, 0, .none }, + .{ .add, .rm, &.{ .r8, .rm8 }, &.{ 0x02 }, 0, .rex }, + .{ .add, .rm, &.{ .r16, .rm16 }, &.{ 0x03 }, 0, .none }, + .{ .add, .rm, &.{ .r32, .rm32 }, &.{ 0x03 }, 0, .none }, + .{ .add, .rm, &.{ .r64, .rm64 }, &.{ 0x03 }, 0, .long }, + + .{ .@"and", .zi, &.{ .al, .imm8 }, &.{ 0x24 }, 0, .none }, + .{ .@"and", .zi, &.{ .ax, .imm16 }, &.{ 0x25 }, 0, .none }, + .{ .@"and", .zi, &.{ .eax, .imm32 }, &.{ 0x25 }, 0, .none }, + .{ .@"and", .zi, &.{ .rax, .imm32s }, &.{ 0x25 }, 0, .long }, + .{ .@"and", .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 4, .none }, + .{ .@"and", .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 4, .rex }, + .{ .@"and", .mi, &.{ .rm16, .imm16 }, &.{ 0x81 }, 4, .none }, + .{ .@"and", .mi, &.{ .rm32, .imm32 }, &.{ 0x81 }, 4, .none }, + .{ .@"and", .mi, &.{ .rm64, .imm32s }, &.{ 0x81 }, 4, .long }, + .{ .@"and", .mi, &.{ .rm16, .imm8s }, &.{ 0x83 }, 4, .none }, + .{ .@"and", .mi, &.{ .rm32, .imm8s }, &.{ 0x83 }, 4, .none }, + .{ .@"and", .mi, &.{ .rm64, .imm8s }, &.{ 0x83 }, 4, .long }, + .{ .@"and", .mr, &.{ .rm8, .r8 }, &.{ 0x20 }, 0, .none }, + .{ .@"and", .mr, &.{ .rm8, .r8 }, &.{ 0x20 }, 0, .rex }, + .{ .@"and", .mr, &.{ .rm16, .r16 }, &.{ 0x21 }, 0, .none }, + .{ .@"and", .mr, &.{ .rm32, .r32 }, &.{ 0x21 }, 0, .none }, + .{ .@"and", .mr, &.{ .rm64, .r64 }, &.{ 0x21 }, 0, .long }, + .{ .@"and", .rm, &.{ .r8, .rm8 }, &.{ 0x22 }, 0, .none }, + .{ .@"and", .rm, &.{ .r8, .rm8 }, &.{ 0x22 }, 0, .rex }, + .{ .@"and", .rm, &.{ .r16, .rm16 }, &.{ 0x23 }, 0, .none }, + .{ .@"and", .rm, &.{ .r32, .rm32 }, &.{ 0x23 }, 0, .none }, + .{ .@"and", .rm, &.{ .r64, .rm64 }, &.{ 0x23 }, 0, .long }, + + .{ .bsf, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0xbc }, 0, .none }, + .{ .bsf, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0xbc }, 0, .none }, + .{ .bsf, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0xbc }, 0, .long }, + + .{ .bsr, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0xbd }, 0, .none }, + .{ .bsr, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0xbd }, 0, .none }, + .{ .bsr, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0xbd }, 0, .long }, + + .{ .bswap, .o, &.{ .r32 }, &.{ 0x0f, 0xc8 }, 0, .none }, + .{ .bswap, .o, &.{ .r64 }, &.{ 0x0f, 0xc8 }, 0, .long }, + + .{ .bt, .mr, &.{ .rm16, .r16 }, &.{ 0x0f, 0xa3 }, 0, .none }, + .{ .bt, .mr, &.{ .rm32, .r32 }, &.{ 0x0f, 0xa3 }, 0, .none }, + .{ .bt, .mr, &.{ .rm64, .r64 }, &.{ 0x0f, 0xa3 }, 0, .long }, + .{ .bt, .mi, &.{ .rm16, .imm8 }, &.{ 0x0f, 0xba }, 4, .none }, + .{ .bt, .mi, &.{ .rm32, .imm8 }, &.{ 0x0f, 0xba }, 4, .none }, + .{ .bt, .mi, &.{ .rm64, .imm8 }, &.{ 0x0f, 0xba }, 4, .long }, + + .{ .btc, .mr, &.{ .rm16, .r16 }, &.{ 0x0f, 0xbb }, 0, .none }, + .{ .btc, .mr, &.{ .rm32, .r32 }, &.{ 0x0f, 0xbb }, 0, .none }, + .{ .btc, .mr, &.{ .rm64, .r64 }, &.{ 0x0f, 0xbb }, 0, .long }, + .{ .btc, .mi, &.{ .rm16, .imm8 }, &.{ 0x0f, 0xba }, 7, .none }, + .{ .btc, .mi, &.{ .rm32, .imm8 }, &.{ 0x0f, 0xba }, 7, .none }, + .{ .btc, .mi, &.{ .rm64, .imm8 }, &.{ 0x0f, 0xba }, 7, .long }, + + .{ .btr, .mr, &.{ .rm16, .r16 }, &.{ 0x0f, 0xb3 }, 0, .none }, + .{ .btr, .mr, &.{ .rm32, .r32 }, &.{ 0x0f, 0xb3 }, 0, .none }, + .{ .btr, .mr, &.{ .rm64, .r64 }, &.{ 0x0f, 0xb3 }, 0, .long }, + .{ .btr, .mi, &.{ .rm16, .imm8 }, &.{ 0x0f, 0xba }, 6, .none }, + .{ .btr, .mi, &.{ .rm32, .imm8 }, &.{ 0x0f, 0xba }, 6, .none }, + .{ .btr, .mi, &.{ .rm64, .imm8 }, &.{ 0x0f, 0xba }, 6, .long }, + + .{ .bts, .mr, &.{ .rm16, .r16 }, &.{ 0x0f, 0xab }, 0, .none }, + .{ .bts, .mr, &.{ .rm32, .r32 }, &.{ 0x0f, 0xab }, 0, .none }, + .{ .bts, .mr, &.{ .rm64, .r64 }, &.{ 0x0f, 0xab }, 0, .long }, + .{ .bts, .mi, &.{ .rm16, .imm8 }, &.{ 0x0f, 0xba }, 5, .none }, + .{ .bts, .mi, &.{ .rm32, .imm8 }, &.{ 0x0f, 0xba }, 5, .none }, + .{ .bts, .mi, &.{ .rm64, .imm8 }, &.{ 0x0f, 0xba }, 5, .long }, // This is M encoding according to Intel, but D makes more sense here. - .{ .call, .d, .rel32, .none, .none, .none, &.{ 0xe8 }, 0, .none }, - .{ .call, .m, .rm64, .none, .none, .none, &.{ 0xff }, 2, .none }, - - .{ .cbw, .np, .o16, .none, .none, .none, &.{ 0x98 }, 0, .none }, - .{ .cwde, .np, .o32, .none, .none, .none, &.{ 0x98 }, 0, .none }, - .{ .cdqe, .np, .o64, .none, .none, .none, &.{ 0x98 }, 0, .long }, - - .{ .cwd, .np, .o16, .none, .none, .none, &.{ 0x99 }, 0, .none }, - .{ .cdq, .np, .o32, .none, .none, .none, &.{ 0x99 }, 0, .none }, - .{ .cqo, .np, .o64, .none, .none, .none, &.{ 0x99 }, 0, .long }, - - .{ .cmova, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x47 }, 0, .none }, - .{ .cmova, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x47 }, 0, .none }, - .{ .cmova, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x47 }, 0, .long }, - .{ .cmovae, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x43 }, 0, .none }, - .{ .cmovae, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x43 }, 0, .none }, - .{ .cmovae, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x43 }, 0, .long }, - .{ .cmovb, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x42 }, 0, .none }, - .{ .cmovb, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x42 }, 0, .none }, - .{ .cmovb, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x42 }, 0, .long }, - .{ .cmovbe, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x46 }, 0, .none }, - .{ .cmovbe, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x46 }, 0, .none }, - .{ .cmovbe, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x46 }, 0, .long }, - .{ .cmovc, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x42 }, 0, .none }, - .{ .cmovc, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x42 }, 0, .none }, - .{ .cmovc, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x42 }, 0, .long }, - .{ .cmove, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x44 }, 0, .none }, - .{ .cmove, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x44 }, 0, .none }, - .{ .cmove, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x44 }, 0, .long }, - .{ .cmovg, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4f }, 0, .none }, - .{ .cmovg, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4f }, 0, .none }, - .{ .cmovg, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4f }, 0, .long }, - .{ .cmovge, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4d }, 0, .none }, - .{ .cmovge, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4d }, 0, .none }, - .{ .cmovge, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4d }, 0, .long }, - .{ .cmovl, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4c }, 0, .none }, - .{ .cmovl, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4c }, 0, .none }, - .{ .cmovl, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4c }, 0, .long }, - .{ .cmovle, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4e }, 0, .none }, - .{ .cmovle, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4e }, 0, .none }, - .{ .cmovle, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4e }, 0, .long }, - .{ .cmovna, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x46 }, 0, .none }, - .{ .cmovna, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x46 }, 0, .none }, - .{ .cmovna, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x46 }, 0, .long }, - .{ .cmovnae, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x42 }, 0, .none }, - .{ .cmovnae, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x42 }, 0, .none }, - .{ .cmovnae, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x42 }, 0, .long }, - .{ .cmovnb, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x43 }, 0, .none }, - .{ .cmovnb, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x43 }, 0, .none }, - .{ .cmovnb, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x43 }, 0, .long }, - .{ .cmovnbe, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x47 }, 0, .none }, - .{ .cmovnbe, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x47 }, 0, .none }, - .{ .cmovnbe, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x47 }, 0, .long }, - .{ .cmovnc, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x43 }, 0, .none }, - .{ .cmovnc, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x43 }, 0, .none }, - .{ .cmovnc, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x43 }, 0, .long }, - .{ .cmovne, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x45 }, 0, .none }, - .{ .cmovne, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x45 }, 0, .none }, - .{ .cmovne, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x45 }, 0, .long }, - .{ .cmovng, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4e }, 0, .none }, - .{ .cmovng, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4e }, 0, .none }, - .{ .cmovng, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4e }, 0, .long }, - .{ .cmovnge, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4c }, 0, .none }, - .{ .cmovnge, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4c }, 0, .none }, - .{ .cmovnge, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4c }, 0, .long }, - .{ .cmovnl, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4d }, 0, .none }, - .{ .cmovnl, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4d }, 0, .none }, - .{ .cmovnl, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4d }, 0, .long }, - .{ .cmovnle, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4f }, 0, .none }, - .{ .cmovnle, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4f }, 0, .none }, - .{ .cmovnle, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4f }, 0, .long }, - .{ .cmovno, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x41 }, 0, .none }, - .{ .cmovno, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x41 }, 0, .none }, - .{ .cmovno, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x41 }, 0, .long }, - .{ .cmovnp, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4b }, 0, .none }, - .{ .cmovnp, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4b }, 0, .none }, - .{ .cmovnp, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4b }, 0, .long }, - .{ .cmovns, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x49 }, 0, .none }, - .{ .cmovns, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x49 }, 0, .none }, - .{ .cmovns, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x49 }, 0, .long }, - .{ .cmovnz, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x45 }, 0, .none }, - .{ .cmovnz, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x45 }, 0, .none }, - .{ .cmovnz, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x45 }, 0, .long }, - .{ .cmovo, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x40 }, 0, .none }, - .{ .cmovo, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x40 }, 0, .none }, - .{ .cmovo, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x40 }, 0, .long }, - .{ .cmovp, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4a }, 0, .none }, - .{ .cmovp, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4a }, 0, .none }, - .{ .cmovp, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4a }, 0, .long }, - .{ .cmovpe, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4a }, 0, .none }, - .{ .cmovpe, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4a }, 0, .none }, - .{ .cmovpe, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4a }, 0, .long }, - .{ .cmovpo, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x4b }, 0, .none }, - .{ .cmovpo, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x4b }, 0, .none }, - .{ .cmovpo, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x4b }, 0, .long }, - .{ .cmovs, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x48 }, 0, .none }, - .{ .cmovs, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x48 }, 0, .none }, - .{ .cmovs, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x48 }, 0, .long }, - .{ .cmovz, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0x44 }, 0, .none }, - .{ .cmovz, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0x44 }, 0, .none }, - .{ .cmovz, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0x44 }, 0, .long }, - - .{ .cmp, .zi, .al, .imm8, .none, .none, &.{ 0x3c }, 0, .none }, - .{ .cmp, .zi, .ax, .imm16, .none, .none, &.{ 0x3d }, 0, .none }, - .{ .cmp, .zi, .eax, .imm32, .none, .none, &.{ 0x3d }, 0, .none }, - .{ .cmp, .zi, .rax, .imm32s, .none, .none, &.{ 0x3d }, 0, .long }, - .{ .cmp, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 7, .none }, - .{ .cmp, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 7, .rex }, - .{ .cmp, .mi, .rm16, .imm16, .none, .none, &.{ 0x81 }, 7, .none }, - .{ .cmp, .mi, .rm32, .imm32, .none, .none, &.{ 0x81 }, 7, .none }, - .{ .cmp, .mi, .rm64, .imm32s, .none, .none, &.{ 0x81 }, 7, .long }, - .{ .cmp, .mi, .rm16, .imm8s, .none, .none, &.{ 0x83 }, 7, .none }, - .{ .cmp, .mi, .rm32, .imm8s, .none, .none, &.{ 0x83 }, 7, .none }, - .{ .cmp, .mi, .rm64, .imm8s, .none, .none, &.{ 0x83 }, 7, .long }, - .{ .cmp, .mr, .rm8, .r8, .none, .none, &.{ 0x38 }, 0, .none }, - .{ .cmp, .mr, .rm8, .r8, .none, .none, &.{ 0x38 }, 0, .rex }, - .{ .cmp, .mr, .rm16, .r16, .none, .none, &.{ 0x39 }, 0, .none }, - .{ .cmp, .mr, .rm32, .r32, .none, .none, &.{ 0x39 }, 0, .none }, - .{ .cmp, .mr, .rm64, .r64, .none, .none, &.{ 0x39 }, 0, .long }, - .{ .cmp, .rm, .r8, .rm8, .none, .none, &.{ 0x3a }, 0, .none }, - .{ .cmp, .rm, .r8, .rm8, .none, .none, &.{ 0x3a }, 0, .rex }, - .{ .cmp, .rm, .r16, .rm16, .none, .none, &.{ 0x3b }, 0, .none }, - .{ .cmp, .rm, .r32, .rm32, .none, .none, &.{ 0x3b }, 0, .none }, - .{ .cmp, .rm, .r64, .rm64, .none, .none, &.{ 0x3b }, 0, .long }, - - .{ .cmps, .np, .m8, .m8, .none, .none, &.{ 0xa6 }, 0, .none }, - .{ .cmps, .np, .m16, .m16, .none, .none, &.{ 0xa7 }, 0, .none }, - .{ .cmps, .np, .m32, .m32, .none, .none, &.{ 0xa7 }, 0, .none }, - .{ .cmps, .np, .m64, .m64, .none, .none, &.{ 0xa7 }, 0, .long }, - .{ .cmpsb, .np, .none, .none, .none, .none, &.{ 0xa6 }, 0, .none }, - .{ .cmpsw, .np, .none, .none, .none, .none, &.{ 0xa7 }, 0, .short }, - .{ .cmpsd, .np, .none, .none, .none, .none, &.{ 0xa7 }, 0, .none }, - .{ .cmpsq, .np, .none, .none, .none, .none, &.{ 0xa7 }, 0, .long }, - - .{ .cmpxchg, .mr, .rm8, .r8, .none, .none, &.{ 0x0f, 0xb0 }, 0, .none }, - .{ .cmpxchg, .mr, .rm8, .r8, .none, .none, &.{ 0x0f, 0xb0 }, 0, .rex }, - .{ .cmpxchg, .mr, .rm16, .r16, .none, .none, &.{ 0x0f, 0xb1 }, 0, .none }, - .{ .cmpxchg, .mr, .rm32, .r32, .none, .none, &.{ 0x0f, 0xb1 }, 0, .none }, - .{ .cmpxchg, .mr, .rm64, .r64, .none, .none, &.{ 0x0f, 0xb1 }, 0, .long }, - - .{ .cmpxchg8b , .m, .m64, .none, .none, .none, &.{ 0x0f, 0xc7 }, 1, .none }, - .{ .cmpxchg16b, .m, .m128, .none, .none, .none, &.{ 0x0f, 0xc7 }, 1, .long }, - - .{ .div, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 6, .none }, - .{ .div, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 6, .rex }, - .{ .div, .m, .rm16, .none, .none, .none, &.{ 0xf7 }, 6, .none }, - .{ .div, .m, .rm32, .none, .none, .none, &.{ 0xf7 }, 6, .none }, - .{ .div, .m, .rm64, .none, .none, .none, &.{ 0xf7 }, 6, .long }, - - .{ .fisttp, .m, .m16, .none, .none, .none, &.{ 0xdf }, 1, .fpu }, - .{ .fisttp, .m, .m32, .none, .none, .none, &.{ 0xdb }, 1, .fpu }, - .{ .fisttp, .m, .m64, .none, .none, .none, &.{ 0xdd }, 1, .fpu }, - - .{ .fld, .m, .m32, .none, .none, .none, &.{ 0xd9 }, 0, .fpu }, - .{ .fld, .m, .m64, .none, .none, .none, &.{ 0xdd }, 0, .fpu }, - .{ .fld, .m, .m80, .none, .none, .none, &.{ 0xdb }, 5, .fpu }, - - .{ .idiv, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 7, .none }, - .{ .idiv, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 7, .rex }, - .{ .idiv, .m, .rm16, .none, .none, .none, &.{ 0xf7 }, 7, .none }, - .{ .idiv, .m, .rm32, .none, .none, .none, &.{ 0xf7 }, 7, .none }, - .{ .idiv, .m, .rm64, .none, .none, .none, &.{ 0xf7 }, 7, .long }, - - .{ .imul, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 5, .none }, - .{ .imul, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 5, .rex }, - .{ .imul, .m, .rm16, .none, .none, .none, &.{ 0xf7 }, 5, .none }, - .{ .imul, .m, .rm32, .none, .none, .none, &.{ 0xf7 }, 5, .none }, - .{ .imul, .m, .rm64, .none, .none, .none, &.{ 0xf7 }, 5, .long }, - .{ .imul, .rm, .r16, .rm16, .none, .none, &.{ 0x0f, 0xaf }, 0, .none }, - .{ .imul, .rm, .r32, .rm32, .none, .none, &.{ 0x0f, 0xaf }, 0, .none }, - .{ .imul, .rm, .r64, .rm64, .none, .none, &.{ 0x0f, 0xaf }, 0, .long }, - .{ .imul, .rmi, .r16, .rm16, .imm8s, .none, &.{ 0x6b }, 0, .none }, - .{ .imul, .rmi, .r32, .rm32, .imm8s, .none, &.{ 0x6b }, 0, .none }, - .{ .imul, .rmi, .r64, .rm64, .imm8s, .none, &.{ 0x6b }, 0, .long }, - .{ .imul, .rmi, .r16, .rm16, .imm16, .none, &.{ 0x69 }, 0, .none }, - .{ .imul, .rmi, .r32, .rm32, .imm32, .none, &.{ 0x69 }, 0, .none }, - .{ .imul, .rmi, .r64, .rm64, .imm32, .none, &.{ 0x69 }, 0, .long }, - - .{ .int3, .np, .none, .none, .none, .none, &.{ 0xcc }, 0, .none }, - - .{ .ja, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x87 }, 0, .none }, - .{ .jae, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x83 }, 0, .none }, - .{ .jb, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x82 }, 0, .none }, - .{ .jbe, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x86 }, 0, .none }, - .{ .jc, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x82 }, 0, .none }, - .{ .jrcxz, .d, .rel32, .none, .none, .none, &.{ 0xe3 }, 0, .none }, - .{ .je, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x84 }, 0, .none }, - .{ .jg, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8f }, 0, .none }, - .{ .jge, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8d }, 0, .none }, - .{ .jl, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8c }, 0, .none }, - .{ .jle, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8e }, 0, .none }, - .{ .jna, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x86 }, 0, .none }, - .{ .jnae, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x82 }, 0, .none }, - .{ .jnb, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x83 }, 0, .none }, - .{ .jnbe, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x87 }, 0, .none }, - .{ .jnc, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x83 }, 0, .none }, - .{ .jne, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x85 }, 0, .none }, - .{ .jng, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8e }, 0, .none }, - .{ .jnge, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8c }, 0, .none }, - .{ .jnl, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8d }, 0, .none }, - .{ .jnle, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8f }, 0, .none }, - .{ .jno, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x81 }, 0, .none }, - .{ .jnp, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8b }, 0, .none }, - .{ .jns, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x89 }, 0, .none }, - .{ .jnz, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x85 }, 0, .none }, - .{ .jo, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x80 }, 0, .none }, - .{ .jp, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8a }, 0, .none }, - .{ .jpe, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8a }, 0, .none }, - .{ .jpo, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x8b }, 0, .none }, - .{ .js, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x88 }, 0, .none }, - .{ .jz, .d, .rel32, .none, .none, .none, &.{ 0x0f, 0x84 }, 0, .none }, - - .{ .jmp, .d, .rel32, .none, .none, .none, &.{ 0xe9 }, 0, .none }, - .{ .jmp, .m, .rm64, .none, .none, .none, &.{ 0xff }, 4, .none }, - - .{ .lea, .rm, .r16, .m, .none, .none, &.{ 0x8d }, 0, .none }, - .{ .lea, .rm, .r32, .m, .none, .none, &.{ 0x8d }, 0, .none }, - .{ .lea, .rm, .r64, .m, .none, .none, &.{ 0x8d }, 0, .long }, - - .{ .lfence, .np, .none, .none, .none, .none, &.{ 0x0f, 0xae, 0xe8 }, 0, .none }, - - .{ .lods, .np, .m8, .none, .none, .none, &.{ 0xac }, 0, .none }, - .{ .lods, .np, .m16, .none, .none, .none, &.{ 0xad }, 0, .none }, - .{ .lods, .np, .m32, .none, .none, .none, &.{ 0xad }, 0, .none }, - .{ .lods, .np, .m64, .none, .none, .none, &.{ 0xad }, 0, .long }, - .{ .lodsb, .np, .none, .none, .none, .none, &.{ 0xac }, 0, .none }, - .{ .lodsw, .np, .none, .none, .none, .none, &.{ 0xad }, 0, .short }, - .{ .lodsd, .np, .none, .none, .none, .none, &.{ 0xad }, 0, .none }, - .{ .lodsq, .np, .none, .none, .none, .none, &.{ 0xad }, 0, .long }, - - .{ .lzcnt, .rm, .r16, .rm16, .none, .none, &.{ 0xf3, 0x0f, 0xbd }, 0, .none }, - .{ .lzcnt, .rm, .r32, .rm32, .none, .none, &.{ 0xf3, 0x0f, 0xbd }, 0, .none }, - .{ .lzcnt, .rm, .r64, .rm64, .none, .none, &.{ 0xf3, 0x0f, 0xbd }, 0, .long }, - - .{ .mfence, .np, .none, .none, .none, .none, &.{ 0x0f, 0xae, 0xf0 }, 0, .none }, - - .{ .mov, .mr, .rm8, .r8, .none, .none, &.{ 0x88 }, 0, .none }, - .{ .mov, .mr, .rm8, .r8, .none, .none, &.{ 0x88 }, 0, .rex }, - .{ .mov, .mr, .rm16, .r16, .none, .none, &.{ 0x89 }, 0, .none }, - .{ .mov, .mr, .rm32, .r32, .none, .none, &.{ 0x89 }, 0, .none }, - .{ .mov, .mr, .rm64, .r64, .none, .none, &.{ 0x89 }, 0, .long }, - .{ .mov, .rm, .r8, .rm8, .none, .none, &.{ 0x8a }, 0, .none }, - .{ .mov, .rm, .r8, .rm8, .none, .none, &.{ 0x8a }, 0, .rex }, - .{ .mov, .rm, .r16, .rm16, .none, .none, &.{ 0x8b }, 0, .none }, - .{ .mov, .rm, .r32, .rm32, .none, .none, &.{ 0x8b }, 0, .none }, - .{ .mov, .rm, .r64, .rm64, .none, .none, &.{ 0x8b }, 0, .long }, - .{ .mov, .mr, .rm16, .sreg, .none, .none, &.{ 0x8c }, 0, .none }, - .{ .mov, .mr, .rm64, .sreg, .none, .none, &.{ 0x8c }, 0, .long }, - .{ .mov, .rm, .sreg, .rm16, .none, .none, &.{ 0x8e }, 0, .none }, - .{ .mov, .rm, .sreg, .rm64, .none, .none, &.{ 0x8e }, 0, .long }, - .{ .mov, .fd, .al, .moffs, .none, .none, &.{ 0xa0 }, 0, .none }, - .{ .mov, .fd, .ax, .moffs, .none, .none, &.{ 0xa1 }, 0, .none }, - .{ .mov, .fd, .eax, .moffs, .none, .none, &.{ 0xa1 }, 0, .none }, - .{ .mov, .fd, .rax, .moffs, .none, .none, &.{ 0xa1 }, 0, .long }, - .{ .mov, .td, .moffs, .al, .none, .none, &.{ 0xa2 }, 0, .none }, - .{ .mov, .td, .moffs, .ax, .none, .none, &.{ 0xa3 }, 0, .none }, - .{ .mov, .td, .moffs, .eax, .none, .none, &.{ 0xa3 }, 0, .none }, - .{ .mov, .td, .moffs, .rax, .none, .none, &.{ 0xa3 }, 0, .long }, - .{ .mov, .oi, .r8, .imm8, .none, .none, &.{ 0xb0 }, 0, .none }, - .{ .mov, .oi, .r8, .imm8, .none, .none, &.{ 0xb0 }, 0, .rex }, - .{ .mov, .oi, .r16, .imm16, .none, .none, &.{ 0xb8 }, 0, .none }, - .{ .mov, .oi, .r32, .imm32, .none, .none, &.{ 0xb8 }, 0, .none }, - .{ .mov, .oi, .r64, .imm64, .none, .none, &.{ 0xb8 }, 0, .long }, - .{ .mov, .mi, .rm8, .imm8, .none, .none, &.{ 0xc6 }, 0, .none }, - .{ .mov, .mi, .rm8, .imm8, .none, .none, &.{ 0xc6 }, 0, .rex }, - .{ .mov, .mi, .rm16, .imm16, .none, .none, &.{ 0xc7 }, 0, .none }, - .{ .mov, .mi, .rm32, .imm32, .none, .none, &.{ 0xc7 }, 0, .none }, - .{ .mov, .mi, .rm64, .imm32s, .none, .none, &.{ 0xc7 }, 0, .long }, - - .{ .movbe, .rm, .r16, .m16, .none, .none, &.{ 0x0f, 0x38, 0xf0 }, 0, .none }, - .{ .movbe, .rm, .r32, .m32, .none, .none, &.{ 0x0f, 0x38, 0xf0 }, 0, .none }, - .{ .movbe, .rm, .r64, .m64, .none, .none, &.{ 0x0f, 0x38, 0xf0 }, 0, .long }, - .{ .movbe, .mr, .m16, .r16, .none, .none, &.{ 0x0f, 0x38, 0xf1 }, 0, .none }, - .{ .movbe, .mr, .m32, .r32, .none, .none, &.{ 0x0f, 0x38, 0xf1 }, 0, .none }, - .{ .movbe, .mr, .m64, .r64, .none, .none, &.{ 0x0f, 0x38, 0xf1 }, 0, .long }, - - .{ .movs, .np, .m8, .m8, .none, .none, &.{ 0xa4 }, 0, .none }, - .{ .movs, .np, .m16, .m16, .none, .none, &.{ 0xa5 }, 0, .none }, - .{ .movs, .np, .m32, .m32, .none, .none, &.{ 0xa5 }, 0, .none }, - .{ .movs, .np, .m64, .m64, .none, .none, &.{ 0xa5 }, 0, .long }, - .{ .movsb, .np, .none, .none, .none, .none, &.{ 0xa4 }, 0, .none }, - .{ .movsw, .np, .none, .none, .none, .none, &.{ 0xa5 }, 0, .short }, - .{ .movsd, .np, .none, .none, .none, .none, &.{ 0xa5 }, 0, .none }, - .{ .movsq, .np, .none, .none, .none, .none, &.{ 0xa5 }, 0, .long }, - - .{ .movsx, .rm, .r16, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .none }, - .{ .movsx, .rm, .r16, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .rex }, - .{ .movsx, .rm, .r32, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .none }, - .{ .movsx, .rm, .r32, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .rex }, - .{ .movsx, .rm, .r64, .rm8, .none, .none, &.{ 0x0f, 0xbe }, 0, .long }, - .{ .movsx, .rm, .r32, .rm16, .none, .none, &.{ 0x0f, 0xbf }, 0, .none }, - .{ .movsx, .rm, .r64, .rm16, .none, .none, &.{ 0x0f, 0xbf }, 0, .long }, + .{ .call, .d, &.{ .rel32 }, &.{ 0xe8 }, 0, .none }, + .{ .call, .m, &.{ .rm64 }, &.{ 0xff }, 2, .none }, + + .{ .cbw, .np, &.{ .o16 }, &.{ 0x98 }, 0, .none }, + .{ .cwde, .np, &.{ .o32 }, &.{ 0x98 }, 0, .none }, + .{ .cdqe, .np, &.{ .o64 }, &.{ 0x98 }, 0, .long }, + + .{ .cwd, .np, &.{ .o16 }, &.{ 0x99 }, 0, .none }, + .{ .cdq, .np, &.{ .o32 }, &.{ 0x99 }, 0, .none }, + .{ .cqo, .np, &.{ .o64 }, &.{ 0x99 }, 0, .long }, + + .{ .cmova, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x47 }, 0, .none }, + .{ .cmova, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x47 }, 0, .none }, + .{ .cmova, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x47 }, 0, .long }, + .{ .cmovae, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x43 }, 0, .none }, + .{ .cmovae, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x43 }, 0, .none }, + .{ .cmovae, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x43 }, 0, .long }, + .{ .cmovb, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x42 }, 0, .none }, + .{ .cmovb, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x42 }, 0, .none }, + .{ .cmovb, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x42 }, 0, .long }, + .{ .cmovbe, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x46 }, 0, .none }, + .{ .cmovbe, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x46 }, 0, .none }, + .{ .cmovbe, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x46 }, 0, .long }, + .{ .cmovc, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x42 }, 0, .none }, + .{ .cmovc, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x42 }, 0, .none }, + .{ .cmovc, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x42 }, 0, .long }, + .{ .cmove, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x44 }, 0, .none }, + .{ .cmove, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x44 }, 0, .none }, + .{ .cmove, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x44 }, 0, .long }, + .{ .cmovg, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4f }, 0, .none }, + .{ .cmovg, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4f }, 0, .none }, + .{ .cmovg, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4f }, 0, .long }, + .{ .cmovge, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4d }, 0, .none }, + .{ .cmovge, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4d }, 0, .none }, + .{ .cmovge, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4d }, 0, .long }, + .{ .cmovl, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4c }, 0, .none }, + .{ .cmovl, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4c }, 0, .none }, + .{ .cmovl, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4c }, 0, .long }, + .{ .cmovle, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4e }, 0, .none }, + .{ .cmovle, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4e }, 0, .none }, + .{ .cmovle, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4e }, 0, .long }, + .{ .cmovna, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x46 }, 0, .none }, + .{ .cmovna, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x46 }, 0, .none }, + .{ .cmovna, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x46 }, 0, .long }, + .{ .cmovnae, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x42 }, 0, .none }, + .{ .cmovnae, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x42 }, 0, .none }, + .{ .cmovnae, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x42 }, 0, .long }, + .{ .cmovnb, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x43 }, 0, .none }, + .{ .cmovnb, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x43 }, 0, .none }, + .{ .cmovnb, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x43 }, 0, .long }, + .{ .cmovnbe, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x47 }, 0, .none }, + .{ .cmovnbe, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x47 }, 0, .none }, + .{ .cmovnbe, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x47 }, 0, .long }, + .{ .cmovnc, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x43 }, 0, .none }, + .{ .cmovnc, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x43 }, 0, .none }, + .{ .cmovnc, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x43 }, 0, .long }, + .{ .cmovne, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x45 }, 0, .none }, + .{ .cmovne, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x45 }, 0, .none }, + .{ .cmovne, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x45 }, 0, .long }, + .{ .cmovng, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4e }, 0, .none }, + .{ .cmovng, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4e }, 0, .none }, + .{ .cmovng, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4e }, 0, .long }, + .{ .cmovnge, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4c }, 0, .none }, + .{ .cmovnge, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4c }, 0, .none }, + .{ .cmovnge, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4c }, 0, .long }, + .{ .cmovnl, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4d }, 0, .none }, + .{ .cmovnl, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4d }, 0, .none }, + .{ .cmovnl, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4d }, 0, .long }, + .{ .cmovnle, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4f }, 0, .none }, + .{ .cmovnle, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4f }, 0, .none }, + .{ .cmovnle, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4f }, 0, .long }, + .{ .cmovno, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x41 }, 0, .none }, + .{ .cmovno, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x41 }, 0, .none }, + .{ .cmovno, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x41 }, 0, .long }, + .{ .cmovnp, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4b }, 0, .none }, + .{ .cmovnp, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4b }, 0, .none }, + .{ .cmovnp, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4b }, 0, .long }, + .{ .cmovns, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x49 }, 0, .none }, + .{ .cmovns, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x49 }, 0, .none }, + .{ .cmovns, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x49 }, 0, .long }, + .{ .cmovnz, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x45 }, 0, .none }, + .{ .cmovnz, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x45 }, 0, .none }, + .{ .cmovnz, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x45 }, 0, .long }, + .{ .cmovo, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x40 }, 0, .none }, + .{ .cmovo, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x40 }, 0, .none }, + .{ .cmovo, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x40 }, 0, .long }, + .{ .cmovp, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4a }, 0, .none }, + .{ .cmovp, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4a }, 0, .none }, + .{ .cmovp, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4a }, 0, .long }, + .{ .cmovpe, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4a }, 0, .none }, + .{ .cmovpe, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4a }, 0, .none }, + .{ .cmovpe, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4a }, 0, .long }, + .{ .cmovpo, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x4b }, 0, .none }, + .{ .cmovpo, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x4b }, 0, .none }, + .{ .cmovpo, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x4b }, 0, .long }, + .{ .cmovs, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x48 }, 0, .none }, + .{ .cmovs, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x48 }, 0, .none }, + .{ .cmovs, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x48 }, 0, .long }, + .{ .cmovz, .rm, &.{ .r16, .rm16 }, &.{ 0x0f, 0x44 }, 0, .none }, + .{ .cmovz, .rm, &.{ .r32, .rm32 }, &.{ 0x0f, 0x44 }, 0, .none }, + .{ .cmovz, .rm, &.{ .r64, .rm64 }, &.{ 0x0f, 0x44 }, 0, .long }, + + .{ .cmp, .zi, &.{ .al, .imm8 }, &.{ 0x3c }, 0, .none }, + .{ .cmp, .zi, &.{ .ax, .imm16 }, &.{ 0x3d }, 0, .none }, + .{ .cmp, .zi, &.{ .eax, .imm32 }, &.{ 0x3d }, 0, .none }, + .{ .cmp, .zi, &.{ .rax, .imm32s }, &.{ 0x3d }, 0, .long }, + .{ .cmp, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 7, .none }, + .{ .cmp, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 7, .rex }, + .{ .cmp, .mi, &.{ .rm16, .imm16 }, &.{ 0x81 }, 7, .none }, + .{ .cmp, .mi, &.{ .rm32, .imm32 }, &.{ 0x81 }, 7, .none }, + .{ .cmp, .mi, &.{ .rm64, .imm32s }, &.{ 0x81 }, 7, .long }, + .{ .cmp, .mi, &.{ .rm16, .imm8s }, &.{ 0x83 }, 7, .none }, + .{ .cmp, .mi, &.{ .rm32, .imm8s }, &.{ 0x83 }, 7, .none }, + .{ .cmp, .mi, &.{ .rm64, .imm8s }, &.{ 0x83 }, 7, .long }, + .{ .cmp, .mr, &.{ .rm8, .r8 }, &.{ 0x38 }, 0, .none }, + .{ .cmp, .mr, &.{ .rm8, .r8 }, &.{ 0x38 }, 0, .rex }, + .{ .cmp, .mr, &.{ .rm16, .r16 }, &.{ 0x39 }, 0, .none }, + .{ .cmp, .mr, &.{ .rm32, .r32 }, &.{ 0x39 }, 0, .none }, + .{ .cmp, .mr, &.{ .rm64, .r64 }, &.{ 0x39 }, 0, .long }, + .{ .cmp, .rm, &.{ .r8, .rm8 }, &.{ 0x3a }, 0, .none }, + .{ .cmp, .rm, &.{ .r8, .rm8 }, &.{ 0x3a }, 0, .rex }, + .{ .cmp, .rm, &.{ .r16, .rm16 }, &.{ 0x3b }, 0, .none }, + .{ .cmp, .rm, &.{ .r32, .rm32 }, &.{ 0x3b }, 0, .none }, + .{ .cmp, .rm, &.{ .r64, .rm64 }, &.{ 0x3b }, 0, .long }, + + .{ .cmps, .np, &.{ .m8, .m8 }, &.{ 0xa6 }, 0, .none }, + .{ .cmps, .np, &.{ .m16, .m16 }, &.{ 0xa7 }, 0, .none }, + .{ .cmps, .np, &.{ .m32, .m32 }, &.{ 0xa7 }, 0, .none }, + .{ .cmps, .np, &.{ .m64, .m64 }, &.{ 0xa7 }, 0, .long }, + + .{ .cmpsb, .np, &.{}, &.{ 0xa6 }, 0, .none }, + .{ .cmpsw, .np, &.{}, &.{ 0xa7 }, 0, .short }, + .{ .cmpsd, .np, &.{}, &.{ 0xa7 }, 0, .none }, + .{ .cmpsq, .np, &.{}, &.{ 0xa7 }, 0, .long }, + + .{ .cmpxchg, .mr, &.{ .rm8, .r8 }, &.{ 0x0f, 0xb0 }, 0, .none }, + .{ .cmpxchg, .mr, &.{ .rm8, .r8 }, &.{ 0x0f, 0xb0 }, 0, .rex }, + .{ .cmpxchg, .mr, &.{ .rm16, .r16 }, &.{ 0x0f, 0xb1 }, 0, .none }, + .{ .cmpxchg, .mr, &.{ .rm32, .r32 }, &.{ 0x0f, 0xb1 }, 0, .none }, + .{ .cmpxchg, .mr, &.{ .rm64, .r64 }, &.{ 0x0f, 0xb1 }, 0, .long }, + + .{ .cmpxchg8b , .m, &.{ .m64 }, &.{ 0x0f, 0xc7 }, 1, .none }, + .{ .cmpxchg16b, .m, &.{ .m128 }, &.{ 0x0f, 0xc7 }, 1, .long }, + + .{ .div, .m, &.{ .rm8 }, &.{ 0xf6 }, 6, .none }, + .{ .div, .m, &.{ .rm8 }, &.{ 0xf6 }, 6, .rex }, + .{ .div, .m, &.{ .rm16 }, &.{ 0xf7 }, 6, .none }, + .{ .div, .m, &.{ .rm32 }, &.{ 0xf7 }, 6, .none }, + .{ .div, .m, &.{ .rm64 }, &.{ 0xf7 }, 6, .long }, + + .{ .fisttp, .m, &.{ .m16 }, &.{ 0xdf }, 1, .fpu }, + .{ .fisttp, .m, &.{ .m32 }, &.{ 0xdb }, 1, .fpu }, + .{ .fisttp, .m, &.{ .m64 }, &.{ 0xdd }, 1, .fpu }, + + .{ .fld, .m, &.{ .m32 }, &.{ 0xd9 }, 0, .fpu }, + .{ .fld, .m, &.{ .m64 }, &.{ 0xdd }, 0, .fpu }, + .{ .fld, .m, &.{ .m80 }, &.{ 0xdb }, 5, .fpu }, + + .{ .idiv, .m, &.{ .rm8 }, &.{ 0xf6 }, 7, .none }, + .{ .idiv, .m, &.{ .rm8 }, &.{ 0xf6 }, 7, .rex }, + .{ .idiv, .m, &.{ .rm16 }, &.{ 0xf7 }, 7, .none }, + .{ .idiv, .m, &.{ .rm32 }, &.{ 0xf7 }, 7, .none }, + .{ .idiv, .m, &.{ .rm64 }, &.{ 0xf7 }, 7, .long }, + + .{ .imul, .m, &.{ .rm8 }, &.{ 0xf6 }, 5, .none }, + .{ .imul, .m, &.{ .rm8 }, &.{ 0xf6 }, 5, .rex }, + .{ .imul, .m, &.{ .rm16, }, &.{ 0xf7 }, 5, .none }, + .{ .imul, .m, &.{ .rm32, }, &.{ 0xf7 }, 5, .none }, + .{ .imul, .m, &.{ .rm64, }, &.{ 0xf7 }, 5, .long }, + .{ .imul, .rm, &.{ .r16, .rm16, }, &.{ 0x0f, 0xaf }, 0, .none }, + .{ .imul, .rm, &.{ .r32, .rm32, }, &.{ 0x0f, 0xaf }, 0, .none }, + .{ .imul, .rm, &.{ .r64, .rm64, }, &.{ 0x0f, 0xaf }, 0, .long }, + .{ .imul, .rmi, &.{ .r16, .rm16, .imm8s }, &.{ 0x6b }, 0, .none }, + .{ .imul, .rmi, &.{ .r32, .rm32, .imm8s }, &.{ 0x6b }, 0, .none }, + .{ .imul, .rmi, &.{ .r64, .rm64, .imm8s }, &.{ 0x6b }, 0, .long }, + .{ .imul, .rmi, &.{ .r16, .rm16, .imm16 }, &.{ 0x69 }, 0, .none }, + .{ .imul, .rmi, &.{ .r32, .rm32, .imm32 }, &.{ 0x69 }, 0, .none }, + .{ .imul, .rmi, &.{ .r64, .rm64, .imm32 }, &.{ 0x69 }, 0, .long }, + + .{ .int3, .np, &.{}, &.{ 0xcc }, 0, .none }, + + .{ .ja, .d, &.{ .rel32 }, &.{ 0x0f, 0x87 }, 0, .none }, + .{ .jae, .d, &.{ .rel32 }, &.{ 0x0f, 0x83 }, 0, .none }, + .{ .jb, .d, &.{ .rel32 }, &.{ 0x0f, 0x82 }, 0, .none }, + .{ .jbe, .d, &.{ .rel32 }, &.{ 0x0f, 0x86 }, 0, .none }, + .{ .jc, .d, &.{ .rel32 }, &.{ 0x0f, 0x82 }, 0, .none }, + .{ .jrcxz, .d, &.{ .rel32 }, &.{ 0xe3 }, 0, .none }, + .{ .je, .d, &.{ .rel32 }, &.{ 0x0f, 0x84 }, 0, .none }, + .{ .jg, .d, &.{ .rel32 }, &.{ 0x0f, 0x8f }, 0, .none }, + .{ .jge, .d, &.{ .rel32 }, &.{ 0x0f, 0x8d }, 0, .none }, + .{ .jl, .d, &.{ .rel32 }, &.{ 0x0f, 0x8c }, 0, .none }, + .{ .jle, .d, &.{ .rel32 }, &.{ 0x0f, 0x8e }, 0, .none }, + .{ .jna, .d, &.{ .rel32 }, &.{ 0x0f, 0x86 }, 0, .none }, + .{ .jnae, .d, &.{ .rel32 }, &.{ 0x0f, 0x82 }, 0, .none }, + .{ .jnb, .d, &.{ .rel32 }, &.{ 0x0f, 0x83 }, 0, .none }, + .{ .jnbe, .d, &.{ .rel32 }, &.{ 0x0f, 0x87 }, 0, .none }, + .{ .jnc, .d, &.{ .rel32 }, &.{ 0x0f, 0x83 }, 0, .none }, + .{ .jne, .d, &.{ .rel32 }, &.{ 0x0f, 0x85 }, 0, .none }, + .{ .jng, .d, &.{ .rel32 }, &.{ 0x0f, 0x8e }, 0, .none }, + .{ .jnge, .d, &.{ .rel32 }, &.{ 0x0f, 0x8c }, 0, .none }, + .{ .jnl, .d, &.{ .rel32 }, &.{ 0x0f, 0x8d }, 0, .none }, + .{ .jnle, .d, &.{ .rel32 }, &.{ 0x0f, 0x8f }, 0, .none }, + .{ .jno, .d, &.{ .rel32 }, &.{ 0x0f, 0x81 }, 0, .none }, + .{ .jnp, .d, &.{ .rel32 }, &.{ 0x0f, 0x8b }, 0, .none }, + .{ .jns, .d, &.{ .rel32 }, &.{ 0x0f, 0x89 }, 0, .none }, + .{ .jnz, .d, &.{ .rel32 }, &.{ 0x0f, 0x85 }, 0, .none }, + .{ .jo, .d, &.{ .rel32 }, &.{ 0x0f, 0x80 }, 0, .none }, + .{ .jp, .d, &.{ .rel32 }, &.{ 0x0f, 0x8a }, 0, .none }, + .{ .jpe, .d, &.{ .rel32 }, &.{ 0x0f, 0x8a }, 0, .none }, + .{ .jpo, .d, &.{ .rel32 }, &.{ 0x0f, 0x8b }, 0, .none }, + .{ .js, .d, &.{ .rel32 }, &.{ 0x0f, 0x88 }, 0, .none }, + .{ .jz, .d, &.{ .rel32 }, &.{ 0x0f, 0x84 }, 0, .none }, + + .{ .jmp, .d, &.{ .rel32 }, &.{ 0xe9 }, 0, .none }, + .{ .jmp, .m, &.{ .rm64 }, &.{ 0xff }, 4, .none }, + + .{ .lea, .rm, &.{ .r16, .m }, &.{ 0x8d }, 0, .none }, + .{ .lea, .rm, &.{ .r32, .m }, &.{ 0x8d }, 0, .none }, + .{ .lea, .rm, &.{ .r64, .m }, &.{ 0x8d }, 0, .long }, + + .{ .lfence, .np, &.{}, &.{ 0x0f, 0xae, 0xe8 }, 0, .none }, + + .{ .lods, .np, &.{ .m8 }, &.{ 0xac }, 0, .none }, + .{ .lods, .np, &.{ .m16 }, &.{ 0xad }, 0, .none }, + .{ .lods, .np, &.{ .m32 }, &.{ 0xad }, 0, .none }, + .{ .lods, .np, &.{ .m64 }, &.{ 0xad }, 0, .long }, + + .{ .lodsb, .np, &.{}, &.{ 0xac }, 0, .none }, + .{ .lodsw, .np, &.{}, &.{ 0xad }, 0, .short }, + .{ .lodsd, .np, &.{}, &.{ 0xad }, 0, .none }, + .{ .lodsq, .np, &.{}, &.{ 0xad }, 0, .long }, + + .{ .lzcnt, .rm, &.{ .r16, .rm16 }, &.{ 0xf3, 0x0f, 0xbd }, 0, .none }, + .{ .lzcnt, .rm, &.{ .r32, .rm32 }, &.{ 0xf3, 0x0f, 0xbd }, 0, .none }, + .{ .lzcnt, .rm, &.{ .r64, .rm64 }, &.{ 0xf3, 0x0f, 0xbd }, 0, .long }, + + .{ .mfence, .np, &.{}, &.{ 0x0f, 0xae, 0xf0 }, 0, .none }, + + .{ .mov, .mr, &.{ .rm8, .r8 }, &.{ 0x88 }, 0, .none }, + .{ .mov, .mr, &.{ .rm8, .r8 }, &.{ 0x88 }, 0, .rex }, + .{ .mov, .mr, &.{ .rm16, .r16 }, &.{ 0x89 }, 0, .none }, + .{ .mov, .mr, &.{ .rm32, .r32 }, &.{ 0x89 }, 0, .none }, + .{ .mov, .mr, &.{ .rm64, .r64 }, &.{ 0x89 }, 0, .long }, + .{ .mov, .rm, &.{ .r8, .rm8 }, &.{ 0x8a }, 0, .none }, + .{ .mov, .rm, &.{ .r8, .rm8 }, &.{ 0x8a }, 0, .rex }, + .{ .mov, .rm, &.{ .r16, .rm16 }, &.{ 0x8b }, 0, .none }, + .{ .mov, .rm, &.{ .r32, .rm32 }, &.{ 0x8b }, 0, .none }, + .{ .mov, .rm, &.{ .r64, .rm64 }, &.{ 0x8b }, 0, .long }, + .{ .mov, .mr, &.{ .rm16, .sreg }, &.{ 0x8c }, 0, .none }, + .{ .mov, .mr, &.{ .rm64, .sreg }, &.{ 0x8c }, 0, .long }, + .{ .mov, .rm, &.{ .sreg, .rm16 }, &.{ 0x8e }, 0, .none }, + .{ .mov, .rm, &.{ .sreg, .rm64 }, &.{ 0x8e }, 0, .long }, + .{ .mov, .fd, &.{ .al, .moffs }, &.{ 0xa0 }, 0, .none }, + .{ .mov, .fd, &.{ .ax, .moffs }, &.{ 0xa1 }, 0, .none }, + .{ .mov, .fd, &.{ .eax, .moffs }, &.{ 0xa1 }, 0, .none }, + .{ .mov, .fd, &.{ .rax, .moffs }, &.{ 0xa1 }, 0, .long }, + .{ .mov, .td, &.{ .moffs, .al }, &.{ 0xa2 }, 0, .none }, + .{ .mov, .td, &.{ .moffs, .ax }, &.{ 0xa3 }, 0, .none }, + .{ .mov, .td, &.{ .moffs, .eax }, &.{ 0xa3 }, 0, .none }, + .{ .mov, .td, &.{ .moffs, .rax }, &.{ 0xa3 }, 0, .long }, + .{ .mov, .oi, &.{ .r8, .imm8 }, &.{ 0xb0 }, 0, .none }, + .{ .mov, .oi, &.{ .r8, .imm8 }, &.{ 0xb0 }, 0, .rex }, + .{ .mov, .oi, &.{ .r16, .imm16 }, &.{ 0xb8 }, 0, .none }, + .{ .mov, .oi, &.{ .r32, .imm32 }, &.{ 0xb8 }, 0, .none }, + .{ .mov, .oi, &.{ .r64, .imm64 }, &.{ 0xb8 }, 0, .long }, + .{ .mov, .mi, &.{ .rm8, .imm8 }, &.{ 0xc6 }, 0, .none }, + .{ .mov, .mi, &.{ .rm8, .imm8 }, &.{ 0xc6 }, 0, .rex }, + .{ .mov, .mi, &.{ .rm16, .imm16 }, &.{ 0xc7 }, 0, .none }, + .{ .mov, .mi, &.{ .rm32, .imm32 }, &.{ 0xc7 }, 0, .none }, + .{ .mov, .mi, &.{ .rm64, .imm32s }, &.{ 0xc7 }, 0, .long }, + + .{ .movbe, .rm, &.{ .r16, .m16 }, &.{ 0x0f, 0x38, 0xf0 }, 0, .none }, + .{ .movbe, .rm, &.{ .r32, .m32 }, &.{ 0x0f, 0x38, 0xf0 }, 0, .none }, + .{ .movbe, .rm, &.{ .r64, .m64 }, &.{ 0x0f, 0x38, 0xf0 }, 0, .long }, + .{ .movbe, .mr, &.{ .m16, .r16 }, &.{ 0x0f, 0x38, 0xf1 }, 0, .none }, + .{ .movbe, .mr, &.{ .m32, .r32 }, &.{ 0x0f, 0x38, 0xf1 }, 0, .none }, + .{ .movbe, .mr, &.{ .m64, .r64 }, &.{ 0x0f, 0x38, 0xf1 }, 0, .long }, + + .{ .movs, .np, &.{ .m8, .m8 }, &.{ 0xa4 }, 0, .none }, + .{ .movs, .np, &.{ .m16, .m16 }, &.{ 0xa5 }, 0, .none }, + .{ .movs, .np, &.{ .m32, .m32 }, &.{ 0xa5 }, 0, .none }, + .{ .movs, .np, &.{ .m64, .m64 }, &.{ 0xa5 }, 0, .long }, + + .{ .movsb, .np, &.{}, &.{ 0xa4 }, 0, .none }, + .{ .movsw, .np, &.{}, &.{ 0xa5 }, 0, .short }, + .{ .movsd, .np, &.{}, &.{ 0xa5 }, 0, .none }, + .{ .movsq, .np, &.{}, &.{ 0xa5 }, 0, .long }, + + .{ .movsx, .rm, &.{ .r16, .rm8 }, &.{ 0x0f, 0xbe }, 0, .none }, + .{ .movsx, .rm, &.{ .r16, .rm8 }, &.{ 0x0f, 0xbe }, 0, .rex }, + .{ .movsx, .rm, &.{ .r32, .rm8 }, &.{ 0x0f, 0xbe }, 0, .none }, + .{ .movsx, .rm, &.{ .r32, .rm8 }, &.{ 0x0f, 0xbe }, 0, .rex }, + .{ .movsx, .rm, &.{ .r64, .rm8 }, &.{ 0x0f, 0xbe }, 0, .long }, + .{ .movsx, .rm, &.{ .r32, .rm16 }, &.{ 0x0f, 0xbf }, 0, .none }, + .{ .movsx, .rm, &.{ .r64, .rm16 }, &.{ 0x0f, 0xbf }, 0, .long }, // This instruction is discouraged. - .{ .movsxd, .rm, .r32, .rm32, .none, .none, &.{ 0x63 }, 0, .none }, - .{ .movsxd, .rm, .r64, .rm32, .none, .none, &.{ 0x63 }, 0, .long }, - - .{ .movzx, .rm, .r16, .rm8, .none, .none, &.{ 0x0f, 0xb6 }, 0, .none }, - .{ .movzx, .rm, .r32, .rm8, .none, .none, &.{ 0x0f, 0xb6 }, 0, .none }, - .{ .movzx, .rm, .r64, .rm8, .none, .none, &.{ 0x0f, 0xb6 }, 0, .long }, - .{ .movzx, .rm, .r32, .rm16, .none, .none, &.{ 0x0f, 0xb7 }, 0, .none }, - .{ .movzx, .rm, .r64, .rm16, .none, .none, &.{ 0x0f, 0xb7 }, 0, .long }, - - .{ .mul, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 4, .none }, - .{ .mul, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 4, .rex }, - .{ .mul, .m, .rm16, .none, .none, .none, &.{ 0xf7 }, 4, .none }, - .{ .mul, .m, .rm32, .none, .none, .none, &.{ 0xf7 }, 4, .none }, - .{ .mul, .m, .rm64, .none, .none, .none, &.{ 0xf7 }, 4, .long }, - - .{ .neg, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 3, .none }, - .{ .neg, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 3, .rex }, - .{ .neg, .m, .rm16, .none, .none, .none, &.{ 0xf7 }, 3, .none }, - .{ .neg, .m, .rm32, .none, .none, .none, &.{ 0xf7 }, 3, .none }, - .{ .neg, .m, .rm64, .none, .none, .none, &.{ 0xf7 }, 3, .long }, - - .{ .nop, .np, .none, .none, .none, .none, &.{ 0x90 }, 0, .none }, - - .{ .not, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 2, .none }, - .{ .not, .m, .rm8, .none, .none, .none, &.{ 0xf6 }, 2, .rex }, - .{ .not, .m, .rm16, .none, .none, .none, &.{ 0xf7 }, 2, .none }, - .{ .not, .m, .rm32, .none, .none, .none, &.{ 0xf7 }, 2, .none }, - .{ .not, .m, .rm64, .none, .none, .none, &.{ 0xf7 }, 2, .long }, - - .{ .@"or", .zi, .al, .imm8, .none, .none, &.{ 0x0c }, 0, .none }, - .{ .@"or", .zi, .ax, .imm16, .none, .none, &.{ 0x0d }, 0, .none }, - .{ .@"or", .zi, .eax, .imm32, .none, .none, &.{ 0x0d }, 0, .none }, - .{ .@"or", .zi, .rax, .imm32s, .none, .none, &.{ 0x0d }, 0, .long }, - .{ .@"or", .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 1, .none }, - .{ .@"or", .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 1, .rex }, - .{ .@"or", .mi, .rm16, .imm16, .none, .none, &.{ 0x81 }, 1, .none }, - .{ .@"or", .mi, .rm32, .imm32, .none, .none, &.{ 0x81 }, 1, .none }, - .{ .@"or", .mi, .rm64, .imm32s, .none, .none, &.{ 0x81 }, 1, .long }, - .{ .@"or", .mi, .rm16, .imm8s, .none, .none, &.{ 0x83 }, 1, .none }, - .{ .@"or", .mi, .rm32, .imm8s, .none, .none, &.{ 0x83 }, 1, .none }, - .{ .@"or", .mi, .rm64, .imm8s, .none, .none, &.{ 0x83 }, 1, .long }, - .{ .@"or", .mr, .rm8, .r8, .none, .none, &.{ 0x08 }, 0, .none }, - .{ .@"or", .mr, .rm8, .r8, .none, .none, &.{ 0x08 }, 0, .rex }, - .{ .@"or", .mr, .rm16, .r16, .none, .none, &.{ 0x09 }, 0, .none }, - .{ .@"or", .mr, .rm32, .r32, .none, .none, &.{ 0x09 }, 0, .none }, - .{ .@"or", .mr, .rm64, .r64, .none, .none, &.{ 0x09 }, 0, .long }, - .{ .@"or", .rm, .r8, .rm8, .none, .none, &.{ 0x0a }, 0, .none }, - .{ .@"or", .rm, .r8, .rm8, .none, .none, &.{ 0x0a }, 0, .rex }, - .{ .@"or", .rm, .r16, .rm16, .none, .none, &.{ 0x0b }, 0, .none }, - .{ .@"or", .rm, .r32, .rm32, .none, .none, &.{ 0x0b }, 0, .none }, - .{ .@"or", .rm, .r64, .rm64, .none, .none, &.{ 0x0b }, 0, .long }, - - .{ .pop, .o, .r16, .none, .none, .none, &.{ 0x58 }, 0, .none }, - .{ .pop, .o, .r64, .none, .none, .none, &.{ 0x58 }, 0, .none }, - .{ .pop, .m, .rm16, .none, .none, .none, &.{ 0x8f }, 0, .none }, - .{ .pop, .m, .rm64, .none, .none, .none, &.{ 0x8f }, 0, .none }, - - .{ .popcnt, .rm, .r16, .rm16, .none, .none, &.{ 0xf3, 0x0f, 0xb8 }, 0, .none }, - .{ .popcnt, .rm, .r32, .rm32, .none, .none, &.{ 0xf3, 0x0f, 0xb8 }, 0, .none }, - .{ .popcnt, .rm, .r64, .rm64, .none, .none, &.{ 0xf3, 0x0f, 0xb8 }, 0, .long }, - - .{ .push, .o, .r16, .none, .none, .none, &.{ 0x50 }, 0, .none }, - .{ .push, .o, .r64, .none, .none, .none, &.{ 0x50 }, 0, .none }, - .{ .push, .m, .rm16, .none, .none, .none, &.{ 0xff }, 6, .none }, - .{ .push, .m, .rm64, .none, .none, .none, &.{ 0xff }, 6, .none }, - .{ .push, .i, .imm8, .none, .none, .none, &.{ 0x6a }, 0, .none }, - .{ .push, .i, .imm16, .none, .none, .none, &.{ 0x68 }, 0, .none }, - .{ .push, .i, .imm32, .none, .none, .none, &.{ 0x68 }, 0, .none }, - - .{ .ret, .np, .none, .none, .none, .none, &.{ 0xc3 }, 0, .none }, - - .{ .rcl, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 2, .none }, - .{ .rcl, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 2, .rex }, - .{ .rcl, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 2, .none }, - .{ .rcl, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 2, .rex }, - .{ .rcl, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 2, .none }, - .{ .rcl, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 2, .rex }, - .{ .rcl, .m1, .rm16, .unity, .none, .none, &.{ 0xd1 }, 2, .none }, - .{ .rcl, .mc, .rm16, .cl, .none, .none, &.{ 0xd3 }, 2, .none }, - .{ .rcl, .mi, .rm16, .imm8, .none, .none, &.{ 0xc1 }, 2, .none }, - .{ .rcl, .m1, .rm32, .unity, .none, .none, &.{ 0xd1 }, 2, .none }, - .{ .rcl, .m1, .rm64, .unity, .none, .none, &.{ 0xd1 }, 2, .long }, - .{ .rcl, .mc, .rm32, .cl, .none, .none, &.{ 0xd3 }, 2, .none }, - .{ .rcl, .mc, .rm64, .cl, .none, .none, &.{ 0xd3 }, 2, .long }, - .{ .rcl, .mi, .rm32, .imm8, .none, .none, &.{ 0xc1 }, 2, .none }, - .{ .rcl, .mi, .rm64, .imm8, .none, .none, &.{ 0xc1 }, 2, .long }, - - .{ .rcr, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 3, .none }, - .{ .rcr, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 3, .rex }, - .{ .rcr, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 3, .none }, - .{ .rcr, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 3, .rex }, - .{ .rcr, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 3, .none }, - .{ .rcr, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 3, .rex }, - .{ .rcr, .m1, .rm16, .unity, .none, .none, &.{ 0xd1 }, 3, .none }, - .{ .rcr, .mc, .rm16, .cl, .none, .none, &.{ 0xd3 }, 3, .none }, - .{ .rcr, .mi, .rm16, .imm8, .none, .none, &.{ 0xc1 }, 3, .none }, - .{ .rcr, .m1, .rm32, .unity, .none, .none, &.{ 0xd1 }, 3, .none }, - .{ .rcr, .m1, .rm64, .unity, .none, .none, &.{ 0xd1 }, 3, .long }, - .{ .rcr, .mc, .rm32, .cl, .none, .none, &.{ 0xd3 }, 3, .none }, - .{ .rcr, .mc, .rm64, .cl, .none, .none, &.{ 0xd3 }, 3, .long }, - .{ .rcr, .mi, .rm32, .imm8, .none, .none, &.{ 0xc1 }, 3, .none }, - .{ .rcr, .mi, .rm64, .imm8, .none, .none, &.{ 0xc1 }, 3, .long }, - - .{ .rol, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 0, .none }, - .{ .rol, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 0, .rex }, - .{ .rol, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 0, .none }, - .{ .rol, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 0, .rex }, - .{ .rol, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 0, .none }, - .{ .rol, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 0, .rex }, - .{ .rol, .m1, .rm16, .unity, .none, .none, &.{ 0xd1 }, 0, .none }, - .{ .rol, .mc, .rm16, .cl, .none, .none, &.{ 0xd3 }, 0, .none }, - .{ .rol, .mi, .rm16, .imm8, .none, .none, &.{ 0xc1 }, 0, .none }, - .{ .rol, .m1, .rm32, .unity, .none, .none, &.{ 0xd1 }, 0, .none }, - .{ .rol, .m1, .rm64, .unity, .none, .none, &.{ 0xd1 }, 0, .long }, - .{ .rol, .mc, .rm32, .cl, .none, .none, &.{ 0xd3 }, 0, .none }, - .{ .rol, .mc, .rm64, .cl, .none, .none, &.{ 0xd3 }, 0, .long }, - .{ .rol, .mi, .rm32, .imm8, .none, .none, &.{ 0xc1 }, 0, .none }, - .{ .rol, .mi, .rm64, .imm8, .none, .none, &.{ 0xc1 }, 0, .long }, - - .{ .ror, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 1, .none }, - .{ .ror, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 1, .rex }, - .{ .ror, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 1, .none }, - .{ .ror, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 1, .rex }, - .{ .ror, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 1, .none }, - .{ .ror, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 1, .rex }, - .{ .ror, .m1, .rm16, .unity, .none, .none, &.{ 0xd1 }, 1, .none }, - .{ .ror, .mc, .rm16, .cl, .none, .none, &.{ 0xd3 }, 1, .none }, - .{ .ror, .mi, .rm16, .imm8, .none, .none, &.{ 0xc1 }, 1, .none }, - .{ .ror, .m1, .rm32, .unity, .none, .none, &.{ 0xd1 }, 1, .none }, - .{ .ror, .m1, .rm64, .unity, .none, .none, &.{ 0xd1 }, 1, .long }, - .{ .ror, .mc, .rm32, .cl, .none, .none, &.{ 0xd3 }, 1, .none }, - .{ .ror, .mc, .rm64, .cl, .none, .none, &.{ 0xd3 }, 1, .long }, - .{ .ror, .mi, .rm32, .imm8, .none, .none, &.{ 0xc1 }, 1, .none }, - .{ .ror, .mi, .rm64, .imm8, .none, .none, &.{ 0xc1 }, 1, .long }, - - .{ .sal, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 4, .none }, - .{ .sal, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 4, .rex }, - .{ .sal, .m1, .rm16, .unity, .none, .none, &.{ 0xd1 }, 4, .none }, - .{ .sal, .m1, .rm32, .unity, .none, .none, &.{ 0xd1 }, 4, .none }, - .{ .sal, .m1, .rm64, .unity, .none, .none, &.{ 0xd1 }, 4, .long }, - .{ .sal, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 4, .none }, - .{ .sal, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 4, .rex }, - .{ .sal, .mc, .rm16, .cl, .none, .none, &.{ 0xd3 }, 4, .none }, - .{ .sal, .mc, .rm32, .cl, .none, .none, &.{ 0xd3 }, 4, .none }, - .{ .sal, .mc, .rm64, .cl, .none, .none, &.{ 0xd3 }, 4, .long }, - .{ .sal, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 4, .none }, - .{ .sal, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 4, .rex }, - .{ .sal, .mi, .rm16, .imm8, .none, .none, &.{ 0xc1 }, 4, .none }, - .{ .sal, .mi, .rm32, .imm8, .none, .none, &.{ 0xc1 }, 4, .none }, - .{ .sal, .mi, .rm64, .imm8, .none, .none, &.{ 0xc1 }, 4, .long }, - - .{ .sar, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 7, .none }, - .{ .sar, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 7, .rex }, - .{ .sar, .m1, .rm16, .unity, .none, .none, &.{ 0xd1 }, 7, .none }, - .{ .sar, .m1, .rm32, .unity, .none, .none, &.{ 0xd1 }, 7, .none }, - .{ .sar, .m1, .rm64, .unity, .none, .none, &.{ 0xd1 }, 7, .long }, - .{ .sar, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 7, .none }, - .{ .sar, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 7, .rex }, - .{ .sar, .mc, .rm16, .cl, .none, .none, &.{ 0xd3 }, 7, .none }, - .{ .sar, .mc, .rm32, .cl, .none, .none, &.{ 0xd3 }, 7, .none }, - .{ .sar, .mc, .rm64, .cl, .none, .none, &.{ 0xd3 }, 7, .long }, - .{ .sar, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 7, .none }, - .{ .sar, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 7, .rex }, - .{ .sar, .mi, .rm16, .imm8, .none, .none, &.{ 0xc1 }, 7, .none }, - .{ .sar, .mi, .rm32, .imm8, .none, .none, &.{ 0xc1 }, 7, .none }, - .{ .sar, .mi, .rm64, .imm8, .none, .none, &.{ 0xc1 }, 7, .long }, - - .{ .sbb, .zi, .al, .imm8, .none, .none, &.{ 0x1c }, 0, .none }, - .{ .sbb, .zi, .ax, .imm16, .none, .none, &.{ 0x1d }, 0, .none }, - .{ .sbb, .zi, .eax, .imm32, .none, .none, &.{ 0x1d }, 0, .none }, - .{ .sbb, .zi, .rax, .imm32s, .none, .none, &.{ 0x1d }, 0, .long }, - .{ .sbb, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 3, .none }, - .{ .sbb, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 3, .rex }, - .{ .sbb, .mi, .rm16, .imm16, .none, .none, &.{ 0x81 }, 3, .none }, - .{ .sbb, .mi, .rm32, .imm32, .none, .none, &.{ 0x81 }, 3, .none }, - .{ .sbb, .mi, .rm64, .imm32s, .none, .none, &.{ 0x81 }, 3, .long }, - .{ .sbb, .mi, .rm16, .imm8s, .none, .none, &.{ 0x83 }, 3, .none }, - .{ .sbb, .mi, .rm32, .imm8s, .none, .none, &.{ 0x83 }, 3, .none }, - .{ .sbb, .mi, .rm64, .imm8s, .none, .none, &.{ 0x83 }, 3, .long }, - .{ .sbb, .mr, .rm8, .r8, .none, .none, &.{ 0x18 }, 0, .none }, - .{ .sbb, .mr, .rm8, .r8, .none, .none, &.{ 0x18 }, 0, .rex }, - .{ .sbb, .mr, .rm16, .r16, .none, .none, &.{ 0x19 }, 0, .none }, - .{ .sbb, .mr, .rm32, .r32, .none, .none, &.{ 0x19 }, 0, .none }, - .{ .sbb, .mr, .rm64, .r64, .none, .none, &.{ 0x19 }, 0, .long }, - .{ .sbb, .rm, .r8, .rm8, .none, .none, &.{ 0x1a }, 0, .none }, - .{ .sbb, .rm, .r8, .rm8, .none, .none, &.{ 0x1a }, 0, .rex }, - .{ .sbb, .rm, .r16, .rm16, .none, .none, &.{ 0x1b }, 0, .none }, - .{ .sbb, .rm, .r32, .rm32, .none, .none, &.{ 0x1b }, 0, .none }, - .{ .sbb, .rm, .r64, .rm64, .none, .none, &.{ 0x1b }, 0, .long }, - - .{ .scas, .np, .m8, .none, .none, .none, &.{ 0xae }, 0, .none }, - .{ .scas, .np, .m16, .none, .none, .none, &.{ 0xaf }, 0, .none }, - .{ .scas, .np, .m32, .none, .none, .none, &.{ 0xaf }, 0, .none }, - .{ .scas, .np, .m64, .none, .none, .none, &.{ 0xaf }, 0, .long }, - .{ .scasb, .np, .none, .none, .none, .none, &.{ 0xae }, 0, .none }, - .{ .scasw, .np, .none, .none, .none, .none, &.{ 0xaf }, 0, .short }, - .{ .scasd, .np, .none, .none, .none, .none, &.{ 0xaf }, 0, .none }, - .{ .scasq, .np, .none, .none, .none, .none, &.{ 0xaf }, 0, .long }, - - .{ .seta, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x97 }, 0, .none }, - .{ .seta, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x97 }, 0, .rex }, - .{ .setae, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x93 }, 0, .none }, - .{ .setae, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x93 }, 0, .rex }, - .{ .setb, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x92 }, 0, .none }, - .{ .setb, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x92 }, 0, .rex }, - .{ .setbe, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x96 }, 0, .none }, - .{ .setbe, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x96 }, 0, .rex }, - .{ .setc, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x92 }, 0, .none }, - .{ .setc, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x92 }, 0, .rex }, - .{ .sete, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x94 }, 0, .none }, - .{ .sete, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x94 }, 0, .rex }, - .{ .setg, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9f }, 0, .none }, - .{ .setg, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9f }, 0, .rex }, - .{ .setge, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9d }, 0, .none }, - .{ .setge, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9d }, 0, .rex }, - .{ .setl, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9c }, 0, .none }, - .{ .setl, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9c }, 0, .rex }, - .{ .setle, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9e }, 0, .none }, - .{ .setle, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9e }, 0, .rex }, - .{ .setna, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x96 }, 0, .none }, - .{ .setna, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x96 }, 0, .rex }, - .{ .setnae, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x92 }, 0, .none }, - .{ .setnae, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x92 }, 0, .rex }, - .{ .setnb, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x93 }, 0, .none }, - .{ .setnb, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x93 }, 0, .rex }, - .{ .setnbe, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x97 }, 0, .none }, - .{ .setnbe, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x97 }, 0, .rex }, - .{ .setnc, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x93 }, 0, .none }, - .{ .setnc, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x93 }, 0, .rex }, - .{ .setne, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x95 }, 0, .none }, - .{ .setne, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x95 }, 0, .rex }, - .{ .setng, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9e }, 0, .none }, - .{ .setng, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9e }, 0, .rex }, - .{ .setnge, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9c }, 0, .none }, - .{ .setnge, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9c }, 0, .rex }, - .{ .setnl, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9d }, 0, .none }, - .{ .setnl, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9d }, 0, .rex }, - .{ .setnle, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9f }, 0, .none }, - .{ .setnle, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9f }, 0, .rex }, - .{ .setno, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x91 }, 0, .none }, - .{ .setno, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x91 }, 0, .rex }, - .{ .setnp, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9b }, 0, .none }, - .{ .setnp, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9b }, 0, .rex }, - .{ .setns, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x99 }, 0, .none }, - .{ .setns, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x99 }, 0, .rex }, - .{ .setnz, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x95 }, 0, .none }, - .{ .setnz, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x95 }, 0, .rex }, - .{ .seto, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x90 }, 0, .none }, - .{ .seto, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x90 }, 0, .rex }, - .{ .setp, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9a }, 0, .none }, - .{ .setp, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9a }, 0, .rex }, - .{ .setpe, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9a }, 0, .none }, - .{ .setpe, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9a }, 0, .rex }, - .{ .setpo, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9b }, 0, .none }, - .{ .setpo, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x9b }, 0, .rex }, - .{ .sets, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x98 }, 0, .none }, - .{ .sets, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x98 }, 0, .rex }, - .{ .setz, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x94 }, 0, .none }, - .{ .setz, .m, .rm8, .none, .none, .none, &.{ 0x0f, 0x94 }, 0, .rex }, - - .{ .sfence, .np, .none, .none, .none, .none, &.{ 0x0f, 0xae, 0xf8 }, 0, .none }, - - .{ .shl, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 4, .none }, - .{ .shl, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 4, .rex }, - .{ .shl, .m1, .rm16, .unity, .none, .none, &.{ 0xd1 }, 4, .none }, - .{ .shl, .m1, .rm32, .unity, .none, .none, &.{ 0xd1 }, 4, .none }, - .{ .shl, .m1, .rm64, .unity, .none, .none, &.{ 0xd1 }, 4, .long }, - .{ .shl, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 4, .none }, - .{ .shl, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 4, .rex }, - .{ .shl, .mc, .rm16, .cl, .none, .none, &.{ 0xd3 }, 4, .none }, - .{ .shl, .mc, .rm32, .cl, .none, .none, &.{ 0xd3 }, 4, .none }, - .{ .shl, .mc, .rm64, .cl, .none, .none, &.{ 0xd3 }, 4, .long }, - .{ .shl, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 4, .none }, - .{ .shl, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 4, .rex }, - .{ .shl, .mi, .rm16, .imm8, .none, .none, &.{ 0xc1 }, 4, .none }, - .{ .shl, .mi, .rm32, .imm8, .none, .none, &.{ 0xc1 }, 4, .none }, - .{ .shl, .mi, .rm64, .imm8, .none, .none, &.{ 0xc1 }, 4, .long }, - - .{ .shld, .mri, .rm16, .r16, .imm8, .none, &.{ 0x0f, 0xa4 }, 0, .none }, - .{ .shld, .mrc, .rm16, .r16, .cl, .none, &.{ 0x0f, 0xa5 }, 0, .none }, - .{ .shld, .mri, .rm32, .r32, .imm8, .none, &.{ 0x0f, 0xa4 }, 0, .none }, - .{ .shld, .mri, .rm64, .r64, .imm8, .none, &.{ 0x0f, 0xa4 }, 0, .long }, - .{ .shld, .mrc, .rm32, .r32, .cl, .none, &.{ 0x0f, 0xa5 }, 0, .none }, - .{ .shld, .mrc, .rm64, .r64, .cl, .none, &.{ 0x0f, 0xa5 }, 0, .long }, - - .{ .shr, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 5, .none }, - .{ .shr, .m1, .rm8, .unity, .none, .none, &.{ 0xd0 }, 5, .rex }, - .{ .shr, .m1, .rm16, .unity, .none, .none, &.{ 0xd1 }, 5, .none }, - .{ .shr, .m1, .rm32, .unity, .none, .none, &.{ 0xd1 }, 5, .none }, - .{ .shr, .m1, .rm64, .unity, .none, .none, &.{ 0xd1 }, 5, .long }, - .{ .shr, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 5, .none }, - .{ .shr, .mc, .rm8, .cl, .none, .none, &.{ 0xd2 }, 5, .rex }, - .{ .shr, .mc, .rm16, .cl, .none, .none, &.{ 0xd3 }, 5, .none }, - .{ .shr, .mc, .rm32, .cl, .none, .none, &.{ 0xd3 }, 5, .none }, - .{ .shr, .mc, .rm64, .cl, .none, .none, &.{ 0xd3 }, 5, .long }, - .{ .shr, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 5, .none }, - .{ .shr, .mi, .rm8, .imm8, .none, .none, &.{ 0xc0 }, 5, .rex }, - .{ .shr, .mi, .rm16, .imm8, .none, .none, &.{ 0xc1 }, 5, .none }, - .{ .shr, .mi, .rm32, .imm8, .none, .none, &.{ 0xc1 }, 5, .none }, - .{ .shr, .mi, .rm64, .imm8, .none, .none, &.{ 0xc1 }, 5, .long }, - - .{ .shrd, .mri, .rm16, .r16, .imm8, .none, &.{ 0x0f, 0xac }, 0, .none }, - .{ .shrd, .mrc, .rm16, .r16, .cl, .none, &.{ 0x0f, 0xad }, 0, .none }, - .{ .shrd, .mri, .rm32, .r32, .imm8, .none, &.{ 0x0f, 0xac }, 0, .none }, - .{ .shrd, .mri, .rm64, .r64, .imm8, .none, &.{ 0x0f, 0xac }, 0, .long }, - .{ .shrd, .mrc, .rm32, .r32, .cl, .none, &.{ 0x0f, 0xad }, 0, .none }, - .{ .shrd, .mrc, .rm64, .r64, .cl, .none, &.{ 0x0f, 0xad }, 0, .long }, - - .{ .stos, .np, .m8, .none, .none, .none, &.{ 0xaa }, 0, .none }, - .{ .stos, .np, .m16, .none, .none, .none, &.{ 0xab }, 0, .none }, - .{ .stos, .np, .m32, .none, .none, .none, &.{ 0xab }, 0, .none }, - .{ .stos, .np, .m64, .none, .none, .none, &.{ 0xab }, 0, .long }, - .{ .stosb, .np, .none, .none, .none, .none, &.{ 0xaa }, 0, .none }, - .{ .stosw, .np, .none, .none, .none, .none, &.{ 0xab }, 0, .short }, - .{ .stosd, .np, .none, .none, .none, .none, &.{ 0xab }, 0, .none }, - .{ .stosq, .np, .none, .none, .none, .none, &.{ 0xab }, 0, .long }, - - .{ .sub, .zi, .al, .imm8, .none, .none, &.{ 0x2c }, 0, .none }, - .{ .sub, .zi, .ax, .imm16, .none, .none, &.{ 0x2d }, 0, .none }, - .{ .sub, .zi, .eax, .imm32, .none, .none, &.{ 0x2d }, 0, .none }, - .{ .sub, .zi, .rax, .imm32s, .none, .none, &.{ 0x2d }, 0, .long }, - .{ .sub, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 5, .none }, - .{ .sub, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 5, .rex }, - .{ .sub, .mi, .rm16, .imm16, .none, .none, &.{ 0x81 }, 5, .none }, - .{ .sub, .mi, .rm32, .imm32, .none, .none, &.{ 0x81 }, 5, .none }, - .{ .sub, .mi, .rm64, .imm32s, .none, .none, &.{ 0x81 }, 5, .long }, - .{ .sub, .mi, .rm16, .imm8s, .none, .none, &.{ 0x83 }, 5, .none }, - .{ .sub, .mi, .rm32, .imm8s, .none, .none, &.{ 0x83 }, 5, .none }, - .{ .sub, .mi, .rm64, .imm8s, .none, .none, &.{ 0x83 }, 5, .long }, - .{ .sub, .mr, .rm8, .r8, .none, .none, &.{ 0x28 }, 0, .none }, - .{ .sub, .mr, .rm8, .r8, .none, .none, &.{ 0x28 }, 0, .rex }, - .{ .sub, .mr, .rm16, .r16, .none, .none, &.{ 0x29 }, 0, .none }, - .{ .sub, .mr, .rm32, .r32, .none, .none, &.{ 0x29 }, 0, .none }, - .{ .sub, .mr, .rm64, .r64, .none, .none, &.{ 0x29 }, 0, .long }, - .{ .sub, .rm, .r8, .rm8, .none, .none, &.{ 0x2a }, 0, .none }, - .{ .sub, .rm, .r8, .rm8, .none, .none, &.{ 0x2a }, 0, .rex }, - .{ .sub, .rm, .r16, .rm16, .none, .none, &.{ 0x2b }, 0, .none }, - .{ .sub, .rm, .r32, .rm32, .none, .none, &.{ 0x2b }, 0, .none }, - .{ .sub, .rm, .r64, .rm64, .none, .none, &.{ 0x2b }, 0, .long }, - - .{ .syscall, .np, .none, .none, .none, .none, &.{ 0x0f, 0x05 }, 0, .none } + .{ .movsxd, .rm, &.{ .r32, .rm32 }, &.{ 0x63 }, 0, .none }, + .{ .movsxd, .rm, &.{ .r64, .rm32 }, &.{ 0x63 }, 0, .long }, + + .{ .movzx, .rm, &.{ .r16, .rm8 }, &.{ 0x0f, 0xb6 }, 0, .none }, + .{ .movzx, .rm, &.{ .r32, .rm8 }, &.{ 0x0f, 0xb6 }, 0, .none }, + .{ .movzx, .rm, &.{ .r64, .rm8 }, &.{ 0x0f, 0xb6 }, 0, .long }, + .{ .movzx, .rm, &.{ .r32, .rm16 }, &.{ 0x0f, 0xb7 }, 0, .none }, + .{ .movzx, .rm, &.{ .r64, .rm16 }, &.{ 0x0f, 0xb7 }, 0, .long }, + + .{ .mul, .m, &.{ .rm8 }, &.{ 0xf6 }, 4, .none }, + .{ .mul, .m, &.{ .rm8 }, &.{ 0xf6 }, 4, .rex }, + .{ .mul, .m, &.{ .rm16 }, &.{ 0xf7 }, 4, .none }, + .{ .mul, .m, &.{ .rm32 }, &.{ 0xf7 }, 4, .none }, + .{ .mul, .m, &.{ .rm64 }, &.{ 0xf7 }, 4, .long }, + + .{ .neg, .m, &.{ .rm8 }, &.{ 0xf6 }, 3, .none }, + .{ .neg, .m, &.{ .rm8 }, &.{ 0xf6 }, 3, .rex }, + .{ .neg, .m, &.{ .rm16 }, &.{ 0xf7 }, 3, .none }, + .{ .neg, .m, &.{ .rm32 }, &.{ 0xf7 }, 3, .none }, + .{ .neg, .m, &.{ .rm64 }, &.{ 0xf7 }, 3, .long }, + + .{ .nop, .np, &.{}, &.{ 0x90 }, 0, .none }, + + .{ .not, .m, &.{ .rm8 }, &.{ 0xf6 }, 2, .none }, + .{ .not, .m, &.{ .rm8 }, &.{ 0xf6 }, 2, .rex }, + .{ .not, .m, &.{ .rm16 }, &.{ 0xf7 }, 2, .none }, + .{ .not, .m, &.{ .rm32 }, &.{ 0xf7 }, 2, .none }, + .{ .not, .m, &.{ .rm64 }, &.{ 0xf7 }, 2, .long }, + + .{ .@"or", .zi, &.{ .al, .imm8 }, &.{ 0x0c }, 0, .none }, + .{ .@"or", .zi, &.{ .ax, .imm16 }, &.{ 0x0d }, 0, .none }, + .{ .@"or", .zi, &.{ .eax, .imm32 }, &.{ 0x0d }, 0, .none }, + .{ .@"or", .zi, &.{ .rax, .imm32s }, &.{ 0x0d }, 0, .long }, + .{ .@"or", .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 1, .none }, + .{ .@"or", .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 1, .rex }, + .{ .@"or", .mi, &.{ .rm16, .imm16 }, &.{ 0x81 }, 1, .none }, + .{ .@"or", .mi, &.{ .rm32, .imm32 }, &.{ 0x81 }, 1, .none }, + .{ .@"or", .mi, &.{ .rm64, .imm32s }, &.{ 0x81 }, 1, .long }, + .{ .@"or", .mi, &.{ .rm16, .imm8s }, &.{ 0x83 }, 1, .none }, + .{ .@"or", .mi, &.{ .rm32, .imm8s }, &.{ 0x83 }, 1, .none }, + .{ .@"or", .mi, &.{ .rm64, .imm8s }, &.{ 0x83 }, 1, .long }, + .{ .@"or", .mr, &.{ .rm8, .r8 }, &.{ 0x08 }, 0, .none }, + .{ .@"or", .mr, &.{ .rm8, .r8 }, &.{ 0x08 }, 0, .rex }, + .{ .@"or", .mr, &.{ .rm16, .r16 }, &.{ 0x09 }, 0, .none }, + .{ .@"or", .mr, &.{ .rm32, .r32 }, &.{ 0x09 }, 0, .none }, + .{ .@"or", .mr, &.{ .rm64, .r64 }, &.{ 0x09 }, 0, .long }, + .{ .@"or", .rm, &.{ .r8, .rm8 }, &.{ 0x0a }, 0, .none }, + .{ .@"or", .rm, &.{ .r8, .rm8 }, &.{ 0x0a }, 0, .rex }, + .{ .@"or", .rm, &.{ .r16, .rm16 }, &.{ 0x0b }, 0, .none }, + .{ .@"or", .rm, &.{ .r32, .rm32 }, &.{ 0x0b }, 0, .none }, + .{ .@"or", .rm, &.{ .r64, .rm64 }, &.{ 0x0b }, 0, .long }, + + .{ .pop, .o, &.{ .r16 }, &.{ 0x58 }, 0, .none }, + .{ .pop, .o, &.{ .r64 }, &.{ 0x58 }, 0, .none }, + .{ .pop, .m, &.{ .rm16 }, &.{ 0x8f }, 0, .none }, + .{ .pop, .m, &.{ .rm64 }, &.{ 0x8f }, 0, .none }, + + .{ .popcnt, .rm, &.{ .r16, .rm16 }, &.{ 0xf3, 0x0f, 0xb8 }, 0, .none }, + .{ .popcnt, .rm, &.{ .r32, .rm32 }, &.{ 0xf3, 0x0f, 0xb8 }, 0, .none }, + .{ .popcnt, .rm, &.{ .r64, .rm64 }, &.{ 0xf3, 0x0f, 0xb8 }, 0, .long }, + + .{ .push, .o, &.{ .r16 }, &.{ 0x50 }, 0, .none }, + .{ .push, .o, &.{ .r64 }, &.{ 0x50 }, 0, .none }, + .{ .push, .m, &.{ .rm16 }, &.{ 0xff }, 6, .none }, + .{ .push, .m, &.{ .rm64 }, &.{ 0xff }, 6, .none }, + .{ .push, .i, &.{ .imm8 }, &.{ 0x6a }, 0, .none }, + .{ .push, .i, &.{ .imm16 }, &.{ 0x68 }, 0, .none }, + .{ .push, .i, &.{ .imm32 }, &.{ 0x68 }, 0, .none }, + + .{ .ret, .np, &.{}, &.{ 0xc3 }, 0, .none }, + + .{ .rcl, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 2, .none }, + .{ .rcl, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 2, .rex }, + .{ .rcl, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 2, .none }, + .{ .rcl, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 2, .rex }, + .{ .rcl, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 2, .none }, + .{ .rcl, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 2, .rex }, + .{ .rcl, .m1, &.{ .rm16, .unity }, &.{ 0xd1 }, 2, .none }, + .{ .rcl, .mc, &.{ .rm16, .cl }, &.{ 0xd3 }, 2, .none }, + .{ .rcl, .mi, &.{ .rm16, .imm8 }, &.{ 0xc1 }, 2, .none }, + .{ .rcl, .m1, &.{ .rm32, .unity }, &.{ 0xd1 }, 2, .none }, + .{ .rcl, .m1, &.{ .rm64, .unity }, &.{ 0xd1 }, 2, .long }, + .{ .rcl, .mc, &.{ .rm32, .cl }, &.{ 0xd3 }, 2, .none }, + .{ .rcl, .mc, &.{ .rm64, .cl }, &.{ 0xd3 }, 2, .long }, + .{ .rcl, .mi, &.{ .rm32, .imm8 }, &.{ 0xc1 }, 2, .none }, + .{ .rcl, .mi, &.{ .rm64, .imm8 }, &.{ 0xc1 }, 2, .long }, + + .{ .rcr, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 3, .none }, + .{ .rcr, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 3, .rex }, + .{ .rcr, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 3, .none }, + .{ .rcr, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 3, .rex }, + .{ .rcr, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 3, .none }, + .{ .rcr, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 3, .rex }, + .{ .rcr, .m1, &.{ .rm16, .unity }, &.{ 0xd1 }, 3, .none }, + .{ .rcr, .mc, &.{ .rm16, .cl }, &.{ 0xd3 }, 3, .none }, + .{ .rcr, .mi, &.{ .rm16, .imm8 }, &.{ 0xc1 }, 3, .none }, + .{ .rcr, .m1, &.{ .rm32, .unity }, &.{ 0xd1 }, 3, .none }, + .{ .rcr, .m1, &.{ .rm64, .unity }, &.{ 0xd1 }, 3, .long }, + .{ .rcr, .mc, &.{ .rm32, .cl }, &.{ 0xd3 }, 3, .none }, + .{ .rcr, .mc, &.{ .rm64, .cl }, &.{ 0xd3 }, 3, .long }, + .{ .rcr, .mi, &.{ .rm32, .imm8 }, &.{ 0xc1 }, 3, .none }, + .{ .rcr, .mi, &.{ .rm64, .imm8 }, &.{ 0xc1 }, 3, .long }, + + .{ .rol, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 0, .none }, + .{ .rol, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 0, .rex }, + .{ .rol, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 0, .none }, + .{ .rol, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 0, .rex }, + .{ .rol, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 0, .none }, + .{ .rol, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 0, .rex }, + .{ .rol, .m1, &.{ .rm16, .unity }, &.{ 0xd1 }, 0, .none }, + .{ .rol, .mc, &.{ .rm16, .cl }, &.{ 0xd3 }, 0, .none }, + .{ .rol, .mi, &.{ .rm16, .imm8 }, &.{ 0xc1 }, 0, .none }, + .{ .rol, .m1, &.{ .rm32, .unity }, &.{ 0xd1 }, 0, .none }, + .{ .rol, .m1, &.{ .rm64, .unity }, &.{ 0xd1 }, 0, .long }, + .{ .rol, .mc, &.{ .rm32, .cl }, &.{ 0xd3 }, 0, .none }, + .{ .rol, .mc, &.{ .rm64, .cl }, &.{ 0xd3 }, 0, .long }, + .{ .rol, .mi, &.{ .rm32, .imm8 }, &.{ 0xc1 }, 0, .none }, + .{ .rol, .mi, &.{ .rm64, .imm8 }, &.{ 0xc1 }, 0, .long }, + + .{ .ror, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 1, .none }, + .{ .ror, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 1, .rex }, + .{ .ror, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 1, .none }, + .{ .ror, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 1, .rex }, + .{ .ror, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 1, .none }, + .{ .ror, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 1, .rex }, + .{ .ror, .m1, &.{ .rm16, .unity }, &.{ 0xd1 }, 1, .none }, + .{ .ror, .mc, &.{ .rm16, .cl }, &.{ 0xd3 }, 1, .none }, + .{ .ror, .mi, &.{ .rm16, .imm8 }, &.{ 0xc1 }, 1, .none }, + .{ .ror, .m1, &.{ .rm32, .unity }, &.{ 0xd1 }, 1, .none }, + .{ .ror, .m1, &.{ .rm64, .unity }, &.{ 0xd1 }, 1, .long }, + .{ .ror, .mc, &.{ .rm32, .cl }, &.{ 0xd3 }, 1, .none }, + .{ .ror, .mc, &.{ .rm64, .cl }, &.{ 0xd3 }, 1, .long }, + .{ .ror, .mi, &.{ .rm32, .imm8 }, &.{ 0xc1 }, 1, .none }, + .{ .ror, .mi, &.{ .rm64, .imm8 }, &.{ 0xc1 }, 1, .long }, + + .{ .sal, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 4, .none }, + .{ .sal, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 4, .rex }, + .{ .sal, .m1, &.{ .rm16, .unity }, &.{ 0xd1 }, 4, .none }, + .{ .sal, .m1, &.{ .rm32, .unity }, &.{ 0xd1 }, 4, .none }, + .{ .sal, .m1, &.{ .rm64, .unity }, &.{ 0xd1 }, 4, .long }, + .{ .sal, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 4, .none }, + .{ .sal, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 4, .rex }, + .{ .sal, .mc, &.{ .rm16, .cl }, &.{ 0xd3 }, 4, .none }, + .{ .sal, .mc, &.{ .rm32, .cl }, &.{ 0xd3 }, 4, .none }, + .{ .sal, .mc, &.{ .rm64, .cl }, &.{ 0xd3 }, 4, .long }, + .{ .sal, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 4, .none }, + .{ .sal, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 4, .rex }, + .{ .sal, .mi, &.{ .rm16, .imm8 }, &.{ 0xc1 }, 4, .none }, + .{ .sal, .mi, &.{ .rm32, .imm8 }, &.{ 0xc1 }, 4, .none }, + .{ .sal, .mi, &.{ .rm64, .imm8 }, &.{ 0xc1 }, 4, .long }, + + .{ .sar, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 7, .none }, + .{ .sar, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 7, .rex }, + .{ .sar, .m1, &.{ .rm16, .unity }, &.{ 0xd1 }, 7, .none }, + .{ .sar, .m1, &.{ .rm32, .unity }, &.{ 0xd1 }, 7, .none }, + .{ .sar, .m1, &.{ .rm64, .unity }, &.{ 0xd1 }, 7, .long }, + .{ .sar, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 7, .none }, + .{ .sar, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 7, .rex }, + .{ .sar, .mc, &.{ .rm16, .cl }, &.{ 0xd3 }, 7, .none }, + .{ .sar, .mc, &.{ .rm32, .cl }, &.{ 0xd3 }, 7, .none }, + .{ .sar, .mc, &.{ .rm64, .cl }, &.{ 0xd3 }, 7, .long }, + .{ .sar, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 7, .none }, + .{ .sar, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 7, .rex }, + .{ .sar, .mi, &.{ .rm16, .imm8 }, &.{ 0xc1 }, 7, .none }, + .{ .sar, .mi, &.{ .rm32, .imm8 }, &.{ 0xc1 }, 7, .none }, + .{ .sar, .mi, &.{ .rm64, .imm8 }, &.{ 0xc1 }, 7, .long }, + + .{ .sbb, .zi, &.{ .al, .imm8 }, &.{ 0x1c }, 0, .none }, + .{ .sbb, .zi, &.{ .ax, .imm16 }, &.{ 0x1d }, 0, .none }, + .{ .sbb, .zi, &.{ .eax, .imm32 }, &.{ 0x1d }, 0, .none }, + .{ .sbb, .zi, &.{ .rax, .imm32s }, &.{ 0x1d }, 0, .long }, + .{ .sbb, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 3, .none }, + .{ .sbb, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 3, .rex }, + .{ .sbb, .mi, &.{ .rm16, .imm16 }, &.{ 0x81 }, 3, .none }, + .{ .sbb, .mi, &.{ .rm32, .imm32 }, &.{ 0x81 }, 3, .none }, + .{ .sbb, .mi, &.{ .rm64, .imm32s }, &.{ 0x81 }, 3, .long }, + .{ .sbb, .mi, &.{ .rm16, .imm8s }, &.{ 0x83 }, 3, .none }, + .{ .sbb, .mi, &.{ .rm32, .imm8s }, &.{ 0x83 }, 3, .none }, + .{ .sbb, .mi, &.{ .rm64, .imm8s }, &.{ 0x83 }, 3, .long }, + .{ .sbb, .mr, &.{ .rm8, .r8 }, &.{ 0x18 }, 0, .none }, + .{ .sbb, .mr, &.{ .rm8, .r8 }, &.{ 0x18 }, 0, .rex }, + .{ .sbb, .mr, &.{ .rm16, .r16 }, &.{ 0x19 }, 0, .none }, + .{ .sbb, .mr, &.{ .rm32, .r32 }, &.{ 0x19 }, 0, .none }, + .{ .sbb, .mr, &.{ .rm64, .r64 }, &.{ 0x19 }, 0, .long }, + .{ .sbb, .rm, &.{ .r8, .rm8 }, &.{ 0x1a }, 0, .none }, + .{ .sbb, .rm, &.{ .r8, .rm8 }, &.{ 0x1a }, 0, .rex }, + .{ .sbb, .rm, &.{ .r16, .rm16 }, &.{ 0x1b }, 0, .none }, + .{ .sbb, .rm, &.{ .r32, .rm32 }, &.{ 0x1b }, 0, .none }, + .{ .sbb, .rm, &.{ .r64, .rm64 }, &.{ 0x1b }, 0, .long }, + + .{ .scas, .np, &.{ .m8 }, &.{ 0xae }, 0, .none }, + .{ .scas, .np, &.{ .m16 }, &.{ 0xaf }, 0, .none }, + .{ .scas, .np, &.{ .m32 }, &.{ 0xaf }, 0, .none }, + .{ .scas, .np, &.{ .m64 }, &.{ 0xaf }, 0, .long }, + + .{ .scasb, .np, &.{}, &.{ 0xae }, 0, .none }, + .{ .scasw, .np, &.{}, &.{ 0xaf }, 0, .short }, + .{ .scasd, .np, &.{}, &.{ 0xaf }, 0, .none }, + .{ .scasq, .np, &.{}, &.{ 0xaf }, 0, .long }, + + .{ .seta, .m, &.{ .rm8 }, &.{ 0x0f, 0x97 }, 0, .none }, + .{ .seta, .m, &.{ .rm8 }, &.{ 0x0f, 0x97 }, 0, .rex }, + .{ .setae, .m, &.{ .rm8 }, &.{ 0x0f, 0x93 }, 0, .none }, + .{ .setae, .m, &.{ .rm8 }, &.{ 0x0f, 0x93 }, 0, .rex }, + .{ .setb, .m, &.{ .rm8 }, &.{ 0x0f, 0x92 }, 0, .none }, + .{ .setb, .m, &.{ .rm8 }, &.{ 0x0f, 0x92 }, 0, .rex }, + .{ .setbe, .m, &.{ .rm8 }, &.{ 0x0f, 0x96 }, 0, .none }, + .{ .setbe, .m, &.{ .rm8 }, &.{ 0x0f, 0x96 }, 0, .rex }, + .{ .setc, .m, &.{ .rm8 }, &.{ 0x0f, 0x92 }, 0, .none }, + .{ .setc, .m, &.{ .rm8 }, &.{ 0x0f, 0x92 }, 0, .rex }, + .{ .sete, .m, &.{ .rm8 }, &.{ 0x0f, 0x94 }, 0, .none }, + .{ .sete, .m, &.{ .rm8 }, &.{ 0x0f, 0x94 }, 0, .rex }, + .{ .setg, .m, &.{ .rm8 }, &.{ 0x0f, 0x9f }, 0, .none }, + .{ .setg, .m, &.{ .rm8 }, &.{ 0x0f, 0x9f }, 0, .rex }, + .{ .setge, .m, &.{ .rm8 }, &.{ 0x0f, 0x9d }, 0, .none }, + .{ .setge, .m, &.{ .rm8 }, &.{ 0x0f, 0x9d }, 0, .rex }, + .{ .setl, .m, &.{ .rm8 }, &.{ 0x0f, 0x9c }, 0, .none }, + .{ .setl, .m, &.{ .rm8 }, &.{ 0x0f, 0x9c }, 0, .rex }, + .{ .setle, .m, &.{ .rm8 }, &.{ 0x0f, 0x9e }, 0, .none }, + .{ .setle, .m, &.{ .rm8 }, &.{ 0x0f, 0x9e }, 0, .rex }, + .{ .setna, .m, &.{ .rm8 }, &.{ 0x0f, 0x96 }, 0, .none }, + .{ .setna, .m, &.{ .rm8 }, &.{ 0x0f, 0x96 }, 0, .rex }, + .{ .setnae, .m, &.{ .rm8 }, &.{ 0x0f, 0x92 }, 0, .none }, + .{ .setnae, .m, &.{ .rm8 }, &.{ 0x0f, 0x92 }, 0, .rex }, + .{ .setnb, .m, &.{ .rm8 }, &.{ 0x0f, 0x93 }, 0, .none }, + .{ .setnb, .m, &.{ .rm8 }, &.{ 0x0f, 0x93 }, 0, .rex }, + .{ .setnbe, .m, &.{ .rm8 }, &.{ 0x0f, 0x97 }, 0, .none }, + .{ .setnbe, .m, &.{ .rm8 }, &.{ 0x0f, 0x97 }, 0, .rex }, + .{ .setnc, .m, &.{ .rm8 }, &.{ 0x0f, 0x93 }, 0, .none }, + .{ .setnc, .m, &.{ .rm8 }, &.{ 0x0f, 0x93 }, 0, .rex }, + .{ .setne, .m, &.{ .rm8 }, &.{ 0x0f, 0x95 }, 0, .none }, + .{ .setne, .m, &.{ .rm8 }, &.{ 0x0f, 0x95 }, 0, .rex }, + .{ .setng, .m, &.{ .rm8 }, &.{ 0x0f, 0x9e }, 0, .none }, + .{ .setng, .m, &.{ .rm8 }, &.{ 0x0f, 0x9e }, 0, .rex }, + .{ .setnge, .m, &.{ .rm8 }, &.{ 0x0f, 0x9c }, 0, .none }, + .{ .setnge, .m, &.{ .rm8 }, &.{ 0x0f, 0x9c }, 0, .rex }, + .{ .setnl, .m, &.{ .rm8 }, &.{ 0x0f, 0x9d }, 0, .none }, + .{ .setnl, .m, &.{ .rm8 }, &.{ 0x0f, 0x9d }, 0, .rex }, + .{ .setnle, .m, &.{ .rm8 }, &.{ 0x0f, 0x9f }, 0, .none }, + .{ .setnle, .m, &.{ .rm8 }, &.{ 0x0f, 0x9f }, 0, .rex }, + .{ .setno, .m, &.{ .rm8 }, &.{ 0x0f, 0x91 }, 0, .none }, + .{ .setno, .m, &.{ .rm8 }, &.{ 0x0f, 0x91 }, 0, .rex }, + .{ .setnp, .m, &.{ .rm8 }, &.{ 0x0f, 0x9b }, 0, .none }, + .{ .setnp, .m, &.{ .rm8 }, &.{ 0x0f, 0x9b }, 0, .rex }, + .{ .setns, .m, &.{ .rm8 }, &.{ 0x0f, 0x99 }, 0, .none }, + .{ .setns, .m, &.{ .rm8 }, &.{ 0x0f, 0x99 }, 0, .rex }, + .{ .setnz, .m, &.{ .rm8 }, &.{ 0x0f, 0x95 }, 0, .none }, + .{ .setnz, .m, &.{ .rm8 }, &.{ 0x0f, 0x95 }, 0, .rex }, + .{ .seto, .m, &.{ .rm8 }, &.{ 0x0f, 0x90 }, 0, .none }, + .{ .seto, .m, &.{ .rm8 }, &.{ 0x0f, 0x90 }, 0, .rex }, + .{ .setp, .m, &.{ .rm8 }, &.{ 0x0f, 0x9a }, 0, .none }, + .{ .setp, .m, &.{ .rm8 }, &.{ 0x0f, 0x9a }, 0, .rex }, + .{ .setpe, .m, &.{ .rm8 }, &.{ 0x0f, 0x9a }, 0, .none }, + .{ .setpe, .m, &.{ .rm8 }, &.{ 0x0f, 0x9a }, 0, .rex }, + .{ .setpo, .m, &.{ .rm8 }, &.{ 0x0f, 0x9b }, 0, .none }, + .{ .setpo, .m, &.{ .rm8 }, &.{ 0x0f, 0x9b }, 0, .rex }, + .{ .sets, .m, &.{ .rm8 }, &.{ 0x0f, 0x98 }, 0, .none }, + .{ .sets, .m, &.{ .rm8 }, &.{ 0x0f, 0x98 }, 0, .rex }, + .{ .setz, .m, &.{ .rm8 }, &.{ 0x0f, 0x94 }, 0, .none }, + .{ .setz, .m, &.{ .rm8 }, &.{ 0x0f, 0x94 }, 0, .rex }, + + .{ .sfence, .np, &.{}, &.{ 0x0f, 0xae, 0xf8 }, 0, .none }, + + .{ .shl, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 4, .none }, + .{ .shl, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 4, .rex }, + .{ .shl, .m1, &.{ .rm16, .unity }, &.{ 0xd1 }, 4, .none }, + .{ .shl, .m1, &.{ .rm32, .unity }, &.{ 0xd1 }, 4, .none }, + .{ .shl, .m1, &.{ .rm64, .unity }, &.{ 0xd1 }, 4, .long }, + .{ .shl, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 4, .none }, + .{ .shl, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 4, .rex }, + .{ .shl, .mc, &.{ .rm16, .cl }, &.{ 0xd3 }, 4, .none }, + .{ .shl, .mc, &.{ .rm32, .cl }, &.{ 0xd3 }, 4, .none }, + .{ .shl, .mc, &.{ .rm64, .cl }, &.{ 0xd3 }, 4, .long }, + .{ .shl, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 4, .none }, + .{ .shl, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 4, .rex }, + .{ .shl, .mi, &.{ .rm16, .imm8 }, &.{ 0xc1 }, 4, .none }, + .{ .shl, .mi, &.{ .rm32, .imm8 }, &.{ 0xc1 }, 4, .none }, + .{ .shl, .mi, &.{ .rm64, .imm8 }, &.{ 0xc1 }, 4, .long }, + + .{ .shld, .mri, &.{ .rm16, .r16, .imm8 }, &.{ 0x0f, 0xa4 }, 0, .none }, + .{ .shld, .mrc, &.{ .rm16, .r16, .cl }, &.{ 0x0f, 0xa5 }, 0, .none }, + .{ .shld, .mri, &.{ .rm32, .r32, .imm8 }, &.{ 0x0f, 0xa4 }, 0, .none }, + .{ .shld, .mri, &.{ .rm64, .r64, .imm8 }, &.{ 0x0f, 0xa4 }, 0, .long }, + .{ .shld, .mrc, &.{ .rm32, .r32, .cl }, &.{ 0x0f, 0xa5 }, 0, .none }, + .{ .shld, .mrc, &.{ .rm64, .r64, .cl }, &.{ 0x0f, 0xa5 }, 0, .long }, + + .{ .shr, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 5, .none }, + .{ .shr, .m1, &.{ .rm8, .unity }, &.{ 0xd0 }, 5, .rex }, + .{ .shr, .m1, &.{ .rm16, .unity }, &.{ 0xd1 }, 5, .none }, + .{ .shr, .m1, &.{ .rm32, .unity }, &.{ 0xd1 }, 5, .none }, + .{ .shr, .m1, &.{ .rm64, .unity }, &.{ 0xd1 }, 5, .long }, + .{ .shr, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 5, .none }, + .{ .shr, .mc, &.{ .rm8, .cl }, &.{ 0xd2 }, 5, .rex }, + .{ .shr, .mc, &.{ .rm16, .cl }, &.{ 0xd3 }, 5, .none }, + .{ .shr, .mc, &.{ .rm32, .cl }, &.{ 0xd3 }, 5, .none }, + .{ .shr, .mc, &.{ .rm64, .cl }, &.{ 0xd3 }, 5, .long }, + .{ .shr, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 5, .none }, + .{ .shr, .mi, &.{ .rm8, .imm8 }, &.{ 0xc0 }, 5, .rex }, + .{ .shr, .mi, &.{ .rm16, .imm8 }, &.{ 0xc1 }, 5, .none }, + .{ .shr, .mi, &.{ .rm32, .imm8 }, &.{ 0xc1 }, 5, .none }, + .{ .shr, .mi, &.{ .rm64, .imm8 }, &.{ 0xc1 }, 5, .long }, + + .{ .shrd, .mri, &.{ .rm16, .r16, .imm8 }, &.{ 0x0f, 0xac }, 0, .none }, + .{ .shrd, .mrc, &.{ .rm16, .r16, .cl }, &.{ 0x0f, 0xad }, 0, .none }, + .{ .shrd, .mri, &.{ .rm32, .r32, .imm8 }, &.{ 0x0f, 0xac }, 0, .none }, + .{ .shrd, .mri, &.{ .rm64, .r64, .imm8 }, &.{ 0x0f, 0xac }, 0, .long }, + .{ .shrd, .mrc, &.{ .rm32, .r32, .cl }, &.{ 0x0f, 0xad }, 0, .none }, + .{ .shrd, .mrc, &.{ .rm64, .r64, .cl }, &.{ 0x0f, 0xad }, 0, .long }, + + .{ .stos, .np, &.{ .m8 }, &.{ 0xaa }, 0, .none }, + .{ .stos, .np, &.{ .m16 }, &.{ 0xab }, 0, .none }, + .{ .stos, .np, &.{ .m32 }, &.{ 0xab }, 0, .none }, + .{ .stos, .np, &.{ .m64 }, &.{ 0xab }, 0, .long }, + + .{ .stosb, .np, &.{}, &.{ 0xaa }, 0, .none }, + .{ .stosw, .np, &.{}, &.{ 0xab }, 0, .short }, + .{ .stosd, .np, &.{}, &.{ 0xab }, 0, .none }, + .{ .stosq, .np, &.{}, &.{ 0xab }, 0, .long }, + + .{ .sub, .zi, &.{ .al, .imm8 }, &.{ 0x2c }, 0, .none }, + .{ .sub, .zi, &.{ .ax, .imm16 }, &.{ 0x2d }, 0, .none }, + .{ .sub, .zi, &.{ .eax, .imm32 }, &.{ 0x2d }, 0, .none }, + .{ .sub, .zi, &.{ .rax, .imm32s }, &.{ 0x2d }, 0, .long }, + .{ .sub, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 5, .none }, + .{ .sub, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 5, .rex }, + .{ .sub, .mi, &.{ .rm16, .imm16 }, &.{ 0x81 }, 5, .none }, + .{ .sub, .mi, &.{ .rm32, .imm32 }, &.{ 0x81 }, 5, .none }, + .{ .sub, .mi, &.{ .rm64, .imm32s }, &.{ 0x81 }, 5, .long }, + .{ .sub, .mi, &.{ .rm16, .imm8s }, &.{ 0x83 }, 5, .none }, + .{ .sub, .mi, &.{ .rm32, .imm8s }, &.{ 0x83 }, 5, .none }, + .{ .sub, .mi, &.{ .rm64, .imm8s }, &.{ 0x83 }, 5, .long }, + .{ .sub, .mr, &.{ .rm8, .r8 }, &.{ 0x28 }, 0, .none }, + .{ .sub, .mr, &.{ .rm8, .r8 }, &.{ 0x28 }, 0, .rex }, + .{ .sub, .mr, &.{ .rm16, .r16 }, &.{ 0x29 }, 0, .none }, + .{ .sub, .mr, &.{ .rm32, .r32 }, &.{ 0x29 }, 0, .none }, + .{ .sub, .mr, &.{ .rm64, .r64 }, &.{ 0x29 }, 0, .long }, + .{ .sub, .rm, &.{ .r8, .rm8 }, &.{ 0x2a }, 0, .none }, + .{ .sub, .rm, &.{ .r8, .rm8 }, &.{ 0x2a }, 0, .rex }, + .{ .sub, .rm, &.{ .r16, .rm16 }, &.{ 0x2b }, 0, .none }, + .{ .sub, .rm, &.{ .r32, .rm32 }, &.{ 0x2b }, 0, .none }, + .{ .sub, .rm, &.{ .r64, .rm64 }, &.{ 0x2b }, 0, .long }, + + .{ .syscall, .np, &.{}, &.{ 0x0f, 0x05 }, 0, .none } , - .{ .@"test", .zi, .al, .imm8, .none, .none, &.{ 0xa8 }, 0, .none }, - .{ .@"test", .zi, .ax, .imm16, .none, .none, &.{ 0xa9 }, 0, .none }, - .{ .@"test", .zi, .eax, .imm32, .none, .none, &.{ 0xa9 }, 0, .none }, - .{ .@"test", .zi, .rax, .imm32s, .none, .none, &.{ 0xa9 }, 0, .long }, - .{ .@"test", .mi, .rm8, .imm8, .none, .none, &.{ 0xf6 }, 0, .none }, - .{ .@"test", .mi, .rm8, .imm8, .none, .none, &.{ 0xf6 }, 0, .rex }, - .{ .@"test", .mi, .rm16, .imm16, .none, .none, &.{ 0xf7 }, 0, .none }, - .{ .@"test", .mi, .rm32, .imm32, .none, .none, &.{ 0xf7 }, 0, .none }, - .{ .@"test", .mi, .rm64, .imm32s, .none, .none, &.{ 0xf7 }, 0, .long }, - .{ .@"test", .mr, .rm8, .r8, .none, .none, &.{ 0x84 }, 0, .none }, - .{ .@"test", .mr, .rm8, .r8, .none, .none, &.{ 0x84 }, 0, .rex }, - .{ .@"test", .mr, .rm16, .r16, .none, .none, &.{ 0x85 }, 0, .none }, - .{ .@"test", .mr, .rm32, .r32, .none, .none, &.{ 0x85 }, 0, .none }, - .{ .@"test", .mr, .rm64, .r64, .none, .none, &.{ 0x85 }, 0, .long }, - - .{ .tzcnt, .rm, .r16, .rm16, .none, .none, &.{ 0xf3, 0x0f, 0xbc }, 0, .none }, - .{ .tzcnt, .rm, .r32, .rm32, .none, .none, &.{ 0xf3, 0x0f, 0xbc }, 0, .none }, - .{ .tzcnt, .rm, .r64, .rm64, .none, .none, &.{ 0xf3, 0x0f, 0xbc }, 0, .long }, - - .{ .ud2, .np, .none, .none, .none, .none, &.{ 0x0f, 0x0b }, 0, .none }, - - .{ .xadd, .mr, .rm8, .r8, .none, .none, &.{ 0x0f, 0xc0 }, 0, .none }, - .{ .xadd, .mr, .rm8, .r8, .none, .none, &.{ 0x0f, 0xc0 }, 0, .rex }, - .{ .xadd, .mr, .rm16, .r16, .none, .none, &.{ 0x0f, 0xc1 }, 0, .none }, - .{ .xadd, .mr, .rm32, .r32, .none, .none, &.{ 0x0f, 0xc1 }, 0, .none }, - .{ .xadd, .mr, .rm64, .r64, .none, .none, &.{ 0x0f, 0xc1 }, 0, .long }, - - .{ .xchg, .o, .ax, .r16, .none, .none, &.{ 0x90 }, 0, .none }, - .{ .xchg, .o, .r16, .ax, .none, .none, &.{ 0x90 }, 0, .none }, - .{ .xchg, .o, .eax, .r32, .none, .none, &.{ 0x90 }, 0, .none }, - .{ .xchg, .o, .rax, .r64, .none, .none, &.{ 0x90 }, 0, .long }, - .{ .xchg, .o, .r32, .eax, .none, .none, &.{ 0x90 }, 0, .none }, - .{ .xchg, .o, .r64, .rax, .none, .none, &.{ 0x90 }, 0, .long }, - .{ .xchg, .mr, .rm8, .r8, .none, .none, &.{ 0x86 }, 0, .none }, - .{ .xchg, .mr, .rm8, .r8, .none, .none, &.{ 0x86 }, 0, .rex }, - .{ .xchg, .rm, .r8, .rm8, .none, .none, &.{ 0x86 }, 0, .none }, - .{ .xchg, .rm, .r8, .rm8, .none, .none, &.{ 0x86 }, 0, .rex }, - .{ .xchg, .mr, .rm16, .r16, .none, .none, &.{ 0x87 }, 0, .none }, - .{ .xchg, .rm, .r16, .rm16, .none, .none, &.{ 0x87 }, 0, .none }, - .{ .xchg, .mr, .rm32, .r32, .none, .none, &.{ 0x87 }, 0, .none }, - .{ .xchg, .mr, .rm64, .r64, .none, .none, &.{ 0x87 }, 0, .long }, - .{ .xchg, .rm, .r32, .rm32, .none, .none, &.{ 0x87 }, 0, .none }, - .{ .xchg, .rm, .r64, .rm64, .none, .none, &.{ 0x87 }, 0, .long }, - - .{ .xor, .zi, .al, .imm8, .none, .none, &.{ 0x34 }, 0, .none }, - .{ .xor, .zi, .ax, .imm16, .none, .none, &.{ 0x35 }, 0, .none }, - .{ .xor, .zi, .eax, .imm32, .none, .none, &.{ 0x35 }, 0, .none }, - .{ .xor, .zi, .rax, .imm32s, .none, .none, &.{ 0x35 }, 0, .long }, - .{ .xor, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 6, .none }, - .{ .xor, .mi, .rm8, .imm8, .none, .none, &.{ 0x80 }, 6, .rex }, - .{ .xor, .mi, .rm16, .imm16, .none, .none, &.{ 0x81 }, 6, .none }, - .{ .xor, .mi, .rm32, .imm32, .none, .none, &.{ 0x81 }, 6, .none }, - .{ .xor, .mi, .rm64, .imm32s, .none, .none, &.{ 0x81 }, 6, .long }, - .{ .xor, .mi, .rm16, .imm8s, .none, .none, &.{ 0x83 }, 6, .none }, - .{ .xor, .mi, .rm32, .imm8s, .none, .none, &.{ 0x83 }, 6, .none }, - .{ .xor, .mi, .rm64, .imm8s, .none, .none, &.{ 0x83 }, 6, .long }, - .{ .xor, .mr, .rm8, .r8, .none, .none, &.{ 0x30 }, 0, .none }, - .{ .xor, .mr, .rm8, .r8, .none, .none, &.{ 0x30 }, 0, .rex }, - .{ .xor, .mr, .rm16, .r16, .none, .none, &.{ 0x31 }, 0, .none }, - .{ .xor, .mr, .rm32, .r32, .none, .none, &.{ 0x31 }, 0, .none }, - .{ .xor, .mr, .rm64, .r64, .none, .none, &.{ 0x31 }, 0, .long }, - .{ .xor, .rm, .r8, .rm8, .none, .none, &.{ 0x32 }, 0, .none }, - .{ .xor, .rm, .r8, .rm8, .none, .none, &.{ 0x32 }, 0, .rex }, - .{ .xor, .rm, .r16, .rm16, .none, .none, &.{ 0x33 }, 0, .none }, - .{ .xor, .rm, .r32, .rm32, .none, .none, &.{ 0x33 }, 0, .none }, - .{ .xor, .rm, .r64, .rm64, .none, .none, &.{ 0x33 }, 0, .long }, + .{ .@"test", .zi, &.{ .al, .imm8 }, &.{ 0xa8 }, 0, .none }, + .{ .@"test", .zi, &.{ .ax, .imm16 }, &.{ 0xa9 }, 0, .none }, + .{ .@"test", .zi, &.{ .eax, .imm32 }, &.{ 0xa9 }, 0, .none }, + .{ .@"test", .zi, &.{ .rax, .imm32s }, &.{ 0xa9 }, 0, .long }, + .{ .@"test", .mi, &.{ .rm8, .imm8 }, &.{ 0xf6 }, 0, .none }, + .{ .@"test", .mi, &.{ .rm8, .imm8 }, &.{ 0xf6 }, 0, .rex }, + .{ .@"test", .mi, &.{ .rm16, .imm16 }, &.{ 0xf7 }, 0, .none }, + .{ .@"test", .mi, &.{ .rm32, .imm32 }, &.{ 0xf7 }, 0, .none }, + .{ .@"test", .mi, &.{ .rm64, .imm32s }, &.{ 0xf7 }, 0, .long }, + .{ .@"test", .mr, &.{ .rm8, .r8 }, &.{ 0x84 }, 0, .none }, + .{ .@"test", .mr, &.{ .rm8, .r8 }, &.{ 0x84 }, 0, .rex }, + .{ .@"test", .mr, &.{ .rm16, .r16 }, &.{ 0x85 }, 0, .none }, + .{ .@"test", .mr, &.{ .rm32, .r32 }, &.{ 0x85 }, 0, .none }, + .{ .@"test", .mr, &.{ .rm64, .r64 }, &.{ 0x85 }, 0, .long }, + + .{ .tzcnt, .rm, &.{ .r16, .rm16 }, &.{ 0xf3, 0x0f, 0xbc }, 0, .none }, + .{ .tzcnt, .rm, &.{ .r32, .rm32 }, &.{ 0xf3, 0x0f, 0xbc }, 0, .none }, + .{ .tzcnt, .rm, &.{ .r64, .rm64 }, &.{ 0xf3, 0x0f, 0xbc }, 0, .long }, + + .{ .ud2, .np, &.{}, &.{ 0x0f, 0x0b }, 0, .none }, + + .{ .xadd, .mr, &.{ .rm8, .r8 }, &.{ 0x0f, 0xc0 }, 0, .none }, + .{ .xadd, .mr, &.{ .rm8, .r8 }, &.{ 0x0f, 0xc0 }, 0, .rex }, + .{ .xadd, .mr, &.{ .rm16, .r16 }, &.{ 0x0f, 0xc1 }, 0, .none }, + .{ .xadd, .mr, &.{ .rm32, .r32 }, &.{ 0x0f, 0xc1 }, 0, .none }, + .{ .xadd, .mr, &.{ .rm64, .r64 }, &.{ 0x0f, 0xc1 }, 0, .long }, + + .{ .xchg, .o, &.{ .ax, .r16 }, &.{ 0x90 }, 0, .none }, + .{ .xchg, .o, &.{ .r16, .ax }, &.{ 0x90 }, 0, .none }, + .{ .xchg, .o, &.{ .eax, .r32 }, &.{ 0x90 }, 0, .none }, + .{ .xchg, .o, &.{ .rax, .r64 }, &.{ 0x90 }, 0, .long }, + .{ .xchg, .o, &.{ .r32, .eax }, &.{ 0x90 }, 0, .none }, + .{ .xchg, .o, &.{ .r64, .rax }, &.{ 0x90 }, 0, .long }, + .{ .xchg, .mr, &.{ .rm8, .r8 }, &.{ 0x86 }, 0, .none }, + .{ .xchg, .mr, &.{ .rm8, .r8 }, &.{ 0x86 }, 0, .rex }, + .{ .xchg, .rm, &.{ .r8, .rm8 }, &.{ 0x86 }, 0, .none }, + .{ .xchg, .rm, &.{ .r8, .rm8 }, &.{ 0x86 }, 0, .rex }, + .{ .xchg, .mr, &.{ .rm16, .r16 }, &.{ 0x87 }, 0, .none }, + .{ .xchg, .rm, &.{ .r16, .rm16 }, &.{ 0x87 }, 0, .none }, + .{ .xchg, .mr, &.{ .rm32, .r32 }, &.{ 0x87 }, 0, .none }, + .{ .xchg, .mr, &.{ .rm64, .r64 }, &.{ 0x87 }, 0, .long }, + .{ .xchg, .rm, &.{ .r32, .rm32 }, &.{ 0x87 }, 0, .none }, + .{ .xchg, .rm, &.{ .r64, .rm64 }, &.{ 0x87 }, 0, .long }, + + .{ .xor, .zi, &.{ .al, .imm8 }, &.{ 0x34 }, 0, .none }, + .{ .xor, .zi, &.{ .ax, .imm16 }, &.{ 0x35 }, 0, .none }, + .{ .xor, .zi, &.{ .eax, .imm32 }, &.{ 0x35 }, 0, .none }, + .{ .xor, .zi, &.{ .rax, .imm32s }, &.{ 0x35 }, 0, .long }, + .{ .xor, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 6, .none }, + .{ .xor, .mi, &.{ .rm8, .imm8 }, &.{ 0x80 }, 6, .rex }, + .{ .xor, .mi, &.{ .rm16, .imm16 }, &.{ 0x81 }, 6, .none }, + .{ .xor, .mi, &.{ .rm32, .imm32 }, &.{ 0x81 }, 6, .none }, + .{ .xor, .mi, &.{ .rm64, .imm32s }, &.{ 0x81 }, 6, .long }, + .{ .xor, .mi, &.{ .rm16, .imm8s }, &.{ 0x83 }, 6, .none }, + .{ .xor, .mi, &.{ .rm32, .imm8s }, &.{ 0x83 }, 6, .none }, + .{ .xor, .mi, &.{ .rm64, .imm8s }, &.{ 0x83 }, 6, .long }, + .{ .xor, .mr, &.{ .rm8, .r8 }, &.{ 0x30 }, 0, .none }, + .{ .xor, .mr, &.{ .rm8, .r8 }, &.{ 0x30 }, 0, .rex }, + .{ .xor, .mr, &.{ .rm16, .r16 }, &.{ 0x31 }, 0, .none }, + .{ .xor, .mr, &.{ .rm32, .r32 }, &.{ 0x31 }, 0, .none }, + .{ .xor, .mr, &.{ .rm64, .r64 }, &.{ 0x31 }, 0, .long }, + .{ .xor, .rm, &.{ .r8, .rm8 }, &.{ 0x32 }, 0, .none }, + .{ .xor, .rm, &.{ .r8, .rm8 }, &.{ 0x32 }, 0, .rex }, + .{ .xor, .rm, &.{ .r16, .rm16 }, &.{ 0x33 }, 0, .none }, + .{ .xor, .rm, &.{ .r32, .rm32 }, &.{ 0x33 }, 0, .none }, + .{ .xor, .rm, &.{ .r64, .rm64 }, &.{ 0x33 }, 0, .long }, // SSE - .{ .addss, .rm, .xmm, .xmm_m32, .none, .none, &.{ 0xf3, 0x0f, 0x58 }, 0, .sse }, + .{ .addss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x58 }, 0, .sse }, - .{ .cmpss, .rmi, .xmm, .xmm_m32, .imm8, .none, &.{ 0xf3, 0x0f, 0xc2 }, 0, .sse }, + .{ .cmpss, .rmi, &.{ .xmm, .xmm_m32, .imm8 }, &.{ 0xf3, 0x0f, 0xc2 }, 0, .sse }, - .{ .divss, .rm, .xmm, .xmm_m32, .none, .none, &.{ 0xf3, 0x0f, 0x5e }, 0, .sse }, + .{ .divss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5e }, 0, .sse }, - .{ .maxss, .rm, .xmm, .xmm_m32, .none, .none, &.{ 0xf3, 0x0f, 0x5f }, 0, .sse }, + .{ .maxss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5f }, 0, .sse }, - .{ .minss, .rm, .xmm, .xmm_m32, .none, .none, &.{ 0xf3, 0x0f, 0x5d }, 0, .sse }, + .{ .minss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5d }, 0, .sse }, - .{ .movss, .rm, .xmm, .xmm_m32, .none, .none, &.{ 0xf3, 0x0f, 0x10 }, 0, .sse }, - .{ .movss, .mr, .xmm_m32, .xmm, .none, .none, &.{ 0xf3, 0x0f, 0x11 }, 0, .sse }, + .{ .movss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x10 }, 0, .sse }, + .{ .movss, .mr, &.{ .xmm_m32, .xmm }, &.{ 0xf3, 0x0f, 0x11 }, 0, .sse }, - .{ .mulss, .rm, .xmm, .xmm_m32, .none, .none, &.{ 0xf3, 0x0f, 0x59 }, 0, .sse }, + .{ .mulss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x59 }, 0, .sse }, - .{ .subss, .rm, .xmm, .xmm_m32, .none, .none, &.{ 0xf3, 0x0f, 0x5c }, 0, .sse }, + .{ .subss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0xf3, 0x0f, 0x5c }, 0, .sse }, - .{ .ucomiss, .rm, .xmm, .xmm_m32, .none, .none, &.{ 0x0f, 0x2e }, 0, .sse }, + .{ .ucomiss, .rm, &.{ .xmm, .xmm_m32 }, &.{ 0x0f, 0x2e }, 0, .sse }, // SSE2 - .{ .addsd, .rm, .xmm, .xmm_m64, .none, .none, &.{ 0xf2, 0x0f, 0x58 }, 0, .sse2 }, + .{ .addsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x58 }, 0, .sse2 }, - .{ .cmpsd, .rmi, .xmm, .xmm_m64, .imm8, .none, &.{ 0xf2, 0x0f, 0xc2 }, 0, .sse2 }, + .{ .cmpsd, .rmi, &.{ .xmm, .xmm_m64, .imm8 }, &.{ 0xf2, 0x0f, 0xc2 }, 0, .sse2 }, - .{ .divsd, .rm, .xmm, .xmm_m64, .none, .none, &.{ 0xf2, 0x0f, 0x5e }, 0, .sse2 }, + .{ .divsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5e }, 0, .sse2 }, - .{ .maxsd, .rm, .xmm, .xmm_m64, .none, .none, &.{ 0xf2, 0x0f, 0x5f }, 0, .sse2 }, + .{ .maxsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5f }, 0, .sse2 }, - .{ .minsd, .rm, .xmm, .xmm_m64, .none, .none, &.{ 0xf2, 0x0f, 0x5d }, 0, .sse2 }, + .{ .minsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5d }, 0, .sse2 }, - .{ .movq, .rm, .xmm, .xmm_m64, .none, .none, &.{ 0xf3, 0x0f, 0x7e }, 0, .sse2 }, - .{ .movq, .mr, .xmm_m64, .xmm, .none, .none, &.{ 0x66, 0x0f, 0xd6 }, 0, .sse2 }, + .{ .movq, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf3, 0x0f, 0x7e }, 0, .sse2 }, + .{ .movq, .mr, &.{ .xmm_m64, .xmm }, &.{ 0x66, 0x0f, 0xd6 }, 0, .sse2 }, - .{ .mulsd, .rm, .xmm, .xmm_m64, .none, .none, &.{ 0xf2, 0x0f, 0x59 }, 0, .sse2 }, + .{ .mulsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x59 }, 0, .sse2 }, - .{ .subsd, .rm, .xmm, .xmm_m64, .none, .none, &.{ 0xf2, 0x0f, 0x5c }, 0, .sse2 }, + .{ .subsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x5c }, 0, .sse2 }, - .{ .movsd, .rm, .xmm, .xmm_m64, .none, .none, &.{ 0xf2, 0x0f, 0x10 }, 0, .sse2 }, - .{ .movsd, .mr, .xmm_m64, .xmm, .none, .none, &.{ 0xf2, 0x0f, 0x11 }, 0, .sse2 }, + .{ .movsd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0xf2, 0x0f, 0x10 }, 0, .sse2 }, + .{ .movsd, .mr, &.{ .xmm_m64, .xmm }, &.{ 0xf2, 0x0f, 0x11 }, 0, .sse2 }, - .{ .ucomisd, .rm, .xmm, .xmm_m64, .none, .none, &.{ 0x66, 0x0f, 0x2e }, 0, .sse2 }, + .{ .ucomisd, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x2e }, 0, .sse2 }, // SSE4.1 - .{ .roundss, .rmi, .xmm, .xmm_m32, .imm8, .none, &.{ 0x66, 0x0f, 0x3a, 0x0a }, 0, .sse4_1 }, - .{ .roundsd, .rmi, .xmm, .xmm_m64, .imm8, .none, &.{ 0x66, 0x0f, 0x3a, 0x0b }, 0, .sse4_1 }, + .{ .roundss, .rmi, &.{ .xmm, .xmm_m32, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0a }, 0, .sse4_1 }, + .{ .roundsd, .rmi, &.{ .xmm, .xmm_m64, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x0b }, 0, .sse4_1 }, }; // zig fmt: on From 3a516433b0cf94e8aa67819acc49c03e0b72f296 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 27 Mar 2023 03:10:03 -0400 Subject: [PATCH 4/9] x86_64: add live codegen debug --- src/arch/x86_64/CodeGen.zig | 69 ++++++++++++++++++++++++++++++- src/arch/x86_64/encoder.zig | 45 ++++++++++----------- src/print_air.zig | 81 ++++++++++++++++++++++--------------- 3 files changed, 138 insertions(+), 57 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5ab0e64615a3..f93cc31b9252 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8,6 +8,7 @@ const link = @import("../../link.zig"); const log = std.log.scoped(.codegen); const math = std.math; const mem = std.mem; +const print_air = @import("../../print_air.zig"); const trace = @import("../../tracy.zig").trace; const Air = @import("../../Air.zig"); @@ -20,6 +21,7 @@ const ErrorMsg = Module.ErrorMsg; const Result = codegen.Result; const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); +const Lower = @import("Lower.zig"); const Mir = @import("Mir.zig"); const Module = @import("../../Module.zig"); const Target = std.Target; @@ -44,6 +46,8 @@ const sse = abi.RegisterClass.sse; const InnerError = CodeGenError || error{OutOfRegisters}; +const debug_wip_mir = false; + gpa: Allocator, air: Air, liveness: Liveness, @@ -103,8 +107,12 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, /// For mir debug info, maps a mir index to a air index mir_to_air_map: if (builtin.mode == .Debug) std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index) else void, +debug_wip_mir_inst: @TypeOf(debug_wip_mir_inst_init) = debug_wip_mir_inst_init, + const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; +const debug_wip_mir_inst_init = if (debug_wip_mir) @as(Mir.Inst.Index, 0) else {}; + pub const MCValue = union(enum) { /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. /// TODO Look into deleting this tag and using `dead` instead, since every use @@ -267,6 +275,12 @@ pub fn generate( assert(fn_owner_decl.has_tv); const fn_type = fn_owner_decl.ty; + if (debug_wip_mir) { + const stderr = std.io.getStdErr().writer(); + fn_owner_decl.renderFullyQualifiedName(mod, stderr) catch {}; + stderr.writeAll(":\n") catch {}; + } + var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); try branch_stack.ensureUnusedCapacity(2); // The outermost branch is used for constants only. @@ -683,6 +697,56 @@ fn asmMemoryRegisterImmediate( }); } +fn printWipMir(self: *Self, stream: anytype, air_inst: ?Air.Inst.Index) !void { + const mod = self.bin_file.options.module.?; + if (!debug_wip_mir) return; + + var lower = Lower{ + .allocator = self.gpa, + .mir = .{ + .instructions = self.mir_instructions.slice(), + .extra = self.mir_extra.items, + }, + .target = self.target, + .src_loc = self.src_loc, + }; + var mir_inst = self.debug_wip_mir_inst; + var mir_end = @intCast(Mir.Inst.Index, self.mir_instructions.len); + defer self.debug_wip_mir_inst = mir_end; + while (mir_inst < mir_end) : (mir_inst += 1) { + for (lower.lowerMir(lower.mir.instructions.get(mir_inst)) catch |err| switch (err) { + error.LowerFail => { + defer { + lower.err_msg.?.deinit(self.gpa); + lower.err_msg = null; + } + try stream.print("{s}\n", .{lower.err_msg.?.msg}); + continue; + }, + error.InvalidInstruction, error.CannotEncode => |e| { + try stream.writeAll(switch (e) { + error.InvalidInstruction => "CodeGen failed to find a viable instruction.\n", + error.CannotEncode => "CodeGen failed to encode the instruction.\n", + }); + continue; + }, + else => |e| return e, + }) |lower_inst| { + try stream.writeAll(" | "); + try lower_inst.fmtPrint(stream); + try stream.writeByte('\n'); + } + } + + if (air_inst) |inst| { + print_air.writeInst(stream, inst, mod, self.air, self.liveness); + } +} + +fn dumpWipMir(self: *Self, air_inst: ?Air.Inst.Index) void { + self.printWipMir(std.io.getStdErr().writer(), air_inst) catch return; +} + fn gen(self: *Self) InnerError!void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { @@ -836,6 +900,8 @@ fn gen(self: *Self) InnerError!void { .ops = undefined, .data = .{ .payload = payload }, }); + + self.dumpWipMir(null); } fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { @@ -845,8 +911,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { const old_air_bookkeeping = self.air_bookkeeping; try self.ensureProcessDeathCapacity(Liveness.bpi); if (builtin.mode == .Debug) { - try self.mir_to_air_map.put(@intCast(u32, self.mir_instructions.len), inst); + try self.mir_to_air_map.put(@intCast(Mir.Inst.Index, self.mir_instructions.len), inst); } + self.dumpWipMir(inst); switch (air_tags[inst]) { // zig fmt: off diff --git a/src/arch/x86_64/encoder.zig b/src/arch/x86_64/encoder.zig index e7e231c0634f..663dad8727f1 100644 --- a/src/arch/x86_64/encoder.zig +++ b/src/arch/x86_64/encoder.zig @@ -54,18 +54,17 @@ pub const Instruction = struct { }; } - pub fn fmtPrint(op: Operand, enc_op: Encoding.Op, writer: anytype) !void { + pub fn fmtPrint(op: Operand, enc_op: Encoding.Op, writer: anytype) @TypeOf(writer).Error!void { switch (op) { .none => {}, .reg => |reg| try writer.writeAll(@tagName(reg)), .mem => |mem| switch (mem) { .rip => |rip| { try writer.print("{s} ptr [rip", .{@tagName(rip.ptr_size)}); - if (rip.disp != 0) { - const sign_bit = if (sign(rip.disp) < 0) "-" else "+"; - const disp_abs = try std.math.absInt(rip.disp); - try writer.print(" {s} 0x{x}", .{ sign_bit, disp_abs }); - } + if (rip.disp != 0) try writer.print(" {c} 0x{x}", .{ + @as(u8, if (rip.disp < 0) '-' else '+'), + std.math.absCast(rip.disp), + }); try writer.writeByte(']'); }, .sib => |sib| { @@ -77,27 +76,31 @@ pub const Instruction = struct { try writer.writeByte('['); + var any = false; if (sib.base) |base| { try writer.print("{s}", .{@tagName(base)}); + any = true; } if (sib.scale_index) |si| { - if (sib.base != null) { - try writer.writeAll(" + "); - } + if (any) try writer.writeAll(" + "); try writer.print("{s} * {d}", .{ @tagName(si.index), si.scale }); + any = true; } - if (sib.disp != 0) { - if (sib.base != null or sib.scale_index != null) { - try writer.writeByte(' '); - } - try writer.writeByte(if (sign(sib.disp) < 0) '-' else '+'); - const disp_abs = try std.math.absInt(sib.disp); - try writer.print(" 0x{x}", .{disp_abs}); + if (sib.disp != 0 or !any) { + if (any) + try writer.print(" {c} ", .{@as(u8, if (sib.disp < 0) '-' else '+')}) + else if (sib.disp < 0) + try writer.writeByte('-'); + try writer.print("0x{x}", .{std.math.absCast(sib.disp)}); + any = true; } try writer.writeByte(']'); }, - .moffs => |moffs| try writer.print("{s}:0x{x}", .{ @tagName(moffs.seg), moffs.offset }), + .moffs => |moffs| try writer.print("{s}:0x{x}", .{ + @tagName(moffs.seg), + moffs.offset, + }), }, .imm => |imm| try writer.print("0x{x}", .{imm.asUnsigned(enc_op.bitSize())}), } @@ -127,10 +130,10 @@ pub const Instruction = struct { return inst; } - pub fn fmtPrint(inst: Instruction, writer: anytype) !void { + pub fn fmtPrint(inst: Instruction, writer: anytype) @TypeOf(writer).Error!void { if (inst.prefix != .none) try writer.print("{s} ", .{@tagName(inst.prefix)}); try writer.print("{s}", .{@tagName(inst.encoding.mnemonic)}); - for (inst.ops, inst.encodings.ops, 0..) |op, enc, i| { + for (inst.ops, inst.encoding.data.ops, 0..) |op, enc, i| { if (op == .none) break; if (i > 0) try writer.writeByte(','); try writer.writeByte(' '); @@ -390,10 +393,6 @@ pub const Instruction = struct { } }; -inline fn sign(i: anytype) @TypeOf(i) { - return @as(@TypeOf(i), @boolToInt(i > 0)) - @boolToInt(i < 0); -} - pub const LegacyPrefixes = packed struct { /// LOCK prefix_f0: bool = false, diff --git a/src/print_air.zig b/src/print_air.zig index f5c06daae2b6..8d29a272ca3b 100644 --- a/src/print_air.zig +++ b/src/print_air.zig @@ -8,7 +8,7 @@ const Type = @import("type.zig").Type; const Air = @import("Air.zig"); const Liveness = @import("Liveness.zig"); -pub fn dump(module: *Module, air: Air, liveness: Liveness) void { +pub fn write(stream: anytype, module: *Module, air: Air, liveness: Liveness) void { const instruction_bytes = air.instructions.len * // Here we don't use @sizeOf(Air.Inst.Data) because it would include // the debug safety tag but we want to measure release size. @@ -23,7 +23,7 @@ pub fn dump(module: *Module, air: Air, liveness: Liveness) void { liveness_special_bytes + tomb_bytes; // zig fmt: off - std.debug.print( + stream.print( \\# Total AIR+Liveness bytes: {} \\# AIR Instructions: {d} ({}) \\# AIR Extra Data: {d} ({}) @@ -40,65 +40,78 @@ pub fn dump(module: *Module, air: Air, liveness: Liveness) void { fmtIntSizeBin(tomb_bytes), liveness.extra.len, fmtIntSizeBin(liveness_extra_bytes), liveness.special.count(), fmtIntSizeBin(liveness_special_bytes), - }); + }) catch return; // zig fmt: on - var arena = std.heap.ArenaAllocator.init(module.gpa); - defer arena.deinit(); var writer: Writer = .{ .module = module, .gpa = module.gpa, - .arena = arena.allocator(), .air = air, .liveness = liveness, .indent = 2, + .skip_body = false, }; - const stream = std.io.getStdErr().writer(); writer.writeAllConstants(stream) catch return; stream.writeByte('\n') catch return; writer.writeBody(stream, air.getMainBody()) catch return; } +pub fn writeInst( + stream: anytype, + inst: Air.Inst.Index, + module: *Module, + air: Air, + liveness: Liveness, +) void { + var writer: Writer = .{ + .module = module, + .gpa = module.gpa, + .air = air, + .liveness = liveness, + .indent = 2, + .skip_body = true, + }; + writer.writeInst(stream, inst) catch return; +} + +pub fn dump(module: *Module, air: Air, liveness: Liveness) void { + write(std.io.getStdErr().writer(), module, air, liveness); +} + +pub fn dumpInst(inst: Air.Inst.Index, module: *Module, air: Air, liveness: Liveness) void { + writeInst(std.io.getStdErr().writer(), inst, module, air, liveness); +} + const Writer = struct { module: *Module, gpa: Allocator, - arena: Allocator, air: Air, liveness: Liveness, indent: usize, + skip_body: bool, fn writeAllConstants(w: *Writer, s: anytype) @TypeOf(s).Error!void { for (w.air.instructions.items(.tag), 0..) |tag, i| { - const inst = @intCast(u32, i); + const inst = @intCast(Air.Inst.Index, i); switch (tag) { - .constant, .const_ty => { - try s.writeByteNTimes(' ', w.indent); - try s.print("%{d} ", .{inst}); - try w.writeInst(s, inst); - try s.writeAll(")\n"); - }, + .constant, .const_ty => try w.writeInst(s, inst), else => continue, } } } fn writeBody(w: *Writer, s: anytype, body: []const Air.Inst.Index) @TypeOf(s).Error!void { - for (body) |inst| { - try s.writeByteNTimes(' ', w.indent); - if (w.liveness.isUnused(inst)) { - try s.print("%{d}!", .{inst}); - } else { - try s.print("%{d} ", .{inst}); - } - try w.writeInst(s, inst); - try s.writeAll(")\n"); - } + for (body) |inst| try w.writeInst(s, inst); } fn writeInst(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { - const tags = w.air.instructions.items(.tag); - const tag = tags[inst]; - try s.print("= {s}(", .{@tagName(tags[inst])}); + const tag = w.air.instructions.items(.tag)[inst]; + try s.writeByteNTimes(' ', w.indent); + try s.print("%{d}{c}= {s}(", .{ + inst, + @as(u8, if (w.liveness.isUnused(inst)) '!' else ' '), + @tagName(tag), + }); switch (tag) { .add, .addwrap, @@ -316,6 +329,7 @@ const Writer = struct { .dbg_block_begin, .dbg_block_end => {}, } + try s.writeAll(")\n"); } fn writeBinOp(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { @@ -372,6 +386,7 @@ const Writer = struct { const body = w.air.extra[extra.end..][0..extra.data.body_len]; try w.writeType(s, w.air.getRefType(ty_pl.ty)); + if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; @@ -703,6 +718,7 @@ const Writer = struct { const body = w.air.extra[extra.end..][0..extra.data.body_len]; try w.writeOperand(s, inst, 0, pl_op.operand); + if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; @@ -721,6 +737,7 @@ const Writer = struct { try s.writeAll(", "); try w.writeType(s, w.air.getRefType(ty_pl.ty)); + if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; @@ -738,6 +755,7 @@ const Writer = struct { const liveness_condbr = w.liveness.getCondBr(inst); try w.writeOperand(s, inst, 0, pl_op.operand); + if (w.skip_body) return s.writeAll(", ..."); try s.writeAll(", {\n"); const old_indent = w.indent; w.indent += 2; @@ -900,10 +918,7 @@ const Writer = struct { dies: bool, ) @TypeOf(s).Error!void { _ = w; - if (dies) { - try s.print("%{d}!", .{inst}); - } else { - try s.print("%{d}", .{inst}); - } + try s.print("%{d}", .{inst}); + if (dies) try s.writeByte('!'); } }; From 65838fcabeee8f0330425156c31a4711d0e026f7 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 27 Mar 2023 03:11:34 -0400 Subject: [PATCH 5/9] x86_64: implement some binary ops for large values --- src/arch/x86_64/CodeGen.zig | 233 ++++++++++++++++++++++-------------- 1 file changed, 145 insertions(+), 88 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index f93cc31b9252..2e802269334f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4010,9 +4010,6 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue .register_overflow => unreachable, .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)), .ptr_stack_offset, .stack_offset => |off| { - if (off > math.maxInt(i32)) { - return self.fail("stack offset too large", .{}); - } if (abi_size > 8) { return self.fail("TODO implement {} for stack dst with large ABI", .{mir_tag}); } @@ -4454,9 +4451,6 @@ fn genBinOp( if (lhs_ty.zigTypeTag() == .Vector) { return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmt(self.bin_file.options.module.?)}); } - if (lhs_ty.abiSize(self.target.*) > 8) { - return self.fail("TODO implement genBinOp for {}", .{lhs_ty.fmt(self.bin_file.options.module.?)}); - } switch (lhs) { .immediate => |imm| switch (imm) { @@ -4686,7 +4680,6 @@ fn genBinOp( const addr_reg = (try self.register_manager.allocReg(null, gp)).to64(); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); defer self.register_manager.unlockReg(addr_reg_lock); - try self.loadMemPtrIntoRegister(addr_reg, Type.usize, mat_src_mcv); // To get the actual address of the value we want to modify we @@ -4801,9 +4794,6 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = reg }); }, .stack_offset => |off| { - if (off > math.maxInt(i32)) { - return self.fail("stack offset too large", .{}); - } try self.asmRegisterMemory( mir_tag, registerAlias(dst_reg, abi_size), @@ -4812,78 +4802,155 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, ty: Type, dst_mcv: MCValue, s }, } }, - .ptr_stack_offset, .stack_offset => |off| { - if (off > math.maxInt(i32)) { - return self.fail("stack offset too large", .{}); - } - if (abi_size > 8) { - return self.fail("TODO implement {} for stack dst with large ABI", .{mir_tag}); - } + .ptr_stack_offset, .stack_offset => |dst_off| { + const src: ?struct { + limb_reg: Register, + limb_lock: RegisterLock, + addr_reg: Register, + addr_lock: RegisterLock, + } = switch (src_mcv) { + else => null, + .memory, .linker_load => addr: { + const src_limb_reg = try self.register_manager.allocReg(null, gp); + const src_limb_lock = self.register_manager.lockRegAssumeUnused(src_limb_reg); + errdefer self.register_manager.unlockReg(src_limb_lock); + + const src_addr_reg = try self.register_manager.allocReg(null, gp); + const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); + errdefer self.register_manager.unlockReg(src_addr_lock); + + try self.loadMemPtrIntoRegister(src_addr_reg, Type.usize, src_mcv); + // To get the actual address of the value we want to modify we + // we have to go through the GOT + try self.asmRegisterMemory( + .mov, + src_addr_reg, + Memory.sib(.qword, .{ .base = src_addr_reg }), + ); - switch (src_mcv) { - .none => unreachable, - .undef => unreachable, - .dead, .unreach => unreachable, - .register_overflow => unreachable, - .register => |src_reg| { - try self.asmMemoryRegister(mir_tag, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ - .base = .rbp, - .disp = -off, - }), registerAlias(src_reg, abi_size)); + break :addr .{ + .addr_reg = src_addr_reg, + .addr_lock = src_addr_lock, + .limb_reg = src_limb_reg, + .limb_lock = src_limb_lock, + }; }, - .immediate => |imm| { - switch (self.regBitSize(ty)) { - 8, 16, 32 => { - try self.asmMemoryImmediate( - mir_tag, - Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ - .base = .rbp, - .disp = -off, - }), - if (math.cast(i32, @bitCast(i64, imm))) |small| - Immediate.s(small) - else - Immediate.u(@intCast(u32, imm)), - ); - }, - 64 => { - if (math.cast(i32, @bitCast(i64, imm))) |small| { + }; + defer if (src) |locks| { + self.register_manager.unlockReg(locks.limb_lock); + self.register_manager.unlockReg(locks.addr_lock); + }; + + const ty_signedness = + if (ty.isAbiInt()) ty.intInfo(self.target.*).signedness else .unsigned; + const limb_ty = if (abi_size <= 8) ty else switch (ty_signedness) { + .signed => Type.usize, + .unsigned => Type.isize, + }; + const limb_abi_size = @min(abi_size, 8); + var off: i32 = 0; + while (off < abi_size) : (off += 8) { + const mir_limb_tag = switch (off) { + 0 => mir_tag, + else => switch (mir_tag) { + .add => .adc, + .sub => .sbb, + .@"or", .@"and", .xor => mir_tag, + else => return self.fail("TODO genBinOpMir implement large ABI for {s}", .{ + @tagName(mir_tag), + }), + }, + }; + const dst_limb_mem = Memory.sib( + Memory.PtrSize.fromSize(limb_abi_size), + .{ .base = .rbp, .disp = off - dst_off }, + ); + switch (src_mcv) { + .none => unreachable, + .undef => unreachable, + .dead, .unreach => unreachable, + .register_overflow => unreachable, + .register => |src_reg| { + assert(off == 0); + try self.asmMemoryRegister( + mir_limb_tag, + dst_limb_mem, + registerAlias(src_reg, limb_abi_size), + ); + }, + .immediate => |src_imm| { + const imm = if (off == 0) src_imm else switch (ty_signedness) { + .signed => @bitCast(u64, @bitCast(i64, src_imm) >> 63), + .unsigned => 0, + }; + switch (self.regBitSize(limb_ty)) { + 8, 16, 32 => { try self.asmMemoryImmediate( - mir_tag, - Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ - .base = .rbp, - .disp = -off, - }), - Immediate.s(small), + mir_limb_tag, + dst_limb_mem, + if (math.cast(i32, @bitCast(i64, imm))) |small| + Immediate.s(small) + else + Immediate.u(@intCast(u32, imm)), ); - } else { - try self.asmMemoryRegister( - mir_tag, - Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ - .base = .rbp, - .disp = -off, - }), - registerAlias(try self.copyToTmpRegister(ty, src_mcv), abi_size), - ); - } - }, - else => return self.fail("TODO genBinOpMir implement large immediate ABI", .{}), - } - }, - .memory, - .linker_load, - .stack_offset, - .ptr_stack_offset, - .eflags, - => { - assert(abi_size <= 8); - - const tmp_reg = try self.copyToTmpRegister(ty, src_mcv); - const tmp_lock = self.register_manager.lockReg(tmp_reg); - defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock); + }, + 64 => { + if (math.cast(i32, @bitCast(i64, imm))) |small| { + try self.asmMemoryImmediate( + mir_limb_tag, + dst_limb_mem, + Immediate.s(small), + ); + } else { + try self.asmMemoryRegister( + mir_limb_tag, + dst_limb_mem, + registerAlias( + try self.copyToTmpRegister(limb_ty, .{ .immediate = imm }), + limb_abi_size, + ), + ); + } + }, + else => unreachable, + } + }, + .memory, .linker_load => { + try self.asmRegisterMemory( + .mov, + registerAlias(src.?.limb_reg, limb_abi_size), + Memory.sib( + Memory.PtrSize.fromSize(limb_abi_size), + .{ .base = src.?.addr_reg, .disp = off }, + ), + ); + try self.asmMemoryRegister( + mir_limb_tag, + dst_limb_mem, + registerAlias(src.?.limb_reg, limb_abi_size), + ); + }, + .stack_offset, .ptr_stack_offset, .eflags => { + const src_limb_reg = try self.copyToTmpRegister(limb_ty, switch (src_mcv) { + .stack_offset => |src_off| .{ .stack_offset = src_off - off }, + .ptr_stack_offset, + .eflags, + => off: { + assert(off == 0); + break :off src_mcv; + }, + else => unreachable, + }); + const src_limb_lock = self.register_manager.lockReg(src_limb_reg); + defer if (src_limb_lock) |lock| self.register_manager.unlockReg(lock); - return self.genBinOpMir(mir_tag, ty, dst_mcv, .{ .register = tmp_reg }); - }, + try self.asmMemoryRegister( + mir_limb_tag, + dst_limb_mem, + registerAlias(src_limb_reg, limb_abi_size), + ); + }, + } } }, .memory => { @@ -6735,10 +6802,6 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl } }, .register => |reg| { - if (stack_offset > math.maxInt(i32)) { - return self.fail("stack offset too large", .{}); - } - const base_reg = opts.dest_stack_base orelse .rbp; switch (ty.zigTypeTag()) { @@ -6973,13 +7036,11 @@ fn genInlineMemset( fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { const abi_size = @intCast(u32, ty.abiSize(self.target.*)); + if (abi_size > 8) return self.fail("genSetReg called with a value larger than one register", .{}); switch (mcv) { .dead => unreachable, .register_overflow => unreachable, .ptr_stack_offset => |off| { - if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { - return self.fail("stack offset too large", .{}); - } try self.asmRegisterMemory( .lea, registerAlias(reg, abi_size), @@ -7158,10 +7219,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void }, }, .stack_offset => |off| { - if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { - return self.fail("stack offset too large", .{}); - } - switch (ty.zigTypeTag()) { .Int => switch (ty.intInfo(self.target.*).signedness) { .signed => { From 587eacefec7d00c60cb32a10f7084dd7e61a970a Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 27 Mar 2023 03:36:44 -0400 Subject: [PATCH 6/9] x86_64: fix 64-bit multiply by 32-bit immediate --- src/arch/x86_64/CodeGen.zig | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 2e802269334f..9128d6ce2259 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -4974,6 +4974,10 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .ptr_stack_offset => unreachable, .register_overflow => unreachable, .register => |dst_reg| { + const dst_alias = registerAlias(dst_reg, abi_size); + const dst_lock = self.register_manager.lockReg(dst_reg); + defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); + switch (src_mcv) { .none => unreachable, .undef => try self.genSetReg(dst_ty, dst_reg, .undef), @@ -4982,21 +4986,18 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .register_overflow => unreachable, .register => |src_reg| try self.asmRegisterRegister( .imul, - registerAlias(dst_reg, abi_size), + dst_alias, registerAlias(src_reg, abi_size), ), .immediate => |imm| { - if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) { - // TODO take into account the type's ABI size when selecting the register alias - // register, immediate + if (std.math.cast(i32, imm)) |small| { try self.asmRegisterRegisterImmediate( .imul, - dst_reg.to32(), - dst_reg.to32(), - Immediate.u(@intCast(u32, imm)), + dst_alias, + dst_alias, + Immediate.s(small), ); } else { - // TODO verify we don't spill and assign to the same register as dst_mcv const src_reg = try self.copyToTmpRegister(dst_ty, src_mcv); return self.genIntMulComplexOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg }); } @@ -5004,7 +5005,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M .stack_offset => |off| { try self.asmRegisterMemory( .imul, - registerAlias(dst_reg, abi_size), + dst_alias, Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -off }), ); }, From 6c5442841565196ddeb90735496aad04db3ecdfd Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 27 Mar 2023 04:05:19 -0400 Subject: [PATCH 7/9] x86_64: implement trunc with large source --- src/arch/x86_64/CodeGen.zig | 78 +++++++++++++++---------------------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 9128d6ce2259..a3615611bacc 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -1549,43 +1549,31 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - if (self.liveness.isUnused(inst)) - return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); - - const src_ty = self.air.typeOf(ty_op.operand); - const dst_ty = self.air.typeOfIndex(inst); - const operand = try self.resolveInst(ty_op.operand); - - const src_ty_size = src_ty.abiSize(self.target.*); - const dst_ty_size = dst_ty.abiSize(self.target.*); + const result = if (self.liveness.isUnused(inst)) .dead else result: { + const dst_ty = self.air.typeOfIndex(inst); + const dst_abi_size = dst_ty.abiSize(self.target.*); + if (dst_abi_size > 8) { + return self.fail("TODO implement trunc for abi sizes larger than 8", .{}); + } - if (src_ty_size > 8 or dst_ty_size > 8) { - return self.fail("TODO implement trunc for abi sizes larger than 8", .{}); - } + const src_mcv = try self.resolveInst(ty_op.operand); + const src_lock = switch (src_mcv) { + .register => |reg| self.register_manager.lockRegAssumeUnused(reg), + else => null, + }; + defer if (src_lock) |lock| self.register_manager.unlockReg(lock); - const operand_lock: ?RegisterLock = switch (operand) { - .register => |reg| self.register_manager.lockRegAssumeUnused(reg), - else => null, - }; - defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); + const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) + src_mcv + else + try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); - const reg: Register = blk: { - if (operand.isRegister()) { - if (self.reuseOperand(inst, ty_op.operand, 0, operand)) { - break :blk operand.register.to64(); - } - } - const mcv = try self.copyToRegisterWithInstTracking(inst, src_ty, operand); - break :blk mcv.register.to64(); + // when truncating a `u16` to `u5`, for example, those top 3 bits in the result + // have to be removed. this only happens if the dst if not a power-of-two size. + if (self.regExtraBits(dst_ty) > 0) try self.truncateRegister(dst_ty, dst_mcv.register.to64()); + break :result dst_mcv; }; - - // when truncating a `u16` to `u5`, for example, those top 3 bits in the result - // have to be removed. this only happens if the dst if not a power-of-two size. - if (self.regExtraBits(dst_ty) > 0) { - try self.truncateRegister(dst_ty, reg); - } - - return self.finishAir(inst, .{ .register = reg }, .{ ty_op.operand, .none, .none }); + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { @@ -3499,23 +3487,21 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { const elem_ty = self.air.typeOfIndex(inst); const elem_size = elem_ty.abiSize(self.target.*); const result: MCValue = result: { - if (!elem_ty.hasRuntimeBitsIgnoreComptime()) - break :result MCValue.none; + if (!elem_ty.hasRuntimeBitsIgnoreComptime()) break :result .none; + + try self.spillRegisters(&.{ .rdi, .rsi, .rcx }); + const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx }); + defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); const ptr = try self.resolveInst(ty_op.operand); const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr(); - if (self.liveness.isUnused(inst) and !is_volatile) - break :result MCValue.dead; + if (self.liveness.isUnused(inst) and !is_volatile) break :result .dead; - const dst_mcv: MCValue = blk: { - if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { - // The MCValue that holds the pointer can be re-used as the value. - break :blk ptr; - } else { - break :blk try self.allocRegOrMem(inst, true); - } - }; - log.debug("airLoad(%{d}): {} <- {}", .{ inst, dst_mcv, ptr }); + const dst_mcv: MCValue = if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) + // The MCValue that holds the pointer can be re-used as the value. + ptr + else + try self.allocRegOrMem(inst, true); try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); break :result dst_mcv; }; From 802c2e4fae4450679c995767d6409dbc9cf568b3 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 27 Mar 2023 04:30:41 -0400 Subject: [PATCH 8/9] x86_64: fix popcnt and disable regressed test --- src/arch/x86_64/CodeGen.zig | 6 +++++- test/behavior/bugs/9584.zig | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index a3615611bacc..5f39bc586a8f 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -3095,7 +3095,8 @@ fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { }; defer if (mat_src_lock) |lock| self.register_manager.unlockReg(lock); - const dst_mcv: MCValue = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) + const dst_mcv: MCValue = + if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else .{ .register = try self.register_manager.allocReg(inst, gp) }; @@ -5478,6 +5479,9 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { } const ty = self.air.typeOf(bin_op.lhs); + const abi_size = ty.abiSize(self.target.*); + if (abi_size > 8) return self.fail("TODO implement cmp for large values", .{}); + const signedness: std.builtin.Signedness = blk: { // For non-int types, we treat the values as unsigned if (ty.zigTypeTag() != .Int) break :blk .unsigned; diff --git a/test/behavior/bugs/9584.zig b/test/behavior/bugs/9584.zig index 307f1689bfb7..fe3dedcd30a5 100644 --- a/test/behavior/bugs/9584.zig +++ b/test/behavior/bugs/9584.zig @@ -47,6 +47,7 @@ test { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var flags = A{ .a = false, From fd13e44e0c69465cf9ea9ef4fd17dfebf5cffd45 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Mon, 27 Mar 2023 05:03:28 -0400 Subject: [PATCH 9/9] x86_64: cleanup debug mir dumping --- src/arch/x86_64/CodeGen.zig | 102 ++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 58 deletions(-) diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 5f39bc586a8f..b071d144476d 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -8,7 +8,6 @@ const link = @import("../../link.zig"); const log = std.log.scoped(.codegen); const math = std.math; const mem = std.mem; -const print_air = @import("../../print_air.zig"); const trace = @import("../../tracy.zig").trace; const Air = @import("../../Air.zig"); @@ -107,12 +106,8 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, /// For mir debug info, maps a mir index to a air index mir_to_air_map: if (builtin.mode == .Debug) std.AutoHashMap(Mir.Inst.Index, Air.Inst.Index) else void, -debug_wip_mir_inst: @TypeOf(debug_wip_mir_inst_init) = debug_wip_mir_inst_init, - const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; -const debug_wip_mir_inst_init = if (debug_wip_mir) @as(Mir.Inst.Index, 0) else {}; - pub const MCValue = union(enum) { /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. /// TODO Look into deleting this tag and using `dead` instead, since every use @@ -395,11 +390,49 @@ pub fn generate( } } +fn dumpWipMir(self: *Self, inst: Mir.Inst) !void { + if (!debug_wip_mir) return; + const stderr = std.io.getStdErr().writer(); + + var lower = Lower{ + .allocator = self.gpa, + .mir = .{ + .instructions = self.mir_instructions.slice(), + .extra = self.mir_extra.items, + }, + .target = self.target, + .src_loc = self.src_loc, + }; + for (lower.lowerMir(inst) catch |err| switch (err) { + error.LowerFail => { + defer { + lower.err_msg.?.deinit(self.gpa); + lower.err_msg = null; + } + try stderr.print("{s}\n", .{lower.err_msg.?.msg}); + return; + }, + error.InvalidInstruction, error.CannotEncode => |e| { + try stderr.writeAll(switch (e) { + error.InvalidInstruction => "CodeGen failed to find a viable instruction.\n", + error.CannotEncode => "CodeGen failed to encode the instruction.\n", + }); + return; + }, + else => |e| return e, + }) |lower_inst| { + try stderr.writeAll(" | "); + try lower_inst.fmtPrint(stderr); + try stderr.writeByte('\n'); + } +} + fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { const gpa = self.gpa; try self.mir_instructions.ensureUnusedCapacity(gpa, 1); const result_index = @intCast(Mir.Inst.Index, self.mir_instructions.len); self.mir_instructions.appendAssumeCapacity(inst); + self.dumpWipMir(inst) catch {}; return result_index; } @@ -697,56 +730,6 @@ fn asmMemoryRegisterImmediate( }); } -fn printWipMir(self: *Self, stream: anytype, air_inst: ?Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; - if (!debug_wip_mir) return; - - var lower = Lower{ - .allocator = self.gpa, - .mir = .{ - .instructions = self.mir_instructions.slice(), - .extra = self.mir_extra.items, - }, - .target = self.target, - .src_loc = self.src_loc, - }; - var mir_inst = self.debug_wip_mir_inst; - var mir_end = @intCast(Mir.Inst.Index, self.mir_instructions.len); - defer self.debug_wip_mir_inst = mir_end; - while (mir_inst < mir_end) : (mir_inst += 1) { - for (lower.lowerMir(lower.mir.instructions.get(mir_inst)) catch |err| switch (err) { - error.LowerFail => { - defer { - lower.err_msg.?.deinit(self.gpa); - lower.err_msg = null; - } - try stream.print("{s}\n", .{lower.err_msg.?.msg}); - continue; - }, - error.InvalidInstruction, error.CannotEncode => |e| { - try stream.writeAll(switch (e) { - error.InvalidInstruction => "CodeGen failed to find a viable instruction.\n", - error.CannotEncode => "CodeGen failed to encode the instruction.\n", - }); - continue; - }, - else => |e| return e, - }) |lower_inst| { - try stream.writeAll(" | "); - try lower_inst.fmtPrint(stream); - try stream.writeByte('\n'); - } - } - - if (air_inst) |inst| { - print_air.writeInst(stream, inst, mod, self.air, self.liveness); - } -} - -fn dumpWipMir(self: *Self, air_inst: ?Air.Inst.Index) void { - self.printWipMir(std.io.getStdErr().writer(), air_inst) catch return; -} - fn gen(self: *Self) InnerError!void { const cc = self.fn_type.fnCallingConvention(); if (cc != .Naked) { @@ -900,8 +883,6 @@ fn gen(self: *Self) InnerError!void { .ops = undefined, .data = .{ .payload = payload }, }); - - self.dumpWipMir(null); } fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { @@ -913,7 +894,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { if (builtin.mode == .Debug) { try self.mir_to_air_map.put(@intCast(Mir.Inst.Index, self.mir_instructions.len), inst); } - self.dumpWipMir(inst); + if (debug_wip_mir) @import("../../print_air.zig").dumpInst( + inst, + self.bin_file.options.module.?, + self.air, + self.liveness, + ); switch (air_tags[inst]) { // zig fmt: off