x86_64: downstream table-driven instruction encoder

This commit is contained in:
Jakub Konka 2023-03-05 17:40:53 +01:00
parent 4ea2f441df
commit 817fb263b5
7 changed files with 2622 additions and 3262 deletions

View File

@ -303,7 +303,12 @@ pub fn generate(
var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
error.CodegenFail => return Result{ .fail = function.err_msg.? },
error.OutOfRegisters => return Result{
.fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
.fail = try ErrorMsg.create(
bin_file.allocator,
src_loc,
"CodeGen ran out of registers. This is a bug in the Zig compiler.",
.{},
),
},
else => |e| return e,
};
@ -342,6 +347,20 @@ pub fn generate(
defer emit.deinit();
emit.lowerMir() catch |err| switch (err) {
error.EmitFail => return Result{ .fail = emit.err_msg.? },
error.InvalidInstruction, error.CannotEncode => |e| {
const msg = switch (e) {
error.InvalidInstruction => "CodeGen failed to find a viable instruction.",
error.CannotEncode => "CodeGen failed to encode the instruction.",
};
return Result{
.fail = try ErrorMsg.create(
bin_file.allocator,
src_loc,
"{s} This is a bug in the Zig compiler.",
.{msg},
),
};
},
else => |e| return e,
};
@ -1687,7 +1706,7 @@ fn genIntMulDivOpMir(
else => unreachable,
},
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
else => unreachable,
@ -2191,7 +2210,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
.reg2 = .rbp,
.flags = 0b01,
}),
.data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) },
.data = .{ .disp = -@intCast(i32, off) },
});
},
else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}),
@ -2275,7 +2294,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
.reg1 = addr_reg.to64(),
.reg2 = .rbp,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
.stack_offset => |off| {
@ -2286,7 +2305,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
.reg1 = addr_reg.to64(),
.reg2 = .rbp,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
.memory, .linker_load => {
@ -2352,7 +2371,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
.reg2 = dst_mcv.register,
.flags = 0b01,
}),
.data = .{ .imm = 0 },
.data = .{ .disp = 0 },
});
break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) };
}
@ -2615,7 +2634,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
.reg2 = reg,
.flags = 0b01,
}),
.data = .{ .imm = 0 },
.data = .{ .disp = 0 },
});
},
.stack_offset => |off| {
@ -2842,7 +2861,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
.reg2 = addr_reg.to64(),
.flags = 0b01,
}),
.data = .{ .imm = 0 },
.data = .{ .disp = 0 },
});
const new_ptr = MCValue{ .register = addr_reg.to64() };
@ -2903,7 +2922,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
.reg2 = tmp_reg,
.flags = 0b01,
}),
.data = .{ .imm = 0 },
.data = .{ .disp = 0 },
});
return self.store(new_ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
}
@ -3542,25 +3561,13 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
if (intrinsicsAllowed(self.target.*, dst_ty)) {
const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) {
.f32 => switch (mir_tag) {
.add => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.add_f32_avx
else
Mir.Inst.Tag.add_f32_sse,
.cmp => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.cmp_f32_avx
else
Mir.Inst.Tag.cmp_f32_sse,
.add => Mir.Inst.Tag.add_f32,
.cmp => Mir.Inst.Tag.cmp_f32,
else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}),
},
.f64 => switch (mir_tag) {
.add => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.add_f64_avx
else
Mir.Inst.Tag.add_f64_sse,
.cmp => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.cmp_f64_avx
else
Mir.Inst.Tag.cmp_f64_sse,
.add => Mir.Inst.Tag.add_f64,
.cmp => Mir.Inst.Tag.cmp_f64,
else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}),
},
else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}),
@ -3618,7 +3625,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
.reg2 = .rbp,
.flags = 0b01,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
}
@ -3644,7 +3651,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
.reg2 = registerAlias(src_reg, abi_size),
.flags = 0b10,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
.immediate => |imm| {
@ -3665,7 +3672,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
else => unreachable,
};
const payload = try self.addExtra(Mir.ImmPair{
.dest_off = @bitCast(u32, -off),
.dest_off = -off,
.operand = @truncate(u32, imm),
});
_ = try self.addInst(.{
@ -3756,7 +3763,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M
.reg2 = .rbp,
.flags = 0b01,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
.memory => {
@ -5360,7 +5367,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
// offset from rbp, which is at the top of the stack frame.
// mov [rbp+offset], immediate
const payload = try self.addExtra(Mir.ImmPair{
.dest_off = @bitCast(u32, -stack_offset),
.dest_off = -stack_offset,
.operand = @truncate(u32, imm),
});
_ = try self.addInst(.{
@ -5400,14 +5407,8 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
.Float => {
if (intrinsicsAllowed(self.target.*, ty)) {
const tag: Mir.Inst.Tag = switch (ty.tag()) {
.f32 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f32_avx
else
Mir.Inst.Tag.mov_f32_sse,
.f64 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f64_avx
else
Mir.Inst.Tag.mov_f64_sse,
.f32 => Mir.Inst.Tag.mov_f32,
.f64 => Mir.Inst.Tag.mov_f64,
else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}),
};
_ = try self.addInst(.{
@ -5421,7 +5422,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
.reg2 = reg.to128(),
.flags = 0b01,
}),
.data = .{ .imm = @bitCast(u32, -stack_offset) },
.data = .{ .disp = -stack_offset },
});
return;
}
@ -5436,7 +5437,7 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
.reg2 = registerAlias(reg, @intCast(u32, abi_size)),
.flags = 0b10,
}),
.data = .{ .imm = @bitCast(u32, -stack_offset) },
.data = .{ .disp = -stack_offset },
});
},
}
@ -5516,7 +5517,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
0 => {
assert(ty.isError());
const payload = try self.addExtra(Mir.ImmPair{
.dest_off = @bitCast(u32, -stack_offset),
.dest_off = -stack_offset,
.operand = @truncate(u32, x_big),
});
_ = try self.addInst(.{
@ -5530,7 +5531,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
},
1, 2, 4 => {
const payload = try self.addExtra(Mir.ImmPair{
.dest_off = @bitCast(u32, -stack_offset),
.dest_off = -stack_offset,
.operand = @truncate(u32, x_big),
});
_ = try self.addInst(.{
@ -5552,7 +5553,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
// insted just use two 32 bit writes to avoid register allocation
{
const payload = try self.addExtra(Mir.ImmPair{
.dest_off = @bitCast(u32, -stack_offset + 4),
.dest_off = -stack_offset + 4,
.operand = @truncate(u32, x_big >> 32),
});
_ = try self.addInst(.{
@ -5566,7 +5567,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
}
{
const payload = try self.addExtra(Mir.ImmPair{
.dest_off = @bitCast(u32, -stack_offset),
.dest_off = -stack_offset,
.operand = @truncate(u32, x_big),
});
_ = try self.addInst(.{
@ -5595,14 +5596,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
.Float => {
if (intrinsicsAllowed(self.target.*, ty)) {
const tag: Mir.Inst.Tag = switch (ty.tag()) {
.f32 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f32_avx
else
Mir.Inst.Tag.mov_f32_sse,
.f64 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f64_avx
else
Mir.Inst.Tag.mov_f64_sse,
.f32 => Mir.Inst.Tag.mov_f32,
.f64 => Mir.Inst.Tag.mov_f64,
else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}),
};
_ = try self.addInst(.{
@ -5616,7 +5611,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
.reg2 = reg.to128(),
.flags = 0b01,
}),
.data = .{ .imm = @bitCast(u32, -stack_offset) },
.data = .{ .disp = -stack_offset },
});
return;
}
@ -5691,7 +5686,7 @@ fn genInlineMemcpyRegisterRegister(
.reg2 = registerAlias(tmp_reg, nearest_power_of_two),
.flags = 0b10,
}),
.data = .{ .imm = @bitCast(u32, -next_offset) },
.data = .{ .disp = -next_offset },
});
if (nearest_power_of_two > 1) {
@ -5711,7 +5706,7 @@ fn genInlineMemcpyRegisterRegister(
.reg2 = registerAlias(src_reg, @intCast(u32, abi_size)),
.flags = 0b10,
}),
.data = .{ .imm = @bitCast(u32, -offset) },
.data = .{ .disp = -offset },
});
}
}
@ -5758,7 +5753,7 @@ fn genInlineMemcpy(
.reg1 = dst_addr_reg.to64(),
.reg2 = opts.dest_stack_base orelse .rbp,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
.register => |reg| {
@ -5787,7 +5782,7 @@ fn genInlineMemcpy(
.reg1 = src_addr_reg.to64(),
.reg2 = opts.source_stack_base orelse .rbp,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
.register => |reg| {
@ -5911,7 +5906,7 @@ fn genInlineMemset(
.reg1 = addr_reg.to64(),
.reg2 = opts.dest_stack_base orelse .rbp,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
.register => |reg| {
@ -5998,7 +5993,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.reg1 = registerAlias(reg, abi_size),
.reg2 = .rbp,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
.unreach, .none => return, // Nothing to do.
@ -6097,14 +6092,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.Float => {
if (intrinsicsAllowed(self.target.*, ty)) {
const tag: Mir.Inst.Tag = switch (ty.tag()) {
.f32 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f32_avx
else
Mir.Inst.Tag.mov_f32_sse,
.f64 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f64_avx
else
Mir.Inst.Tag.mov_f64_sse,
.f32 => Mir.Inst.Tag.mov_f32,
.f64 => Mir.Inst.Tag.mov_f64,
else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}),
};
_ = try self.addInst(.{
@ -6141,14 +6130,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
if (intrinsicsAllowed(self.target.*, ty)) {
const tag: Mir.Inst.Tag = switch (ty.tag()) {
.f32 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f32_avx
else
Mir.Inst.Tag.mov_f32_sse,
.f64 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f64_avx
else
Mir.Inst.Tag.mov_f64_sse,
.f32 => Mir.Inst.Tag.mov_f32,
.f64 => Mir.Inst.Tag.mov_f64,
else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
};
@ -6162,7 +6145,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
else => unreachable,
},
}),
.data = .{ .imm = 0 },
.data = .{ .disp = 0 },
});
return;
}
@ -6178,7 +6161,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.reg2 = reg.to64(),
.flags = 0b01,
}),
.data = .{ .imm = 0 },
.data = .{ .disp = 0 },
});
},
}
@ -6190,14 +6173,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
if (intrinsicsAllowed(self.target.*, ty)) {
const tag: Mir.Inst.Tag = switch (ty.tag()) {
.f32 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f32_avx
else
Mir.Inst.Tag.mov_f32_sse,
.f64 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f64_avx
else
Mir.Inst.Tag.mov_f64_sse,
.f32 => Mir.Inst.Tag.mov_f32,
.f64 => Mir.Inst.Tag.mov_f64,
else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
};
@ -6211,7 +6188,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
else => unreachable,
},
}),
.data = .{ .imm = 0 },
.data = .{ .disp = 0 },
});
return;
}
@ -6255,7 +6232,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.reg2 = reg.to64(),
.flags = 0b01,
}),
.data = .{ .imm = 0 },
.data = .{ .disp = 0 },
});
}
}
@ -6283,7 +6260,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.reg2 = .rbp,
.flags = flags,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
return;
}
@ -6302,7 +6279,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.reg2 = .rbp,
.flags = flags,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
return;
}
@ -6311,14 +6288,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.Float => {
if (intrinsicsAllowed(self.target.*, ty)) {
const tag: Mir.Inst.Tag = switch (ty.tag()) {
.f32 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f32_avx
else
Mir.Inst.Tag.mov_f32_sse,
.f64 => if (hasAvxSupport(self.target.*))
Mir.Inst.Tag.mov_f64_avx
else
Mir.Inst.Tag.mov_f64_sse,
.f32 => Mir.Inst.Tag.mov_f32,
.f64 => Mir.Inst.Tag.mov_f64,
else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}),
};
_ = try self.addInst(.{
@ -6331,7 +6302,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
else => unreachable,
},
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
return;
}
@ -6347,7 +6318,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
.reg2 = .rbp,
.flags = 0b01,
}),
.data = .{ .imm = @bitCast(u32, -off) },
.data = .{ .disp = -off },
});
},
}
@ -6436,7 +6407,7 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
else => |size| return self.fail("TODO load ST(0) with abiSize={}", .{size}),
},
}),
.data = .{ .imm = @bitCast(u32, -stack_offset) },
.data = .{ .disp = -stack_offset },
});
// convert
@ -6452,7 +6423,7 @@ fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
else => |size| return self.fail("TODO convert float with abiSize={}", .{size}),
},
}),
.data = .{ .imm = @bitCast(u32, -stack_dst.stack_offset) },
.data = .{ .disp = -stack_dst.stack_offset },
});
return self.finishAir(inst, stack_dst, .{ ty_op.operand, .none, .none });
@ -6551,7 +6522,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
.reg2 = reg,
.flags = 0b01,
}),
.data = .{ .imm = 0 },
.data = .{ .disp = 0 },
});
break :blk MCValue{ .register = reg };
},

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,521 @@
const Encoding = @This();
const std = @import("std");
const assert = std.debug.assert;
const math = std.math;
const bits = @import("bits.zig");
const encoder = @import("encoder.zig");
const Instruction = encoder.Instruction;
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: u2,
opc: [3]u8,
modrm_ext: u3,
mode: Mode,
pub fn findByMnemonic(mnemonic: Mnemonic, args: struct {
op1: Instruction.Operand,
op2: Instruction.Operand,
op3: Instruction.Operand,
op4: Instruction.Operand,
}) ?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);
// 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;
inline 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],
};
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))
{
candidates[count] = enc;
count += 1;
}
}
if (count == 0) return null;
if (count == 1) return candidates[0];
const EncodingLength = struct {
fn estimate(encoding: Encoding, params: struct {
op1: Instruction.Operand,
op2: Instruction.Operand,
op3: Instruction.Operand,
op4: Instruction.Operand,
}) usize {
var inst = Instruction{
.op1 = params.op1,
.op2 = params.op2,
.op3 = params.op3,
.op4 = params.op4,
.encoding = encoding,
};
var cwriter = std.io.countingWriter(std.io.null_writer);
inst.encode(cwriter.writer()) catch unreachable;
return cwriter.bytes_written;
}
};
var shortest_encoding: ?struct {
index: usize,
len: usize,
} = null;
var i: usize = 0;
while (i < count) : (i += 1) {
const len = EncodingLength.estimate(candidates[i], .{
.op1 = args.op1,
.op2 = args.op2,
.op3 = args.op3,
.op4 = args.op4,
});
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];
}
/// Returns first matching encoding by opcode.
pub fn findByOpcode(opc: []const u8, prefixes: struct {
legacy: LegacyPrefixes,
rex: Rex,
}, modrm_ext: ?u3) ?Encoding {
inline 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);
}
break :match std.mem.eql(u8, enc.opcode(), opc);
};
if (match) {
if (prefixes.rex.w) {
switch (enc.mode) {
.fpu, .sse, .sse2 => {},
.long => return enc,
.none => {
// TODO this is a hack to allow parsing of instructions which contain
// spurious prefix bytes such as
// rex.W mov dil, 0x1
// Here, rex.W is not needed.
const rex_w_allowed = blk: {
const bit_size = enc.operandSize();
break :blk bit_size == 64 or bit_size == 8;
};
if (rex_w_allowed) return enc;
},
}
} else if (prefixes.legacy.prefix_66) {
switch (enc.operandSize()) {
16 => return enc,
else => {},
}
} else {
if (enc.mode == .none) {
switch (enc.operandSize()) {
16 => {},
else => return enc,
}
}
}
}
}
return null;
}
pub fn opcode(encoding: *const Encoding) []const u8 {
return encoding.opc[0..encoding.opc_len];
}
pub fn mandatoryPrefix(encoding: *const Encoding) ?u8 {
const prefix = encoding.opc[0];
return switch (prefix) {
0x66, 0xf2, 0xf3 => prefix,
else => null,
};
}
pub fn modRmExt(encoding: Encoding) u3 {
return switch (encoding.op_en) {
.m, .mi, .m1, .mc => encoding.modrm_ext,
else => unreachable,
};
}
pub fn operandSize(encoding: Encoding) u32 {
if (encoding.mode == .long) return 64;
const bit_size: u32 = switch (encoding.op_en) {
.np => switch (encoding.op1) {
.o16 => 16,
.o32 => 32,
.o64 => 64,
else => 32,
},
.td => encoding.op2.size(),
else => encoding.op1.size(),
};
return bit_size;
}
pub fn format(
encoding: Encoding,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = options;
_ = fmt;
switch (encoding.mode) {
.long => try writer.writeAll("REX.W + "),
else => {},
}
for (encoding.opcode()) |byte| {
try writer.print("{x:0>2} ", .{byte});
}
switch (encoding.op_en) {
.np, .fd, .td, .i, .zi, .d => {},
.o, .oi => {
const tag = switch (encoding.op1) {
.r8 => "rb",
.r16 => "rw",
.r32 => "rd",
.r64 => "rd",
else => unreachable,
};
try writer.print("+{s} ", .{tag});
},
.m, .mi, .m1, .mc => try writer.print("/{d} ", .{encoding.modRmExt()}),
.mr, .rm, .rmi => try writer.writeAll("/r "),
}
switch (encoding.op_en) {
.i, .d, .zi, .oi, .mi, .rmi => {
const op = switch (encoding.op_en) {
.i, .d => encoding.op1,
.zi, .oi, .mi => encoding.op2,
.rmi => encoding.op3,
else => unreachable,
};
const tag = switch (op) {
.imm8 => "ib",
.imm16 => "iw",
.imm32 => "id",
.imm64 => "io",
.rel8 => "cb",
.rel16 => "cw",
.rel32 => "cd",
else => unreachable,
};
try writer.print("{s} ", .{tag});
},
.np, .fd, .td, .o, .m, .m1, .mc, .mr, .rm => {},
}
try writer.print("{s} ", .{@tagName(encoding.mnemonic)});
const ops = &[_]Op{ encoding.op1, encoding.op2, encoding.op3, encoding.op4 };
for (ops) |op| switch (op) {
.none, .o16, .o32, .o64 => break,
else => try writer.print("{s} ", .{@tagName(op)}),
};
const op_en = switch (encoding.op_en) {
.zi => .i,
else => |op_en| op_en,
};
try writer.print("{s}", .{@tagName(op_en)});
}
pub const Mnemonic = enum {
// zig fmt: off
// General-purpose
adc, add, @"and",
call, cbw, cwde, cdqe, cwd, cdq, cqo, cmp,
cmova, cmovae, cmovb, cmovbe, cmovc, cmove, cmovg, cmovge, cmovl, cmovle, cmovna,
cmovnae, cmovnb, cmovnbe, cmovnc, cmovne, cmovng, cmovnge, cmovnl, cmovnle, cmovno,
cmovnp, cmovns, cmovnz, cmovo, cmovp, cmovpe, cmovpo, cmovs, cmovz,
div,
fisttp, fld,
idiv, imul, int3,
ja, jae, jb, jbe, jc, jrcxz, je, jg, jge, jl, jle, jna, jnae, jnb, jnbe,
jnc, jne, jng, jnge, jnl, jnle, jno, jnp, jns, jnz, jo, jp, jpe, jpo, js, jz,
jmp,
lea,
mov, movsx, movsxd, movzx, mul,
nop,
@"or",
pop, push,
ret,
sal, sar, sbb, shl, shr, sub, syscall,
seta, setae, setb, setbe, setc, sete, setg, setge, setl, setle, setna, setnae,
setnb, setnbe, setnc, setne, setng, setnge, setnl, setnle, setno, setnp, setns,
setnz, seto, setp, setpe, setpo, sets, setz,
@"test",
ud2,
xor,
// SSE
addss,
cmpss,
movss,
ucomiss,
// SSE2
addsd,
cmpsd,
movq, movsd,
ucomisd,
// zig fmt: on
};
pub const OpEn = enum {
// zig fmt: off
np,
o, oi,
i, zi,
d, m,
fd, td,
m1, mc, mi, mr, rm, rmi,
// zig fmt: on
};
pub const Op = enum {
// zig fmt: off
none,
o16, o32, o64,
unity,
imm8, imm16, imm32, imm64,
al, ax, eax, rax,
cl,
r8, r16, r32, r64,
rm8, rm16, rm32, rm64,
m8, m16, m32, m64, m80,
rel8, rel16, rel32,
m,
moffs,
sreg,
xmm, xmm_m32, xmm_m64,
// zig fmt: on
pub fn fromOperand(operand: Instruction.Operand) Op {
switch (operand) {
.none => return .none,
.reg => |reg| {
switch (reg.class()) {
.segment => return .sreg,
.floating_point => return switch (reg.size()) {
128 => .xmm,
else => unreachable,
},
.general_purpose => {
if (reg.to64() == .rax) return switch (reg) {
.al => .al,
.ax => .ax,
.eax => .eax,
.rax => .rax,
else => unreachable,
};
if (reg == .cl) return .cl;
return switch (reg.size()) {
8 => .r8,
16 => .r16,
32 => .r32,
64 => .r64,
else => unreachable,
};
},
}
},
.mem => |mem| switch (mem) {
.moffs => return .moffs,
.sib, .rip => {
const bit_size = mem.size();
return switch (bit_size) {
8 => .m8,
16 => .m16,
32 => .m32,
64 => .m64,
80 => .m80,
else => unreachable,
};
},
},
.imm => |imm| {
if (imm == 1) return .unity;
if (math.cast(i8, imm)) |_| return .imm8;
if (math.cast(i16, imm)) |_| return .imm16;
if (math.cast(i32, imm)) |_| return .imm32;
return .imm64;
},
}
}
pub fn size(op: Op) u32 {
return switch (op) {
.none, .o16, .o32, .o64, .moffs, .m, .sreg, .unity => unreachable,
.imm8, .al, .cl, .r8, .m8, .rm8, .rel8 => 8,
.imm16, .ax, .r16, .m16, .rm16, .rel16 => 16,
.imm32, .eax, .r32, .m32, .rm32, .rel32, .xmm_m32 => 32,
.imm64, .rax, .r64, .m64, .rm64, .xmm_m64 => 64,
.m80 => 80,
.xmm => 128,
};
}
pub fn isRegister(op: Op) bool {
// zig fmt: off
return switch (op) {
.cl,
.al, .ax, .eax, .rax,
.r8, .r16, .r32, .r64,
.rm8, .rm16, .rm32, .rm64,
.xmm, .xmm_m32, .xmm_m64,
=> true,
else => false,
};
// zig fmt: on
}
pub fn isImmediate(op: Op) bool {
// zig fmt: off
return switch (op) {
.imm8, .imm16, .imm32, .imm64,
.rel8, .rel16, .rel32,
.unity,
=> true,
else => false,
};
// zig fmt: on
}
pub fn isMemory(op: Op) bool {
// zig fmt: off
return switch (op) {
.rm8, .rm16, .rm32, .rm64,
.m8, .m16, .m32, .m64, .m80,
.m,
.xmm_m32, .xmm_m64,
=> true,
else => false,
};
// zig fmt: on
}
pub fn isSegmentRegister(op: Op) bool {
return switch (op) {
.moffs, .sreg => true,
else => false,
};
}
pub fn isFloatingPointRegister(op: Op) bool {
return switch (op) {
.xmm, .xmm_m32, .xmm_m64 => true,
else => false,
};
}
/// Given an operand `op` checks if `target` is a subset for the purposes
/// of the encoding.
pub fn isSubset(op: Op, target: Op, mode: Mode) bool {
switch (op) {
.m, .o16, .o32, .o64 => unreachable,
.moffs, .sreg => return op == target,
.none => switch (target) {
.o16, .o32, .o64, .none => return true,
else => return false,
},
else => {
if (op.isRegister() and target.isRegister()) {
switch (mode) {
.sse, .sse2 => return op.isFloatingPointRegister() and target.isFloatingPointRegister(),
else => switch (target) {
.cl, .al, .ax, .eax, .rax => return op == target,
else => return op.size() == target.size(),
},
}
}
if (op.isMemory() and target.isMemory()) {
switch (target) {
.m => return true,
else => return op.size() == target.size(),
}
}
if (op.isImmediate() and target.isImmediate()) {
switch (target) {
.imm32, .rel32 => switch (op) {
.unity, .imm8, .imm16, .imm32 => return true,
else => return op == target,
},
.imm16, .rel16 => switch (op) {
.unity, .imm8, .imm16 => return true,
else => return op == target,
},
.imm8, .rel8 => switch (op) {
.unity, .imm8 => return true,
else => return op == target,
},
else => return op == target,
}
}
return false;
},
}
}
};
pub const Mode = enum {
none,
fpu,
long,
sse,
sse2,
};

View File

@ -339,41 +339,23 @@ pub const Inst = struct {
/// Nop
nop,
/// SSE instructions
/// SSE/AVX instructions
/// ops flags: form:
/// 0b00 reg1, qword ptr [reg2 + imm32]
/// 0b01 qword ptr [reg1 + imm32], reg2
/// 0b10 reg1, reg2
mov_f64_sse,
mov_f32_sse,
mov_f64,
mov_f32,
/// ops flags: form:
/// 0b00 reg1, reg2
add_f64_sse,
add_f32_sse,
add_f64,
add_f32,
/// ops flags: form:
/// 0b00 reg1, reg2
cmp_f64_sse,
cmp_f32_sse,
/// AVX instructions
/// ops flags: form:
/// 0b00 reg1, qword ptr [reg2 + imm32]
/// 0b01 qword ptr [reg1 + imm32], reg2
/// 0b10 reg1, reg1, reg2
mov_f64_avx,
mov_f32_avx,
/// ops flags: form:
/// 0b00 reg1, reg1, reg2
add_f64_avx,
add_f32_avx,
/// ops flags: form:
/// 0b00 reg1, reg1, reg2
cmp_f64_avx,
cmp_f32_avx,
cmp_f64,
cmp_f32,
/// Pseudo-instructions
/// call extern function
@ -439,6 +421,8 @@ pub const Inst = struct {
inst: Index,
/// A 32-bit immediate value.
imm: u32,
/// A 32-bit signed displacement value.
disp: i32,
/// A condition code for use with EFLAGS register.
cc: bits.Condition,
/// Another instruction with condition code.
@ -476,9 +460,9 @@ pub const IndexRegisterDisp = struct {
index: u32,
/// Displacement value
disp: u32,
disp: i32,
pub fn encode(index: Register, disp: u32) IndexRegisterDisp {
pub fn encode(index: Register, disp: i32) IndexRegisterDisp {
return .{
.index = @enumToInt(index),
.disp = disp,
@ -487,7 +471,7 @@ pub const IndexRegisterDisp = struct {
pub fn decode(this: IndexRegisterDisp) struct {
index: Register,
disp: u32,
disp: i32,
} {
return .{
.index = @intToEnum(Register, this.index),
@ -503,12 +487,12 @@ pub const IndexRegisterDispImm = struct {
index: u32,
/// Displacement value
disp: u32,
disp: i32,
/// Immediate
imm: u32,
pub fn encode(index: Register, disp: u32, imm: u32) IndexRegisterDispImm {
pub fn encode(index: Register, disp: i32, imm: u32) IndexRegisterDispImm {
return .{
.index = @enumToInt(index),
.disp = disp,
@ -518,7 +502,7 @@ pub const IndexRegisterDispImm = struct {
pub fn decode(this: IndexRegisterDispImm) struct {
index: Register,
disp: u32,
disp: i32,
imm: u32,
} {
return .{
@ -576,7 +560,7 @@ pub const SaveRegisterList = struct {
};
pub const ImmPair = struct {
dest_off: u32,
dest_off: i32,
operand: u32,
};

File diff suppressed because it is too large Load Diff

794
src/arch/x86_64/encoder.zig Normal file
View File

@ -0,0 +1,794 @@
const std = @import("std");
const assert = std.debug.assert;
const math = std.math;
const bits = @import("bits.zig");
const Encoding = @import("Encoding.zig");
const Memory = bits.Memory;
const Moffs = bits.Moffs;
const PtrSize = bits.PtrSize;
const Register = bits.Register;
pub const Instruction = struct {
op1: Operand = .none,
op2: Operand = .none,
op3: Operand = .none,
op4: Operand = .none,
encoding: Encoding,
pub const Mnemonic = Encoding.Mnemonic;
pub const Operand = union(enum) {
none,
reg: Register,
mem: Memory,
imm: i64,
/// Returns the bitsize of the operand.
/// Asserts the operand is either register or memory.
pub fn size(op: Operand) u64 {
return switch (op) {
.none => unreachable,
.reg => |reg| reg.size(),
.mem => |mem| mem.size(),
.imm => unreachable,
};
}
/// Returns true if the operand is a segment register.
/// Asserts the operand is either register or memory.
pub fn isSegmentRegister(op: Operand) bool {
return switch (op) {
.none => unreachable,
.reg => |reg| reg.class() == .segment,
.mem => |mem| mem.isSegmentRegister(),
.imm => unreachable,
};
}
pub fn fmtPrint(op: Operand, enc_op: Encoding.Op, writer: anytype) !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 });
}
try writer.writeByte(']');
},
.sib => |sib| {
try writer.print("{s} ptr ", .{@tagName(sib.ptr_size)});
if (mem.isSegmentRegister()) {
return writer.print("{s}:0x{x}", .{ @tagName(sib.base.?), sib.disp });
}
try writer.writeByte('[');
if (sib.base) |base| {
try writer.print("{s}", .{@tagName(base)});
}
if (sib.scale_index) |si| {
if (sib.base != null) {
try writer.writeAll(" + ");
}
try writer.print("{s} * {d}", .{ @tagName(si.index), si.scale });
}
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});
}
try writer.writeByte(']');
},
.moffs => |moffs| try writer.print("{s}:0x{x}", .{ @tagName(moffs.seg), moffs.offset }),
},
.imm => |imm| {
if (enc_op == .imm64) {
return writer.print("0x{x}", .{@bitCast(u64, imm)});
}
const imm_abs = try std.math.absInt(imm);
if (sign(imm) < 0) {
try writer.writeByte('-');
}
try writer.print("0x{x}", .{imm_abs});
},
}
}
};
pub fn new(mnemonic: Mnemonic, args: struct {
op1: Operand = .none,
op2: Operand = .none,
op3: Operand = .none,
op4: Operand = .none,
}) !Instruction {
const encoding = Encoding.findByMnemonic(mnemonic, .{
.op1 = args.op1,
.op2 = args.op2,
.op3 = args.op3,
.op4 = args.op4,
}) orelse return error.InvalidInstruction;
std.log.debug("{}", .{encoding});
return .{
.op1 = args.op1,
.op2 = args.op2,
.op3 = args.op3,
.op4 = args.op4,
.encoding = encoding,
};
}
pub fn fmtPrint(inst: Instruction, writer: anytype) !void {
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(',');
}
try writer.writeByte(' ');
try op[0].fmtPrint(op[1], writer);
}
}
pub fn encode(inst: Instruction, writer: anytype) !void {
const encoder = Encoder(@TypeOf(writer)){ .writer = writer };
const encoding = inst.encoding;
try inst.encodeLegacyPrefixes(encoder);
try inst.encodeMandatoryPrefix(encoder);
try inst.encodeRexPrefix(encoder);
try inst.encodeOpcode(encoder);
switch (encoding.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),
else => {
const mem_op = switch (encoding.op_en) {
.m, .mi, .m1, .mc, .mr => inst.op1,
.rm, .rmi => inst.op2,
else => unreachable,
};
switch (mem_op) {
.reg => |reg| {
const rm = switch (encoding.op_en) {
.m, .mi, .m1, .mc => encoding.modRmExt(),
.mr => inst.op2.reg.lowEnc(),
.rm, .rmi => inst.op1.reg.lowEnc(),
else => unreachable,
};
try encoder.modRm_direct(rm, reg.lowEnc());
},
.mem => |mem| {
const op = switch (encoding.op_en) {
.m, .mi, .m1, .mc => .none,
.mr => inst.op2,
.rm, .rmi => inst.op1,
else => unreachable,
};
try encodeMemory(encoding, mem, op, encoder);
},
else => unreachable,
}
switch (encoding.op_en) {
.mi => try encodeImm(inst.op2.imm, encoding.op2, encoder),
.rmi => try encodeImm(inst.op3.imm, encoding.op3, encoder),
else => {},
}
},
}
}
fn encodeOpcode(inst: Instruction, encoder: anytype) !void {
const opcode = inst.encoding.opcode();
switch (inst.encoding.op_en) {
.o, .oi => try encoder.opcode_withReg(opcode[0], inst.op1.reg.lowEnc()),
else => {
const index: usize = if (inst.encoding.mandatoryPrefix()) |_| 1 else 0;
for (opcode[index..]) |byte| {
try encoder.opcode_1byte(byte);
}
},
}
}
fn encodeLegacyPrefixes(inst: Instruction, encoder: anytype) !void {
const enc = inst.encoding;
const op_en = enc.op_en;
var legacy = LegacyPrefixes{};
if (enc.mode == .none) {
const bit_size = enc.operandSize();
if (bit_size == 16) {
legacy.set16BitOverride();
}
}
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) {
.reg => |r| r,
.mem => |m| m.base().?,
else => unreachable,
};
} else null,
.m, .mi, .m1, .mc, .mr => if (inst.op1.isSegmentRegister()) blk: {
break :blk switch (inst.op1) {
.reg => |r| r,
.mem => |m| m.base().?,
else => unreachable,
};
} else null,
};
if (segment_override) |seg| {
legacy.setSegmentOverride(seg);
}
try encoder.legacyPrefixes(legacy);
}
fn encodeRexPrefix(inst: Instruction, encoder: anytype) !void {
const op_en = inst.encoding.op_en;
// Check if we need REX and can actually encode it
const is_rex_invalid = for (&[_]Operand{ inst.op1, inst.op2, inst.op3, inst.op4 }) |op| switch (op) {
.reg => |r| if (r.isRexInvalid()) break true,
else => {},
} else false;
var rex = Rex{};
rex.w = inst.encoding.mode == .long;
switch (op_en) {
.np, .i, .zi, .fd, .td, .d => {},
.o, .oi => {
rex.b = inst.op1.reg.isExtended();
},
.m, .mi, .m1, .mc, .mr, .rm, .rmi => {
const r_op = switch (op_en) {
.rm, .rmi => inst.op1,
.mr => inst.op2,
else => null,
};
if (r_op) |op| {
rex.r = op.reg.isExtended();
}
const b_x_op = switch (op_en) {
.rm, .rmi => inst.op2,
.m, .mi, .m1, .mc, .mr => inst.op1,
else => unreachable,
};
switch (b_x_op) {
.reg => |r| {
rex.b = r.isExtended();
},
.mem => |mem| {
rex.b = if (mem.base()) |base| base.isExtended() else false;
rex.x = if (mem.scaleIndex()) |si| si.index.isExtended() else false;
},
else => unreachable,
}
},
}
if (rex.isSet() and is_rex_invalid) return error.CannotEncode;
try encoder.rex(rex);
}
fn encodeMandatoryPrefix(inst: Instruction, encoder: anytype) !void {
const prefix = inst.encoding.mandatoryPrefix() orelse return;
try encoder.opcode_1byte(prefix);
}
fn encodeMemory(encoding: Encoding, mem: Memory, operand: Operand, encoder: anytype) !void {
const operand_enc = switch (operand) {
.reg => |reg| reg.lowEnc(),
.none => encoding.modRmExt(),
else => unreachable,
};
switch (mem) {
.moffs => unreachable,
.sib => |sib| {
if (sib.base) |base| {
if (base.class() == .segment) {
// TODO audit this wrt SIB
try encoder.modRm_SIBDisp0(operand_enc);
if (sib.scale_index) |si| {
const scale = math.log2_int(u4, si.scale);
try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
} else {
try encoder.sib_disp32();
}
try encoder.disp32(sib.disp);
} else {
assert(base.class() == .general_purpose);
const dst = base.lowEnc();
const src = operand_enc;
if (dst == 4 or sib.scale_index != null) {
if (sib.disp == 0 and dst != 5) {
try encoder.modRm_SIBDisp0(src);
if (sib.scale_index) |si| {
const scale = math.log2_int(u4, si.scale);
try encoder.sib_scaleIndexBase(scale, si.index.lowEnc(), dst);
} else {
try encoder.sib_base(dst);
}
} else if (math.cast(i8, sib.disp)) |_| {
try encoder.modRm_SIBDisp8(src);
if (sib.scale_index) |si| {
const scale = math.log2_int(u4, si.scale);
try encoder.sib_scaleIndexBaseDisp8(scale, si.index.lowEnc(), dst);
} else {
try encoder.sib_baseDisp8(dst);
}
try encoder.disp8(@truncate(i8, sib.disp));
} else {
try encoder.modRm_SIBDisp32(src);
if (sib.scale_index) |si| {
const scale = math.log2_int(u4, si.scale);
try encoder.sib_scaleIndexBaseDisp32(scale, si.index.lowEnc(), dst);
} else {
try encoder.sib_baseDisp32(dst);
}
try encoder.disp32(sib.disp);
}
} else {
if (sib.disp == 0 and dst != 5) {
try encoder.modRm_indirectDisp0(src, dst);
} else if (math.cast(i8, sib.disp)) |_| {
try encoder.modRm_indirectDisp8(src, dst);
try encoder.disp8(@truncate(i8, sib.disp));
} else {
try encoder.modRm_indirectDisp32(src, dst);
try encoder.disp32(sib.disp);
}
}
}
} else {
try encoder.modRm_SIBDisp0(operand_enc);
if (sib.scale_index) |si| {
const scale = math.log2_int(u4, si.scale);
try encoder.sib_scaleIndexDisp32(scale, si.index.lowEnc());
} else {
try encoder.sib_disp32();
}
try encoder.disp32(sib.disp);
}
},
.rip => |rip| {
try encoder.modRm_RIPDisp32(operand_enc);
try encoder.disp32(rip.disp);
},
}
}
fn encodeImm(imm: i64, kind: Encoding.Op, encoder: anytype) !void {
switch (kind) {
.imm8, .rel8 => try encoder.imm8(@truncate(i8, imm)),
.imm16, .rel16 => try encoder.imm16(@truncate(i16, imm)),
.imm32, .rel32 => try encoder.imm32(@truncate(i32, imm)),
.imm64 => try encoder.imm64(@bitCast(u64, imm)),
else => unreachable,
}
}
};
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,
/// REPNZ, REPNE, REP, Scalar Double-precision
prefix_f2: bool = false,
/// REPZ, REPE, REP, Scalar Single-precision
prefix_f3: bool = false,
/// CS segment override or Branch not taken
prefix_2e: bool = false,
/// SS segment override
prefix_36: bool = false,
/// ES segment override
prefix_26: bool = false,
/// FS segment override
prefix_64: bool = false,
/// GS segment override
prefix_65: bool = false,
/// Branch taken
prefix_3e: bool = false,
/// Address size override (enables 16 bit address size)
prefix_67: bool = false,
/// Operand size override (enables 16 bit operation)
prefix_66: bool = false,
padding: u5 = 0,
pub fn setSegmentOverride(self: *LegacyPrefixes, reg: Register) void {
assert(reg.class() == .segment);
switch (reg) {
.cs => self.prefix_2e = true,
.ss => self.prefix_36 = true,
.es => self.prefix_26 = true,
.fs => self.prefix_64 = true,
.gs => self.prefix_65 = true,
.ds => {},
else => unreachable,
}
}
pub fn set16BitOverride(self: *LegacyPrefixes) void {
self.prefix_66 = true;
}
};
fn Encoder(comptime T: type) type {
return struct {
writer: T,
const Self = @This();
// --------
// Prefixes
// --------
/// Encodes legacy prefixes
pub fn legacyPrefixes(self: Self, prefixes: LegacyPrefixes) !void {
if (@bitCast(u16, prefixes) != 0) {
// Hopefully this path isn't taken very often, so we'll do it the slow way for now
// LOCK
if (prefixes.prefix_f0) try self.writer.writeByte(0xf0);
// REPNZ, REPNE, REP, Scalar Double-precision
if (prefixes.prefix_f2) try self.writer.writeByte(0xf2);
// REPZ, REPE, REP, Scalar Single-precision
if (prefixes.prefix_f3) try self.writer.writeByte(0xf3);
// CS segment override or Branch not taken
if (prefixes.prefix_2e) try self.writer.writeByte(0x2e);
// DS segment override
if (prefixes.prefix_36) try self.writer.writeByte(0x36);
// ES segment override
if (prefixes.prefix_26) try self.writer.writeByte(0x26);
// FS segment override
if (prefixes.prefix_64) try self.writer.writeByte(0x64);
// GS segment override
if (prefixes.prefix_65) try self.writer.writeByte(0x65);
// Branch taken
if (prefixes.prefix_3e) try self.writer.writeByte(0x3e);
// Operand size override
if (prefixes.prefix_66) try self.writer.writeByte(0x66);
// Address size override
if (prefixes.prefix_67) try self.writer.writeByte(0x67);
}
}
/// Use 16 bit operand size
///
/// Note that this flag is overridden by REX.W, if both are present.
pub fn prefix16BitMode(self: Self) !void {
try self.writer.writeByte(0x66);
}
/// Encodes a REX prefix byte given all the fields
///
/// Use this byte whenever you need 64 bit operation,
/// or one of reg, index, r/m, base, or opcode-reg might be extended.
///
/// See struct `Rex` for a description of each field.
///
/// Does not add a prefix byte if none of the fields are set!
pub fn rex(self: Self, byte: Rex) !void {
var value: u8 = 0b0100_0000;
if (byte.w) value |= 0b1000;
if (byte.r) value |= 0b0100;
if (byte.x) value |= 0b0010;
if (byte.b) value |= 0b0001;
if (value != 0b0100_0000) {
try self.writer.writeByte(value);
}
}
// ------
// Opcode
// ------
/// Encodes a 1 byte opcode
pub fn opcode_1byte(self: Self, opcode: u8) !void {
try self.writer.writeByte(opcode);
}
/// Encodes a 2 byte opcode
///
/// e.g. IMUL has the opcode 0x0f 0xaf, so you use
///
/// encoder.opcode_2byte(0x0f, 0xaf);
pub fn opcode_2byte(self: Self, prefix: u8, opcode: u8) !void {
try self.writer.writeAll(&.{ prefix, opcode });
}
/// Encodes a 3 byte opcode
///
/// e.g. MOVSD has the opcode 0xf2 0x0f 0x10
///
/// encoder.opcode_3byte(0xf2, 0x0f, 0x10);
pub fn opcode_3byte(self: Self, prefix_1: u8, prefix_2: u8, opcode: u8) !void {
try self.writer.writeAll(&.{ prefix_1, prefix_2, opcode });
}
/// Encodes a 1 byte opcode with a reg field
///
/// Remember to add a REX prefix byte if reg is extended!
pub fn opcode_withReg(self: Self, opcode: u8, reg: u3) !void {
assert(opcode & 0b111 == 0);
try self.writer.writeByte(opcode | reg);
}
// ------
// ModR/M
// ------
/// Construct a ModR/M byte given all the fields
///
/// Remember to add a REX prefix byte if reg or rm are extended!
pub fn modRm(self: Self, mod: u2, reg_or_opx: u3, rm: u3) !void {
try self.writer.writeByte(@as(u8, mod) << 6 | @as(u8, reg_or_opx) << 3 | rm);
}
/// Construct a ModR/M byte using direct r/m addressing
/// r/m effective address: r/m
///
/// Note reg's effective address is always just reg for the ModR/M byte.
/// Remember to add a REX prefix byte if reg or rm are extended!
pub fn modRm_direct(self: Self, reg_or_opx: u3, rm: u3) !void {
try self.modRm(0b11, reg_or_opx, rm);
}
/// Construct a ModR/M byte using indirect r/m addressing
/// r/m effective address: [r/m]
///
/// Note reg's effective address is always just reg for the ModR/M byte.
/// Remember to add a REX prefix byte if reg or rm are extended!
pub fn modRm_indirectDisp0(self: Self, reg_or_opx: u3, rm: u3) !void {
assert(rm != 4 and rm != 5);
try self.modRm(0b00, reg_or_opx, rm);
}
/// Construct a ModR/M byte using indirect SIB addressing
/// r/m effective address: [SIB]
///
/// Note reg's effective address is always just reg for the ModR/M byte.
/// Remember to add a REX prefix byte if reg or rm are extended!
pub fn modRm_SIBDisp0(self: Self, reg_or_opx: u3) !void {
try self.modRm(0b00, reg_or_opx, 0b100);
}
/// Construct a ModR/M byte using RIP-relative addressing
/// r/m effective address: [RIP + disp32]
///
/// Note reg's effective address is always just reg for the ModR/M byte.
/// Remember to add a REX prefix byte if reg or rm are extended!
pub fn modRm_RIPDisp32(self: Self, reg_or_opx: u3) !void {
try self.modRm(0b00, reg_or_opx, 0b101);
}
/// Construct a ModR/M byte using indirect r/m with a 8bit displacement
/// r/m effective address: [r/m + disp8]
///
/// Note reg's effective address is always just reg for the ModR/M byte.
/// Remember to add a REX prefix byte if reg or rm are extended!
pub fn modRm_indirectDisp8(self: Self, reg_or_opx: u3, rm: u3) !void {
assert(rm != 4);
try self.modRm(0b01, reg_or_opx, rm);
}
/// Construct a ModR/M byte using indirect SIB with a 8bit displacement
/// r/m effective address: [SIB + disp8]
///
/// Note reg's effective address is always just reg for the ModR/M byte.
/// Remember to add a REX prefix byte if reg or rm are extended!
pub fn modRm_SIBDisp8(self: Self, reg_or_opx: u3) !void {
try self.modRm(0b01, reg_or_opx, 0b100);
}
/// Construct a ModR/M byte using indirect r/m with a 32bit displacement
/// r/m effective address: [r/m + disp32]
///
/// Note reg's effective address is always just reg for the ModR/M byte.
/// Remember to add a REX prefix byte if reg or rm are extended!
pub fn modRm_indirectDisp32(self: Self, reg_or_opx: u3, rm: u3) !void {
assert(rm != 4);
try self.modRm(0b10, reg_or_opx, rm);
}
/// Construct a ModR/M byte using indirect SIB with a 32bit displacement
/// r/m effective address: [SIB + disp32]
///
/// Note reg's effective address is always just reg for the ModR/M byte.
/// Remember to add a REX prefix byte if reg or rm are extended!
pub fn modRm_SIBDisp32(self: Self, reg_or_opx: u3) !void {
try self.modRm(0b10, reg_or_opx, 0b100);
}
// ---
// SIB
// ---
/// Construct a SIB byte given all the fields
///
/// Remember to add a REX prefix byte if index or base are extended!
pub fn sib(self: Self, scale: u2, index: u3, base: u3) !void {
try self.writer.writeByte(@as(u8, scale) << 6 | @as(u8, index) << 3 | base);
}
/// Construct a SIB byte with scale * index + base, no frills.
/// r/m effective address: [base + scale * index]
///
/// Remember to add a REX prefix byte if index or base are extended!
pub fn sib_scaleIndexBase(self: Self, scale: u2, index: u3, base: u3) !void {
assert(base != 5);
try self.sib(scale, index, base);
}
/// Construct a SIB byte with scale * index + disp32
/// r/m effective address: [scale * index + disp32]
///
/// Remember to add a REX prefix byte if index or base are extended!
pub fn sib_scaleIndexDisp32(self: Self, scale: u2, index: u3) !void {
// scale is actually ignored
// index = 4 means no index if and only if we haven't extended the register
// TODO enforce this
// base = 5 means no base, if mod == 0.
try self.sib(scale, index, 5);
}
/// Construct a SIB byte with just base
/// r/m effective address: [base]
///
/// Remember to add a REX prefix byte if index or base are extended!
pub fn sib_base(self: Self, base: u3) !void {
assert(base != 5);
// scale is actually ignored
// index = 4 means no index
try self.sib(0, 4, base);
}
/// Construct a SIB byte with just disp32
/// r/m effective address: [disp32]
///
/// Remember to add a REX prefix byte if index or base are extended!
pub fn sib_disp32(self: Self) !void {
// scale is actually ignored
// index = 4 means no index
// base = 5 means no base, if mod == 0.
try self.sib(0, 4, 5);
}
/// Construct a SIB byte with scale * index + base + disp8
/// r/m effective address: [base + scale * index + disp8]
///
/// Remember to add a REX prefix byte if index or base are extended!
pub fn sib_scaleIndexBaseDisp8(self: Self, scale: u2, index: u3, base: u3) !void {
try self.sib(scale, index, base);
}
/// Construct a SIB byte with base + disp8, no index
/// r/m effective address: [base + disp8]
///
/// Remember to add a REX prefix byte if index or base are extended!
pub fn sib_baseDisp8(self: Self, base: u3) !void {
// scale is ignored
// index = 4 means no index
try self.sib(0, 4, base);
}
/// Construct a SIB byte with scale * index + base + disp32
/// r/m effective address: [base + scale * index + disp32]
///
/// Remember to add a REX prefix byte if index or base are extended!
pub fn sib_scaleIndexBaseDisp32(self: Self, scale: u2, index: u3, base: u3) !void {
try self.sib(scale, index, base);
}
/// Construct a SIB byte with base + disp32, no index
/// r/m effective address: [base + disp32]
///
/// Remember to add a REX prefix byte if index or base are extended!
pub fn sib_baseDisp32(self: Self, base: u3) !void {
// scale is ignored
// index = 4 means no index
try self.sib(0, 4, base);
}
// -------------------------
// Trivial (no bit fiddling)
// -------------------------
/// Encode an 8 bit immediate
///
/// It is sign-extended to 64 bits by the cpu.
pub fn imm8(self: Self, imm: i8) !void {
try self.writer.writeByte(@bitCast(u8, imm));
}
/// Encode an 8 bit displacement
///
/// It is sign-extended to 64 bits by the cpu.
pub fn disp8(self: Self, disp: i8) !void {
try self.writer.writeByte(@bitCast(u8, disp));
}
/// Encode an 16 bit immediate
///
/// It is sign-extended to 64 bits by the cpu.
pub fn imm16(self: Self, imm: i16) !void {
try self.writer.writeIntLittle(i16, imm);
}
/// Encode an 32 bit immediate
///
/// It is sign-extended to 64 bits by the cpu.
pub fn imm32(self: Self, imm: i32) !void {
try self.writer.writeIntLittle(i32, imm);
}
/// Encode an 32 bit displacement
///
/// It is sign-extended to 64 bits by the cpu.
pub fn disp32(self: Self, disp: i32) !void {
try self.writer.writeIntLittle(i32, disp);
}
/// Encode an 64 bit immediate
///
/// It is sign-extended to 64 bits by the cpu.
pub fn imm64(self: Self, imm: u64) !void {
try self.writer.writeIntLittle(u64, imm);
}
};
}
pub const Rex = struct {
w: bool = false,
r: bool = false,
x: bool = false,
b: bool = false,
pub fn isSet(rex: Rex) bool {
return rex.w or rex.r or rex.x or rex.b;
}
};

View File

@ -0,0 +1,542 @@
const Encoding = @import("Encoding.zig");
const Mnemonic = Encoding.Mnemonic;
const OpEn = Encoding.OpEn;
const Op = Encoding.Op;
const Mode = Encoding.Mode;
const opcode_len = u2;
const modrm_ext = u3;
const Entry = struct { Mnemonic, OpEn, Op, Op, Op, Op, opcode_len, u8, u8, 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{
// General-purpose
.{ .adc, .zi, .al, .imm8, .none, .none, 1, 0x14, 0x00, 0x00, 0, .none },
.{ .adc, .zi, .ax, .imm16, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none },
.{ .adc, .zi, .eax, .imm32, .none, .none, 1, 0x15, 0x00, 0x00, 0, .none },
.{ .adc, .zi, .rax, .imm32, .none, .none, 1, 0x15, 0x00, 0x00, 0, .long },
.{ .adc, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 2, .none },
.{ .adc, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none },
.{ .adc, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 2, .none },
.{ .adc, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 2, .long },
.{ .adc, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none },
.{ .adc, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 2, .none },
.{ .adc, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 2, .long },
.{ .adc, .mr, .rm8, .r8, .none, .none, 1, 0x10, 0x00, 0x00, 0, .none },
.{ .adc, .mr, .rm16, .r16, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none },
.{ .adc, .mr, .rm32, .r32, .none, .none, 1, 0x11, 0x00, 0x00, 0, .none },
.{ .adc, .mr, .rm64, .r64, .none, .none, 1, 0x11, 0x00, 0x00, 0, .long },
.{ .adc, .rm, .r8, .rm8, .none, .none, 1, 0x12, 0x00, 0x00, 0, .none },
.{ .adc, .rm, .r16, .rm16, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none },
.{ .adc, .rm, .r32, .rm32, .none, .none, 1, 0x13, 0x00, 0x00, 0, .none },
.{ .adc, .rm, .r64, .rm64, .none, .none, 1, 0x13, 0x00, 0x00, 0, .long },
.{ .add, .zi, .al, .imm8, .none, .none, 1, 0x04, 0x00, 0x00, 0, .none },
.{ .add, .zi, .ax, .imm16, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none },
.{ .add, .zi, .eax, .imm32, .none, .none, 1, 0x05, 0x00, 0x00, 0, .none },
.{ .add, .zi, .rax, .imm32, .none, .none, 1, 0x05, 0x00, 0x00, 0, .long },
.{ .add, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 0, .none },
.{ .add, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none },
.{ .add, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 0, .none },
.{ .add, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 0, .long },
.{ .add, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none },
.{ .add, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 0, .none },
.{ .add, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 0, .long },
.{ .add, .mr, .rm8, .r8, .none, .none, 1, 0x00, 0x00, 0x00, 0, .none },
.{ .add, .mr, .rm16, .r16, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none },
.{ .add, .mr, .rm32, .r32, .none, .none, 1, 0x01, 0x00, 0x00, 0, .none },
.{ .add, .mr, .rm64, .r64, .none, .none, 1, 0x01, 0x00, 0x00, 0, .long },
.{ .add, .rm, .r8, .rm8, .none, .none, 1, 0x02, 0x00, 0x00, 0, .none },
.{ .add, .rm, .r16, .rm16, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none },
.{ .add, .rm, .r32, .rm32, .none, .none, 1, 0x03, 0x00, 0x00, 0, .none },
.{ .add, .rm, .r64, .rm64, .none, .none, 1, 0x03, 0x00, 0x00, 0, .long },
.{ .@"and", .zi, .al, .imm8, .none, .none, 1, 0x24, 0x00, 0x00, 0, .none },
.{ .@"and", .zi, .ax, .imm16, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none },
.{ .@"and", .zi, .eax, .imm32, .none, .none, 1, 0x25, 0x00, 0x00, 0, .none },
.{ .@"and", .zi, .rax, .imm32, .none, .none, 1, 0x25, 0x00, 0x00, 0, .long },
.{ .@"and", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 4, .none },
.{ .@"and", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none },
.{ .@"and", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 4, .none },
.{ .@"and", .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 4, .long },
.{ .@"and", .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none },
.{ .@"and", .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 4, .none },
.{ .@"and", .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 4, .long },
.{ .@"and", .mr, .rm8, .r8, .none, .none, 1, 0x20, 0x00, 0x00, 0, .none },
.{ .@"and", .mr, .rm16, .r16, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none },
.{ .@"and", .mr, .rm32, .r32, .none, .none, 1, 0x21, 0x00, 0x00, 0, .none },
.{ .@"and", .mr, .rm64, .r64, .none, .none, 1, 0x21, 0x00, 0x00, 0, .long },
.{ .@"and", .rm, .r8, .rm8, .none, .none, 1, 0x22, 0x00, 0x00, 0, .none },
.{ .@"and", .rm, .r16, .rm16, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none },
.{ .@"and", .rm, .r32, .rm32, .none, .none, 1, 0x23, 0x00, 0x00, 0, .none },
.{ .@"and", .rm, .r64, .rm64, .none, .none, 1, 0x23, 0x00, 0x00, 0, .long },
// This is M encoding according to Intel, but D makes more sense here.
.{ .call, .d, .rel32, .none, .none, .none, 1, 0xe8, 0x00, 0x00, 0, .none },
.{ .call, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 2, .none },
.{ .cbw, .np, .o16, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .none },
.{ .cwde, .np, .o32, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .none },
.{ .cdqe, .np, .o64, .none, .none, .none, 1, 0x98, 0x00, 0x00, 0, .long },
.{ .cwd, .np, .o16, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .none },
.{ .cdq, .np, .o32, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .none },
.{ .cqo, .np, .o64, .none, .none, .none, 1, 0x99, 0x00, 0x00, 0, .long },
.{ .cmova, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none },
.{ .cmova, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none },
.{ .cmova, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .long },
.{ .cmovae, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
.{ .cmovae, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
.{ .cmovae, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long },
.{ .cmovb, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
.{ .cmovb, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
.{ .cmovb, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long },
.{ .cmovbe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none },
.{ .cmovbe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none },
.{ .cmovbe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .long },
.{ .cmovc, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
.{ .cmovc, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
.{ .cmovc, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long },
.{ .cmove, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none },
.{ .cmove, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none },
.{ .cmove, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .long },
.{ .cmovg, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none },
.{ .cmovg, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none },
.{ .cmovg, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .long },
.{ .cmovge, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none },
.{ .cmovge, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none },
.{ .cmovge, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .long },
.{ .cmovl, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none },
.{ .cmovl, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none },
.{ .cmovl, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .long },
.{ .cmovle, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none },
.{ .cmovle, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none },
.{ .cmovle, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .long },
.{ .cmovna, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none },
.{ .cmovna, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .none },
.{ .cmovna, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x46, 0x00, 0, .long },
.{ .cmovnae, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
.{ .cmovnae, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .none },
.{ .cmovnae, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x42, 0x00, 0, .long },
.{ .cmovnb, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
.{ .cmovnb, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
.{ .cmovnb, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long },
.{ .cmovnbe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none },
.{ .cmovnbe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .none },
.{ .cmovnbe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x47, 0x00, 0, .long },
.{ .cmovnc, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
.{ .cmovnc, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .none },
.{ .cmovnc, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x43, 0x00, 0, .long },
.{ .cmovne, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none },
.{ .cmovne, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none },
.{ .cmovne, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .long },
.{ .cmovng, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none },
.{ .cmovng, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .none },
.{ .cmovng, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4e, 0x00, 0, .long },
.{ .cmovnge, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none },
.{ .cmovnge, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .none },
.{ .cmovnge, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4c, 0x00, 0, .long },
.{ .cmovnl, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none },
.{ .cmovnl, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .none },
.{ .cmovnl, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4d, 0x00, 0, .long },
.{ .cmovnle, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none },
.{ .cmovnle, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .none },
.{ .cmovnle, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4f, 0x00, 0, .long },
.{ .cmovno, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .none },
.{ .cmovno, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .none },
.{ .cmovno, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x41, 0x00, 0, .long },
.{ .cmovnp, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none },
.{ .cmovnp, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none },
.{ .cmovnp, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .long },
.{ .cmovns, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .none },
.{ .cmovns, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .none },
.{ .cmovns, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x49, 0x00, 0, .long },
.{ .cmovnz, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none },
.{ .cmovnz, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .none },
.{ .cmovnz, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x45, 0x00, 0, .long },
.{ .cmovo, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .none },
.{ .cmovo, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .none },
.{ .cmovo, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x40, 0x00, 0, .long },
.{ .cmovp, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none },
.{ .cmovp, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none },
.{ .cmovp, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .long },
.{ .cmovpe, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none },
.{ .cmovpe, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .none },
.{ .cmovpe, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4a, 0x00, 0, .long },
.{ .cmovpo, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none },
.{ .cmovpo, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .none },
.{ .cmovpo, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x4b, 0x00, 0, .long },
.{ .cmovs, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .none },
.{ .cmovs, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .none },
.{ .cmovs, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x48, 0x00, 0, .long },
.{ .cmovz, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none },
.{ .cmovz, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .none },
.{ .cmovz, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0x44, 0x00, 0, .long },
.{ .cmp, .zi, .al, .imm8, .none, .none, 1, 0x3c, 0x00, 0x00, 0, .none },
.{ .cmp, .zi, .ax, .imm16, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none },
.{ .cmp, .zi, .eax, .imm32, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .none },
.{ .cmp, .zi, .rax, .imm32, .none, .none, 1, 0x3d, 0x00, 0x00, 0, .long },
.{ .cmp, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 7, .none },
.{ .cmp, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none },
.{ .cmp, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 7, .none },
.{ .cmp, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 7, .long },
.{ .cmp, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none },
.{ .cmp, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 7, .none },
.{ .cmp, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 7, .long },
.{ .cmp, .mr, .rm8, .r8, .none, .none, 1, 0x38, 0x00, 0x00, 0, .none },
.{ .cmp, .mr, .rm16, .r16, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none },
.{ .cmp, .mr, .rm32, .r32, .none, .none, 1, 0x39, 0x00, 0x00, 0, .none },
.{ .cmp, .mr, .rm64, .r64, .none, .none, 1, 0x39, 0x00, 0x00, 0, .long },
.{ .cmp, .rm, .r8, .rm8, .none, .none, 1, 0x3a, 0x00, 0x00, 0, .none },
.{ .cmp, .rm, .r16, .rm16, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none },
.{ .cmp, .rm, .r32, .rm32, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .none },
.{ .cmp, .rm, .r64, .rm64, .none, .none, 1, 0x3b, 0x00, 0x00, 0, .long },
.{ .div, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 6, .none },
.{ .div, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none },
.{ .div, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .none },
.{ .div, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 6, .long },
.{ .fisttp, .m, .m16, .none, .none, .none, 1, 0xdf, 0x00, 0x00, 1, .fpu },
.{ .fisttp, .m, .m32, .none, .none, .none, 1, 0xdb, 0x00, 0x00, 1, .fpu },
.{ .fisttp, .m, .m64, .none, .none, .none, 1, 0xdd, 0x00, 0x00, 1, .fpu },
.{ .fld, .m, .m32, .none, .none, .none, 1, 0xd9, 0x00, 0x00, 0, .fpu },
.{ .fld, .m, .m64, .none, .none, .none, 1, 0xdd, 0x00, 0x00, 0, .fpu },
.{ .fld, .m, .m80, .none, .none, .none, 1, 0xdb, 0x00, 0x00, 5, .fpu },
.{ .idiv, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 7, .none },
.{ .idiv, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none },
.{ .idiv, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .none },
.{ .idiv, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 7, .long },
.{ .imul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 5, .none },
.{ .imul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none },
.{ .imul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .none },
.{ .imul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 5, .long },
.{ .imul, .rm, .r16, .rm16, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none },
.{ .imul, .rm, .r32, .rm32, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .none },
.{ .imul, .rm, .r64, .rm64, .none, .none, 2, 0x0f, 0xaf, 0x00, 0, .long },
.{ .imul, .rmi, .r16, .rm16, .imm8, .none, 1, 0x6b, 0x00, 0x00, 0, .none },
.{ .imul, .rmi, .r32, .rm32, .imm8, .none, 1, 0x6b, 0x00, 0x00, 0, .none },
.{ .imul, .rmi, .r64, .rm64, .imm8, .none, 1, 0x6b, 0x00, 0x00, 0, .long },
.{ .imul, .rmi, .r16, .rm16, .imm16, .none, 1, 0x69, 0x00, 0x00, 0, .none },
.{ .imul, .rmi, .r32, .rm32, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .none },
.{ .imul, .rmi, .r64, .rm64, .imm32, .none, 1, 0x69, 0x00, 0x00, 0, .long },
.{ .int3, .np, .none, .none, .none, .none, 1, 0xcc, 0x00, 0x00, 0, .none },
.{ .ja, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x87, 0x00, 0, .none },
.{ .jae, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none },
.{ .jb, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none },
.{ .jbe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x86, 0x00, 0, .none },
.{ .jc, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none },
.{ .jrcxz, .d, .rel32, .none, .none, .none, 1, 0xe3, 0x00, 0x00, 0, .none },
.{ .je, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x84, 0x00, 0, .none },
.{ .jg, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8f, 0x00, 0, .none },
.{ .jge, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8d, 0x00, 0, .none },
.{ .jl, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8c, 0x00, 0, .none },
.{ .jle, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8e, 0x00, 0, .none },
.{ .jna, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x86, 0x00, 0, .none },
.{ .jnae, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x82, 0x00, 0, .none },
.{ .jnb, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none },
.{ .jnbe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x87, 0x00, 0, .none },
.{ .jnc, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x83, 0x00, 0, .none },
.{ .jne, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x85, 0x00, 0, .none },
.{ .jng, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8e, 0x00, 0, .none },
.{ .jnge, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8c, 0x00, 0, .none },
.{ .jnl, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8d, 0x00, 0, .none },
.{ .jnle, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8f, 0x00, 0, .none },
.{ .jno, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x81, 0x00, 0, .none },
.{ .jnp, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8b, 0x00, 0, .none },
.{ .jns, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x89, 0x00, 0, .none },
.{ .jnz, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x85, 0x00, 0, .none },
.{ .jo, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x80, 0x00, 0, .none },
.{ .jp, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8a, 0x00, 0, .none },
.{ .jpe, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8a, 0x00, 0, .none },
.{ .jpo, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x8b, 0x00, 0, .none },
.{ .js, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x88, 0x00, 0, .none },
.{ .jz, .d, .rel32, .none, .none, .none, 2, 0x0f, 0x84, 0x00, 0, .none },
.{ .jmp, .d, .rel32, .none, .none, .none, 1, 0xe9, 0x00, 0x00, 0, .none },
.{ .jmp, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 4, .none },
.{ .lea, .rm, .r16, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .none },
.{ .lea, .rm, .r32, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .none },
.{ .lea, .rm, .r64, .m, .none, .none, 1, 0x8d, 0x00, 0x00, 0, .long },
.{ .mov, .mr, .rm8, .r8, .none, .none, 1, 0x88, 0x00, 0x00, 0, .none },
.{ .mov, .mr, .rm16, .r16, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none },
.{ .mov, .mr, .rm32, .r32, .none, .none, 1, 0x89, 0x00, 0x00, 0, .none },
.{ .mov, .mr, .rm64, .r64, .none, .none, 1, 0x89, 0x00, 0x00, 0, .long },
.{ .mov, .rm, .r8, .rm8, .none, .none, 1, 0x8a, 0x00, 0x00, 0, .none },
.{ .mov, .rm, .r16, .rm16, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none },
.{ .mov, .rm, .r32, .rm32, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .none },
.{ .mov, .rm, .r64, .rm64, .none, .none, 1, 0x8b, 0x00, 0x00, 0, .long },
.{ .mov, .mr, .rm16, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .none },
.{ .mov, .mr, .rm64, .sreg, .none, .none, 1, 0x8c, 0x00, 0x00, 0, .long },
.{ .mov, .rm, .sreg, .rm16, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .none },
.{ .mov, .rm, .sreg, .rm64, .none, .none, 1, 0x8e, 0x00, 0x00, 0, .long },
.{ .mov, .fd, .al, .moffs, .none, .none, 1, 0xa0, 0x00, 0x00, 0, .none },
.{ .mov, .fd, .ax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none },
.{ .mov, .fd, .eax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .none },
.{ .mov, .fd, .rax, .moffs, .none, .none, 1, 0xa1, 0x00, 0x00, 0, .long },
.{ .mov, .td, .moffs, .al, .none, .none, 1, 0xa2, 0x00, 0x00, 0, .none },
.{ .mov, .td, .moffs, .ax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none },
.{ .mov, .td, .moffs, .eax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .none },
.{ .mov, .td, .moffs, .rax, .none, .none, 1, 0xa3, 0x00, 0x00, 0, .long },
.{ .mov, .oi, .r8, .imm8, .none, .none, 1, 0xb0, 0x00, 0x00, 0, .none },
.{ .mov, .oi, .r16, .imm16, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none },
.{ .mov, .oi, .r32, .imm32, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .none },
.{ .mov, .oi, .r64, .imm64, .none, .none, 1, 0xb8, 0x00, 0x00, 0, .long },
.{ .mov, .mi, .rm8, .imm8, .none, .none, 1, 0xc6, 0x00, 0x00, 0, .none },
.{ .mov, .mi, .rm16, .imm16, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none },
.{ .mov, .mi, .rm32, .imm32, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .none },
.{ .mov, .mi, .rm64, .imm32, .none, .none, 1, 0xc7, 0x00, 0x00, 0, .long },
.{ .movsx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none },
.{ .movsx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .none },
.{ .movsx, .rm, .r64, .rm8, .none, .none, 2, 0x0f, 0xbe, 0x00, 0, .long },
.{ .movsx, .rm, .r32, .rm16, .none, .none, 2, 0x0f, 0xbf, 0x00, 0, .none },
.{ .movsx, .rm, .r64, .rm16, .none, .none, 2, 0x0f, 0xbf, 0x00, 0, .long },
// This instruction is discouraged.
.{ .movsxd, .rm, .r32, .rm32, .none, .none, 1, 0x63, 0x00, 0x00, 0, .none },
.{ .movsxd, .rm, .r64, .rm32, .none, .none, 1, 0x63, 0x00, 0x00, 0, .long },
.{ .movzx, .rm, .r16, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .none },
.{ .movzx, .rm, .r32, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .none },
.{ .movzx, .rm, .r64, .rm8, .none, .none, 2, 0x0f, 0xb6, 0x00, 0, .long },
.{ .movzx, .rm, .r32, .rm16, .none, .none, 2, 0x0f, 0xb7, 0x00, 0, .none },
.{ .movzx, .rm, .r64, .rm16, .none, .none, 2, 0x0f, 0xb7, 0x00, 0, .long },
.{ .mul, .m, .rm8, .none, .none, .none, 1, 0xf6, 0x00, 0x00, 4, .none },
.{ .mul, .m, .rm16, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .none },
.{ .mul, .m, .rm32, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .none },
.{ .mul, .m, .rm64, .none, .none, .none, 1, 0xf7, 0x00, 0x00, 4, .long },
.{ .nop, .np, .none, .none, .none, .none, 1, 0x90, 0x00, 0x00, 0, .none },
.{ .@"or", .zi, .al, .imm8, .none, .none, 1, 0x0c, 0x00, 0x00, 0, .none },
.{ .@"or", .zi, .ax, .imm16, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none },
.{ .@"or", .zi, .eax, .imm32, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .none },
.{ .@"or", .zi, .rax, .imm32, .none, .none, 1, 0x0d, 0x00, 0x00, 0, .long },
.{ .@"or", .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 1, .none },
.{ .@"or", .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none },
.{ .@"or", .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 1, .none },
.{ .@"or", .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 1, .long },
.{ .@"or", .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none },
.{ .@"or", .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 1, .none },
.{ .@"or", .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 1, .long },
.{ .@"or", .mr, .rm8, .r8, .none, .none, 1, 0x08, 0x00, 0x00, 0, .none },
.{ .@"or", .mr, .rm16, .r16, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none },
.{ .@"or", .mr, .rm32, .r32, .none, .none, 1, 0x09, 0x00, 0x00, 0, .none },
.{ .@"or", .mr, .rm64, .r64, .none, .none, 1, 0x09, 0x00, 0x00, 0, .long },
.{ .@"or", .rm, .r8, .rm8, .none, .none, 1, 0x0a, 0x00, 0x00, 0, .none },
.{ .@"or", .rm, .r16, .rm16, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none },
.{ .@"or", .rm, .r32, .rm32, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .none },
.{ .@"or", .rm, .r64, .rm64, .none, .none, 1, 0x0b, 0x00, 0x00, 0, .long },
.{ .pop, .o, .r16, .none, .none, .none, 1, 0x58, 0x00, 0x00, 0, .none },
.{ .pop, .o, .r64, .none, .none, .none, 1, 0x58, 0x00, 0x00, 0, .none },
.{ .pop, .m, .rm16, .none, .none, .none, 1, 0x8f, 0x00, 0x00, 0, .none },
.{ .pop, .m, .rm64, .none, .none, .none, 1, 0x8f, 0x00, 0x00, 0, .none },
.{ .push, .o, .r16, .none, .none, .none, 1, 0x50, 0x00, 0x00, 0, .none },
.{ .push, .o, .r64, .none, .none, .none, 1, 0x50, 0x00, 0x00, 0, .none },
.{ .push, .m, .rm16, .none, .none, .none, 1, 0xff, 0x00, 0x00, 6, .none },
.{ .push, .m, .rm64, .none, .none, .none, 1, 0xff, 0x00, 0x00, 6, .none },
.{ .push, .i, .imm8, .none, .none, .none, 1, 0x6a, 0x00, 0x00, 0, .none },
.{ .push, .i, .imm16, .none, .none, .none, 1, 0x68, 0x00, 0x00, 0, .none },
.{ .push, .i, .imm32, .none, .none, .none, 1, 0x68, 0x00, 0x00, 0, .none },
.{ .ret, .np, .none, .none, .none, .none, 1, 0xc3, 0x00, 0x00, 0, .none },
.{ .sal, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .none },
.{ .sal, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none },
.{ .sal, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none },
.{ .sal, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .long },
.{ .sal, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .none },
.{ .sal, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none },
.{ .sal, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none },
.{ .sal, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .long },
.{ .sal, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .none },
.{ .sal, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none },
.{ .sal, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none },
.{ .sal, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .long },
.{ .sar, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 7, .none },
.{ .sar, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .none },
.{ .sar, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .none },
.{ .sar, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 7, .long },
.{ .sar, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 7, .none },
.{ .sar, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .none },
.{ .sar, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .none },
.{ .sar, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 7, .long },
.{ .sar, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 7, .none },
.{ .sar, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none },
.{ .sar, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .none },
.{ .sar, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 7, .long },
.{ .sbb, .zi, .al, .imm8, .none, .none, 1, 0x1c, 0x00, 0x00, 0, .none },
.{ .sbb, .zi, .ax, .imm16, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none },
.{ .sbb, .zi, .eax, .imm32, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .none },
.{ .sbb, .zi, .rax, .imm32, .none, .none, 1, 0x1d, 0x00, 0x00, 0, .long },
.{ .sbb, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 3, .none },
.{ .sbb, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none },
.{ .sbb, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 3, .none },
.{ .sbb, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 3, .long },
.{ .sbb, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none },
.{ .sbb, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 3, .none },
.{ .sbb, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 3, .long },
.{ .sbb, .mr, .rm8, .r8, .none, .none, 1, 0x18, 0x00, 0x00, 0, .none },
.{ .sbb, .mr, .rm16, .r16, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none },
.{ .sbb, .mr, .rm32, .r32, .none, .none, 1, 0x19, 0x00, 0x00, 0, .none },
.{ .sbb, .mr, .rm64, .r64, .none, .none, 1, 0x19, 0x00, 0x00, 0, .long },
.{ .sbb, .rm, .r8, .rm8, .none, .none, 1, 0x1a, 0x00, 0x00, 0, .none },
.{ .sbb, .rm, .r16, .rm16, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none },
.{ .sbb, .rm, .r32, .rm32, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .none },
.{ .sbb, .rm, .r64, .rm64, .none, .none, 1, 0x1b, 0x00, 0x00, 0, .long },
.{ .seta, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none },
.{ .setae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none },
.{ .setb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none },
.{ .setbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .none },
.{ .setc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none },
.{ .sete, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .none },
.{ .setg, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .none },
.{ .setge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .none },
.{ .setl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .none },
.{ .setle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .none },
.{ .setna, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x96, 0x00, 0, .none },
.{ .setnae, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x92, 0x00, 0, .none },
.{ .setnb, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none },
.{ .setnbe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x97, 0x00, 0, .none },
.{ .setnc, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x93, 0x00, 0, .none },
.{ .setne, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .none },
.{ .setng, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9e, 0x00, 0, .none },
.{ .setnge, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9c, 0x00, 0, .none },
.{ .setnl, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9d, 0x00, 0, .none },
.{ .setnle, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9f, 0x00, 0, .none },
.{ .setno, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x91, 0x00, 0, .none },
.{ .setnp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .none },
.{ .setns, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x99, 0x00, 0, .none },
.{ .setnz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x95, 0x00, 0, .none },
.{ .seto, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x90, 0x00, 0, .none },
.{ .setp, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .none },
.{ .setpe, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9a, 0x00, 0, .none },
.{ .setpo, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x9b, 0x00, 0, .none },
.{ .sets, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x98, 0x00, 0, .none },
.{ .setz, .m, .rm8, .none, .none, .none, 2, 0x0f, 0x94, 0x00, 0, .none },
.{ .shl, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 4, .none },
.{ .shl, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none },
.{ .shl, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .none },
.{ .shl, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 4, .long },
.{ .shl, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 4, .none },
.{ .shl, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none },
.{ .shl, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .none },
.{ .shl, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 4, .long },
.{ .shl, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 4, .none },
.{ .shl, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none },
.{ .shl, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .none },
.{ .shl, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 4, .long },
.{ .shr, .m1, .rm8, .unity, .none, .none, 1, 0xd0, 0x00, 0x00, 5, .none },
.{ .shr, .m1, .rm16, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .none },
.{ .shr, .m1, .rm32, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .none },
.{ .shr, .m1, .rm64, .unity, .none, .none, 1, 0xd1, 0x00, 0x00, 5, .long },
.{ .shr, .mc, .rm8, .cl, .none, .none, 1, 0xd2, 0x00, 0x00, 5, .none },
.{ .shr, .mc, .rm16, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .none },
.{ .shr, .mc, .rm32, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .none },
.{ .shr, .mc, .rm64, .cl, .none, .none, 1, 0xd3, 0x00, 0x00, 5, .long },
.{ .shr, .mi, .rm8, .imm8, .none, .none, 1, 0xc0, 0x00, 0x00, 5, .none },
.{ .shr, .mi, .rm16, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none },
.{ .shr, .mi, .rm32, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .none },
.{ .shr, .mi, .rm64, .imm8, .none, .none, 1, 0xc1, 0x00, 0x00, 5, .long },
.{ .sub, .zi, .al, .imm8, .none, .none, 1, 0x2c, 0x00, 0x00, 0, .none },
.{ .sub, .zi, .ax, .imm16, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none },
.{ .sub, .zi, .eax, .imm32, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .none },
.{ .sub, .zi, .rax, .imm32, .none, .none, 1, 0x2d, 0x00, 0x00, 0, .long },
.{ .sub, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 5, .none },
.{ .sub, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none },
.{ .sub, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 5, .none },
.{ .sub, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 5, .long },
.{ .sub, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none },
.{ .sub, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 5, .none },
.{ .sub, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 5, .long },
.{ .sub, .mr, .rm8, .r8, .none, .none, 1, 0x28, 0x00, 0x00, 0, .none },
.{ .sub, .mr, .rm16, .r16, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none },
.{ .sub, .mr, .rm32, .r32, .none, .none, 1, 0x29, 0x00, 0x00, 0, .none },
.{ .sub, .mr, .rm64, .r64, .none, .none, 1, 0x29, 0x00, 0x00, 0, .long },
.{ .sub, .rm, .r8, .rm8, .none, .none, 1, 0x2a, 0x00, 0x00, 0, .none },
.{ .sub, .rm, .r16, .rm16, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none },
.{ .sub, .rm, .r32, .rm32, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .none },
.{ .sub, .rm, .r64, .rm64, .none, .none, 1, 0x2b, 0x00, 0x00, 0, .long },
.{ .syscall, .np, .none, .none, .none, .none, 2, 0x0f, 0x05, 0x00, 0, .none },
.{ .@"test", .zi, .al, .imm8, .none, .none, 1, 0xa8, 0x00, 0x00, 0, .none },
.{ .@"test", .zi, .ax, .imm16, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none },
.{ .@"test", .zi, .eax, .imm32, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .none },
.{ .@"test", .zi, .rax, .imm32, .none, .none, 1, 0xa9, 0x00, 0x00, 0, .long },
.{ .@"test", .mi, .rm8, .imm8, .none, .none, 1, 0xf6, 0x00, 0x00, 0, .none },
.{ .@"test", .mi, .rm16, .imm16, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none },
.{ .@"test", .mi, .rm32, .imm32, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .none },
.{ .@"test", .mi, .rm64, .imm32, .none, .none, 1, 0xf7, 0x00, 0x00, 0, .long },
.{ .@"test", .mr, .rm8, .r8, .none, .none, 1, 0x84, 0x00, 0x00, 0, .none },
.{ .@"test", .mr, .rm16, .r16, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none },
.{ .@"test", .mr, .rm32, .r32, .none, .none, 1, 0x85, 0x00, 0x00, 0, .none },
.{ .@"test", .mr, .rm64, .r64, .none, .none, 1, 0x85, 0x00, 0x00, 0, .long },
.{ .ud2, .np, .none, .none, .none, .none, 2, 0x0f, 0x0b, 0x00, 0, .none },
.{ .xor, .zi, .al, .imm8, .none, .none, 1, 0x34, 0x00, 0x00, 0, .none },
.{ .xor, .zi, .ax, .imm16, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none },
.{ .xor, .zi, .eax, .imm32, .none, .none, 1, 0x35, 0x00, 0x00, 0, .none },
.{ .xor, .zi, .rax, .imm32, .none, .none, 1, 0x35, 0x00, 0x00, 0, .long },
.{ .xor, .mi, .rm8, .imm8, .none, .none, 1, 0x80, 0x00, 0x00, 6, .none },
.{ .xor, .mi, .rm16, .imm16, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none },
.{ .xor, .mi, .rm32, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 6, .none },
.{ .xor, .mi, .rm64, .imm32, .none, .none, 1, 0x81, 0x00, 0x00, 6, .long },
.{ .xor, .mi, .rm16, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none },
.{ .xor, .mi, .rm32, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 6, .none },
.{ .xor, .mi, .rm64, .imm8, .none, .none, 1, 0x83, 0x00, 0x00, 6, .long },
.{ .xor, .mr, .rm8, .r8, .none, .none, 1, 0x30, 0x00, 0x00, 0, .none },
.{ .xor, .mr, .rm16, .r16, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none },
.{ .xor, .mr, .rm32, .r32, .none, .none, 1, 0x31, 0x00, 0x00, 0, .none },
.{ .xor, .mr, .rm64, .r64, .none, .none, 1, 0x31, 0x00, 0x00, 0, .long },
.{ .xor, .rm, .r8, .rm8, .none, .none, 1, 0x32, 0x00, 0x00, 0, .none },
.{ .xor, .rm, .r16, .rm16, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none },
.{ .xor, .rm, .r32, .rm32, .none, .none, 1, 0x33, 0x00, 0x00, 0, .none },
.{ .xor, .rm, .r64, .rm64, .none, .none, 1, 0x33, 0x00, 0x00, 0, .long },
// SSE
.{ .addss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x58, 0, .sse },
.{ .cmpss, .rmi, .xmm, .xmm_m32, .imm8, .none, 3, 0xf3, 0x0f, 0xc2, 0, .sse },
.{ .movss, .rm, .xmm, .xmm_m32, .none, .none, 3, 0xf3, 0x0f, 0x10, 0, .sse },
.{ .movss, .mr, .xmm_m32, .xmm, .none, .none, 3, 0xf3, 0x0f, 0x11, 0, .sse },
.{ .ucomiss, .rm, .xmm, .xmm_m32, .none, .none, 2, 0x0f, 0x2e, 0x00, 0, .sse },
// SSE2
.{ .addsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x58, 0, .sse2 },
.{ .cmpsd, .rmi, .xmm, .xmm_m64, .imm8, .none, 3, 0xf2, 0x0f, 0xc2, 0, .sse2 },
.{ .movq, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf3, 0x0f, 0x7e, 0, .sse2 },
.{ .movq, .mr, .xmm_m64, .xmm, .none, .none, 3, 0x66, 0x0f, 0xd6, 0, .sse2 },
.{ .movsd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0xf2, 0x0f, 0x10, 0, .sse2 },
.{ .movsd, .mr, .xmm_m64, .xmm, .none, .none, 3, 0xf2, 0x0f, 0x11, 0, .sse2 },
.{ .ucomisd, .rm, .xmm, .xmm_m64, .none, .none, 3, 0x66, 0x0f, 0x2e, 0, .sse2 },
};
// zig fmt: on