So, I've got a program which generates JIT x86 machine code and executes it directly and I want it to support x86-64/AMD64/x64 as well. The obvious differences are:
- New registers (
rax
,r8
...) and pointer width (pointers need to use 64bit regs) - Default C calling convention (arguments on stack vs. registers)
- Some new mnemonics (
pushq
to push 64bit)
Are there any differences in the binary instructions as well or should it be (roughly) sufficient to use pushq
and 64bit registers when appropriate and the code will just work?
Code example:
static inline void emit_call(uint32_t target) {
emit_byte(0xE8);
emit_dword(target - ((uint32_t)out + 4));
}
This would still work if I use uintptr_t
instead of uint32_t
I assume,
but loading an immediate into a 64bit register rax
is different from loading it into the lower 32bit alias:
static void emit_mov_x86reg_immediate(int x86reg, int imm) {
emit_byte(0xB8 | x86reg);
emit_dword(imm);
}
Are there any other differences?
The code I'm working on is accessible here, if you want to take a look at it.
There's actually no difference between the old 32bit push and the new 64bit push, that's one of the few instructions that are implicitly 64bit.
Relative branches and calls still use 32bit offsets.
Some actual differences are:
sil
anddil
- a REX prefix with none of the bits set can still matter!)40+rd
,48+rd
) forinc
anddec
. Soinc
anddec
must theFF /1
encoding.rip
-relative addressingmov
with a direct 64bit addressmovsxd
) shares opcode witharpl
les
andlds
don't exist, reused as VEX prefixes (in 32bit mode, onlyles
andlds
with illegal operands are VEX prefixes, which is why the encoding of VEX prefixes is a bit odd)bound
,into
,pushad
,pop es
and friends)82 /?
aliases of80 /?
are no longer validlahf
andsahf
don't exist on some old x64 processors (not that you'd use them anyway..)