x64 指令编码(r/m,reg 与 reg,r/m)

x64 instruction encoding (r/m, reg vs reg, r/m)

编码 (ModRM:r/m, ModRM:reg) 与 (ModRM:reg, ModRM:r/m) 有什么区别?具体说像 CMPXCHG vs DIVPD 这样的指令。我以为寄存器和地址总是在第一个字节中编码,然后在需要时在第二个字节中编码 SIB 和位移?这是我的代码:

    static void WriteRegisterToMemory(ICollection<Byte> bytes, IRegisterToMemoryInstruction instruction, Byte rex)
    {
        IAddress address = instruction.Address;
        Byte register = instruction.Register;

        if (address.NeedsRex)
        {
            rex |= 0x40;
            if (address.RexB)
                rex |= 1;
            if (address.RexX)
                rex |= 1 << 1;
        }

        if (register > 7)
            rex |= 0x44;        // REX.R
        if (rex != 0)
            bytes.Add(rex);

        bytes.AddRange(instruction.Opcode);
        Byte modRM = (Byte)((register % 8) << 3);
        modRM |= address.GetModRMAddressByte();
        bytes.Add(modRM);
        address.WriteScaledIndexByteAndDisplacement(bytes);
    }

所以这两条指令的编码完全相同,只是操作码不同? (在 intel x64 手册的第 457 页添加)

Op/En Operand 1        Operand 2 
RM    ModRM:reg (r, w) ModRM:r/m (r) 
MR    ModRM:r/m (r, w) ModRM:reg (r)

确实没有什么区别w.r.t。编码,区别在于哪个是源,哪个是目的地。大多数指令都以 r/m 作为源,除了像 cmpxchgbtsxaddxchg 之类的东西是模棱两可的(它是对称的),ALU ops 有一个 r/m, r 形式和一个 r/m, imm 形式,显然是 mov 到记忆中。因此,在对这些指令进行编码时(即使两个操作数都是寄存器),也要小心 "which way around" 它们是,否则它们最终可能会交换操作数。不过也仅此而已,最终编码方式没有什么区别