movsd 从内存到 c x86-64 jit 中的 xmm0
movsd from memory to xmm0 in c x86-64 jit
我正在尝试编写一个小型的 x86-64 JIT,但有几个地方有点难以理解。
我正在尝试 JIT 一个简单的函数,该函数将 float 的值分配给 xmm0 寄存器,然后 returns 它,但我不确定我应该如何对 movsd 的参数进行编码打电话。
如有任何帮助,我们将不胜感激。
/* main.c */
#include <stdio.h>
#include <sys/mman.h>
#define xmm(n) (n)
typedef double(*fn)();
fn jit(){
char* memory = mmap(NULL,
4096,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0);
int i=0;
float myfloat = 3.1f;
memory[i++] = 0x48; /* REX.W */
memory[i++] = 0xf2; /*******************/
memory[i++] = 0x0f; /* MOVSD xmm0, m64 */
memory[i++] = 0x10; /*******************/
memory[i++] = 0x47 | xmm(0) << 3; /* Not 100% sure this is correct */
memory[i++] = 0; /* what goes here to load myfloat into xmm0? */
memory[i++] = 0xc3; /* RET */
return (fn) memory;
}
int main(){
fn f = jit();
printf("result: %f\n", (*f)());
return 0;
}
SSE 指令通常不支持立即数,除了一些使用 one-byte 立即数来控制其操作的罕见指令。因此你需要:
- 存储
myfloat
到附近的内存区域
- 生成一个引用该区域的内存操作数
这两个步骤都很简单。对于第一步,我只是使用 memory
的开头,然后让代码立即开始。请注意,在这种情况下,您需要确保 return 指向函数开头的指针,而不是 memory
的开头。其他解决方案也是可能的。只需确保 myfloat
存储在代码中的 ±2 GiB 内。
要生成操作数,请重新查阅英特尔手册。您想要的寻址 mode 是一个 32 位 RIP-relative 操作数。这是用 mod = 0,r/m = 5 生成的。位移是一个有符号的 32 位数字,它在指令末尾添加到 RIP 的值(这是 +4来自必须考虑位移的长度)。
因此我们有类似的东西:
memory[i++] = 0xf2; /*******************/
memory[i++] = 0x0f; /* MOVSD xmm0, m64 */
memory[i++] = 0x10; /*******************/
memory[i++] = 0005 | xmm(0) << 3; /* mod = 0, r/m = 5: [rip + disp32] */
*(int *)(memory + i) = memory + i + 4 - addr_of_myfloat;
i += 4;
memory[i++] = 0xc3; /* RET */
注意这里不需要REX前缀。
我正在尝试编写一个小型的 x86-64 JIT,但有几个地方有点难以理解。
我正在尝试 JIT 一个简单的函数,该函数将 float 的值分配给 xmm0 寄存器,然后 returns 它,但我不确定我应该如何对 movsd 的参数进行编码打电话。
如有任何帮助,我们将不胜感激。
/* main.c */
#include <stdio.h>
#include <sys/mman.h>
#define xmm(n) (n)
typedef double(*fn)();
fn jit(){
char* memory = mmap(NULL,
4096,
PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_PRIVATE|MAP_ANONYMOUS,
-1, 0);
int i=0;
float myfloat = 3.1f;
memory[i++] = 0x48; /* REX.W */
memory[i++] = 0xf2; /*******************/
memory[i++] = 0x0f; /* MOVSD xmm0, m64 */
memory[i++] = 0x10; /*******************/
memory[i++] = 0x47 | xmm(0) << 3; /* Not 100% sure this is correct */
memory[i++] = 0; /* what goes here to load myfloat into xmm0? */
memory[i++] = 0xc3; /* RET */
return (fn) memory;
}
int main(){
fn f = jit();
printf("result: %f\n", (*f)());
return 0;
}
SSE 指令通常不支持立即数,除了一些使用 one-byte 立即数来控制其操作的罕见指令。因此你需要:
- 存储
myfloat
到附近的内存区域 - 生成一个引用该区域的内存操作数
这两个步骤都很简单。对于第一步,我只是使用 memory
的开头,然后让代码立即开始。请注意,在这种情况下,您需要确保 return 指向函数开头的指针,而不是 memory
的开头。其他解决方案也是可能的。只需确保 myfloat
存储在代码中的 ±2 GiB 内。
要生成操作数,请重新查阅英特尔手册。您想要的寻址 mode 是一个 32 位 RIP-relative 操作数。这是用 mod = 0,r/m = 5 生成的。位移是一个有符号的 32 位数字,它在指令末尾添加到 RIP 的值(这是 +4来自必须考虑位移的长度)。
因此我们有类似的东西:
memory[i++] = 0xf2; /*******************/
memory[i++] = 0x0f; /* MOVSD xmm0, m64 */
memory[i++] = 0x10; /*******************/
memory[i++] = 0005 | xmm(0) << 3; /* mod = 0, r/m = 5: [rip + disp32] */
*(int *)(memory + i) = memory + i + 4 - addr_of_myfloat;
i += 4;
memory[i++] = 0xc3; /* RET */
注意这里不需要REX前缀。