made rsi / rdi registers callee-saved on windows

made xmm registers caller-saved on linux / mac
added x64_backend_push_regs and x64_backend_pop_regs helpers
This commit is contained in:
Anthony Pesch 2017-11-23 23:15:16 -05:00
parent 8e6212cfea
commit 86205daf1b
5 changed files with 164 additions and 137 deletions

View File

@ -15,34 +15,6 @@ extern "C" {
* x64 register layout
*/
/* %rax %eax %ax %al <-- both: temporary
%rcx %ecx %cx %cl <-- both: argument
%rdx %edx %dx %dl <-- both: argument
%rbx %ebx %bx %bl <-- both: available (callee saved)
%rsp %esp %sp %spl <-- both: reserved
%rbp %ebp %bp %bpl <-- both: available (callee saved)
%rsi %esi %si %sil <-- msvc: available (callee saved), amd64: argument
%rdi %edi %di %dil <-- msvc: available (callee saved), amd64: argument
%r8 %r8d %r8w %r8b <-- both: argument
%r9 %r9d %r9w %r9b <-- both: argument
%r10 %r10d %r10w %r10b <-- both: available (not callee saved)
%r11 %r11d %r11w %r11b <-- both: available (not callee saved)
%r12 %r12d %r12w %r12b <-- both: available (callee saved)
%r13 %r13d %r13w %r13b <-- both: available (callee saved)
%r14 %r14d %r14w %r14b <-- both: available (callee saved)
%r15 %r15d %r15w %r15b <-- both: available (callee saved)
msvc calling convention uses rcx, rdx, r8, r9 for arguments
amd64 calling convention uses rdi, rsi, rdx, rcx, r8, r9 for arguments
both use the same xmm registers for floating point arguments
our largest function call uses only 3 arguments
msvc is left with rax, rsi, rdi, r10 and r11
amd64 is left with rax, r8, r9, r10 and r11
rax is used as a scratch register
r10, r11, xmm1 are used for constant not eliminated by const propagation
r14, r15 are reserved for the context and memory pointers */
/* clang-format off */
#if PLATFORM_WINDOWS
const int x64_arg0_idx = Xbyak::Operand::RCX;
@ -64,29 +36,64 @@ const Xbyak::Reg64 guestctx(Xbyak::Operand::R14);
const Xbyak::Reg64 guestmem(Xbyak::Operand::R15);
const struct jit_register x64_registers[] = {
{"rbx", JIT_REG_I64 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::rbx},
{"rbp", JIT_REG_I64 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::rbp},
{"rax", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::rax},
{"rcx", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::rcx},
{"rdx", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::rdx},
{"rbx", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::rbx},
{"rsp", JIT_RESERVED | JIT_CALLEE_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::rsp},
{"rbp", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::rbp},
#if PLATFORM_WINDOWS
{"rsi", JIT_REG_I64 | JIT_CALLER_SAVED, (const void *)&Xbyak::util::rsi},
{"rdi", JIT_REG_I64 | JIT_CALLER_SAVED, (const void *)&Xbyak::util::rdi},
{"rsi", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::rsi},
{"rdi", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::rdi},
{"r8", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::r8},
{"r9", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::r9},
#else
{"r8", JIT_REG_I64 | JIT_CALLER_SAVED, (const void *)&Xbyak::util::r8},
{"r9", JIT_REG_I64 | JIT_CALLER_SAVED, (const void *)&Xbyak::util::r9},
{"rsi", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::rsi},
{"rdi", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::rdi},
{"r8", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::r8},
{"r9", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::r9},
#endif
{"r10", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::r10},
{"r11", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::r11},
{"r12", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::r12},
{"r13", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::r13},
{"r14", JIT_RESERVED | JIT_CALLEE_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::r14},
{"r15", JIT_RESERVED | JIT_CALLEE_SAVE | JIT_REG_I64, (const void *)&Xbyak::util::r15},
#if PLATFORM_WINDOWS
{"xmm0", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm0},
{"xmm1", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm1},
{"xmm2", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm2},
{"xmm3", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm3},
{"xmm4", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm4},
{"xmm5", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm5},
{"xmm6", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm6},
{"xmm7", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm7},
{"xmm8", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm8},
{"xmm9", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm9},
{"xmm10", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm10},
{"xmm11", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm11},
{"xmm12", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm12},
{"xmm13", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm13},
{"xmm14", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm14},
{"xmm15", JIT_ALLOCATE | JIT_CALLEE_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm15},
#else
{"xmm0", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm0},
{"xmm1", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm1},
{"xmm2", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm2},
{"xmm3", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm3},
{"xmm4", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm4},
{"xmm5", JIT_RESERVED | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm5},
{"xmm6", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm6},
{"xmm7", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm7},
{"xmm8", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm8},
{"xmm9", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm9},
{"xmm10", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm10},
{"xmm11", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm11},
{"xmm12", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm12},
{"xmm13", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm13},
{"xmm14", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm14},
{"xmm15", JIT_ALLOCATE | JIT_CALLER_SAVE | JIT_REG_F64 | JIT_REG_V128, (const void *)&Xbyak::util::xmm15},
#endif
{"r10", JIT_REG_I64 | JIT_CALLER_SAVED, (const void *)&Xbyak::util::r10},
{"r11", JIT_REG_I64 | JIT_CALLER_SAVED, (const void *)&Xbyak::util::r11},
{"r12", JIT_REG_I64 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::r12},
{"r13", JIT_REG_I64 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::r13},
{"xmm6", JIT_REG_F64 | JIT_REG_V128 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::xmm6},
{"xmm7", JIT_REG_F64 | JIT_REG_V128 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::xmm7},
{"xmm8", JIT_REG_F64 | JIT_REG_V128 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::xmm8},
{"xmm9", JIT_REG_F64 | JIT_REG_V128 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::xmm9},
{"xmm10", JIT_REG_F64 | JIT_REG_V128 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::xmm10},
{"xmm11", JIT_REG_F64 | JIT_REG_V128 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::xmm11},
{"xmm12", JIT_REG_F64 | JIT_REG_V128 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::xmm12},
{"xmm13", JIT_REG_F64 | JIT_REG_V128 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::xmm13},
{"xmm14", JIT_REG_F64 | JIT_REG_V128 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::xmm14},
{"xmm15", JIT_REG_F64 | JIT_REG_V128 | JIT_CALLEE_SAVED, (const void *)&Xbyak::util::xmm15}
};
const int x64_num_registers = ARRAY_SIZE(x64_registers);
@ -127,6 +134,60 @@ Xbyak::Xmm x64_backend_xmm(struct x64_backend *backend,
return xmm;
}
int x64_backend_push_regs(struct x64_backend *backend, int mask) {
int size = 0;
auto &e = *backend->codegen;
for (int i = 0; i < x64_num_registers; i++) {
const struct jit_register *r = &x64_registers[i];
if ((r->flags & mask) != mask) {
continue;
}
if (r->flags & JIT_REG_I64) {
Xbyak::Reg reg = *(const Xbyak::Reg *)r->data;
CHECK(reg.isREG());
size += 8;
e.mov(e.qword[e.rsp - size], reg);
} else if (r->flags & (JIT_REG_F64 | JIT_REG_V128)) {
Xbyak::Xmm xmm = *(const Xbyak::Xmm *)r->data;
CHECK(xmm.isXMM());
size += 16;
e.movdqu(e.ptr[e.rsp - size], xmm);
}
}
return size;
}
void x64_backend_pop_regs(struct x64_backend *backend, int mask) {
int size = 0;
auto &e = *backend->codegen;
for (int i = 0; i < x64_num_registers; i++) {
const struct jit_register *r = &x64_registers[i];
if ((r->flags & mask) != mask) {
continue;
}
if ((r->flags & JIT_REG_I64)) {
Xbyak::Reg reg = *(const Xbyak::Reg *)r->data;
CHECK(reg.isREG());
size += 8;
e.mov(reg, e.qword[e.rsp - size]);
} else if (r->flags & (JIT_REG_F64 | JIT_REG_V128)) {
Xbyak::Xmm xmm = *(const Xbyak::Xmm *)r->data;
CHECK(xmm.isXMM());
size += 16;
e.movdqu(xmm, e.ptr[e.rsp - size]);
}
}
}
void x64_backend_load_mem(struct x64_backend *backend,
const struct ir_value *dst,
const Xbyak::RegExp &src_exp) {
@ -306,33 +367,19 @@ static void x64_backend_emit_thunks(struct x64_backend *backend) {
backend->load_thunk[i] = e.getCurr<void (*)()>();
/* save caller-saved registers and offset the stack an extra 8 bytes to
align it */
#if PLATFORM_WINDOWS
e.push(e.rsi);
e.push(e.rdi);
#else
e.push(e.r8);
e.push(e.r9);
#endif
e.push(e.r10);
e.push(e.r11);
e.sub(e.rsp, X64_STACK_SHADOW_SPACE + 8);
/* save caller-saved registers that our code uses and ensure stack is
16-byte aligned */
int save_mask = JIT_ALLOCATE | JIT_CALLER_SAVE;
int offset = x64_backend_push_regs(backend, save_mask);
offset = ALIGN_UP(offset + X64_STACK_SHADOW_SPACE + 8, 16) - 8;
e.sub(e.rsp, offset);
/* call the mmio handler */
e.call(e.rax);
/* restore caller-saved registers */
e.add(e.rsp, X64_STACK_SHADOW_SPACE + 8);
e.pop(e.r11);
e.pop(e.r10);
#if PLATFORM_WINDOWS
e.pop(e.rdi);
e.pop(e.rsi);
#else
e.pop(e.r9);
e.pop(e.r8);
#endif
e.add(e.rsp, offset);
x64_backend_pop_regs(backend, save_mask);
/* save mmio handler result */
e.mov(dst, e.rax);
@ -346,34 +393,19 @@ static void x64_backend_emit_thunks(struct x64_backend *backend) {
e.align(32);
backend->store_thunk = e.getCurr<void (*)()>();
/* save caller-saved registers and offset the stack an extra 8 bytes to
align it */
#if PLATFORM_WINDOWS
e.push(e.rsi);
e.push(e.rdi);
#else
e.push(e.r8);
e.push(e.r9);
#endif
e.push(e.r10);
e.push(e.r11);
e.sub(e.rsp, X64_STACK_SHADOW_SPACE + 8);
/* save caller-saved registers that our code uses and ensure stack is
16-byte aligned */
int save_mask = JIT_ALLOCATE | JIT_CALLER_SAVE;
int offset = x64_backend_push_regs(backend, save_mask);
offset = ALIGN_UP(offset + X64_STACK_SHADOW_SPACE + 8, 16) - 8;
e.sub(e.rsp, offset);
/* call the mmio handler */
e.call(e.rax);
/* restore caller-saved registers */
e.add(e.rsp, X64_STACK_SHADOW_SPACE + 8);
e.pop(e.r11);
e.pop(e.r10);
#if PLATFORM_WINDOWS
e.pop(e.rdi);
e.pop(e.rsi);
#else
e.pop(e.r9);
e.pop(e.r8);
#endif
e.add(e.rsp, offset);
x64_backend_pop_regs(backend, save_mask);
/* return to jit code */
e.ret();

View File

@ -75,6 +75,7 @@ void x64_dispatch_emit_thunks(struct x64_backend *backend) {
struct jit_guest *guest = backend->base.guest;
auto &e = *backend->codegen;
int stack_offset = 0;
/* emit dispatch thunks */
{
@ -152,18 +153,12 @@ void x64_dispatch_emit_thunks(struct x64_backend *backend) {
backend->dispatch_enter = e.getCurr<void (*)(int)>();
/* create stack frame */
e.push(e.rbx);
e.push(e.rbp);
#if PLATFORM_WINDOWS
e.push(e.rdi);
e.push(e.rsi);
#endif
e.push(e.r12);
e.push(e.r13);
e.push(e.r14);
e.push(e.r15);
e.sub(e.rsp, X64_STACK_SIZE + 8);
/* create stack frame and ensure stack is 16-byte aligned. note, the stack
is currently unaligned due to the 8-byte return address that was pushed
when this thunk was called */
stack_offset = x64_backend_push_regs(backend, JIT_CALLEE_SAVE);
stack_offset = ALIGN_UP(stack_offset + X64_STACK_SIZE + 8, 16) - 8;
e.sub(e.rsp, stack_offset);
/* assign fixed registers */
e.mov(guestctx, (uint64_t)guest->ctx);
@ -184,17 +179,9 @@ void x64_dispatch_emit_thunks(struct x64_backend *backend) {
backend->dispatch_exit = e.getCurr<void *>();
/* destroy stack frame */
e.add(e.rsp, X64_STACK_SIZE + 8);
e.pop(e.r15);
e.pop(e.r14);
e.pop(e.r13);
e.pop(e.r12);
#if PLATFORM_WINDOWS
e.pop(e.rsi);
e.pop(e.rdi);
#endif
e.pop(e.rbp);
e.pop(e.rbx);
e.add(e.rsp, stack_offset);
x64_backend_pop_regs(backend, JIT_CALLEE_SAVE);
e.ret();
}

View File

@ -51,7 +51,7 @@ struct x64_backend {
/*
* backend functionality used by emitters
*/
#define X64_THUNK_SIZE 1024
#define X64_THUNK_SIZE 8192
#define X64_STACK_SIZE 1024
#if PLATFORM_WINDOWS
@ -79,6 +79,8 @@ Xbyak::Reg x64_backend_reg(struct x64_backend *backend,
const struct ir_value *v);
Xbyak::Xmm x64_backend_xmm(struct x64_backend *backend,
const struct ir_value *v);
int x64_backend_push_regs(struct x64_backend *backend, int mask);
void x64_backend_pop_regs(struct x64_backend *backend, int mask);
void x64_backend_load_mem(struct x64_backend *backend,
const struct ir_value *dst,
const Xbyak::RegExp &src_exp);

View File

@ -27,33 +27,37 @@ struct jit_guest;
#endif
enum {
/* allocate to this register */
JIT_ALLOCATE = 0x1,
/* don't allocate to this register */
JIT_RESERVED = 0x2,
/* register is callee-saved */
JIT_CALLEE_SAVED = 0x1,
JIT_CALLEE_SAVE = 0x4,
/* register is caller-saved */
JIT_CALLER_SAVED = 0x2,
JIT_CALLER_SAVE = 0x8,
/* result must contain arg0. this signals the register allocator to insert a
copy from arg0 to result if it fails to reuse the same register for both.
this is required by several operations, namely binary arithmetic ops on
x64, which only take two operands */
JIT_REUSE_ARG0 = 0x4,
JIT_REUSE_ARG0 = 0x10,
/* argument is optional */
JIT_OPTIONAL = 0x8,
JIT_OPTIONAL = 0x20,
/* argument can be in a 64-bit or less int register */
JIT_REG_I64 = 0x10,
JIT_REG_I64 = 0x40,
/* argument can be in a 64-bit or less float register */
JIT_REG_F64 = 0x20,
JIT_REG_F64 = 0x80,
/* argument can be in a 128-bit or less vector register */
JIT_REG_V128 = 0x40,
JIT_REG_V128 = 0x100,
/* argument can be a 32-bit or less int immediate */
JIT_IMM_I32 = 0x80,
JIT_IMM_I32 = 0x200,
/* argument can be a 64-bit or less int immediate */
JIT_IMM_I64 = 0x100,
JIT_IMM_I64 = 0x400,
/* argument can be a 32-bit or less float immediate */
JIT_IMM_F32 = 0x200,
JIT_IMM_F32 = 0x800,
/* argument can be a 64-bit or less float immediate */
JIT_IMM_F64 = 0x400,
JIT_IMM_F64 = 0x1000,
/* argument can be a block reference */
JIT_IMM_BLK = 0x800,
JIT_IMM_BLK = 0x2000,
JIT_TYPE_MASK = JIT_REG_I64 | JIT_REG_F64 | JIT_REG_V128 | JIT_IMM_I32 |
JIT_IMM_I64 | JIT_IMM_F32 | JIT_IMM_F64 | JIT_IMM_BLK,
};

View File

@ -91,12 +91,14 @@ struct ra {
static int ra_reg_can_store(const struct jit_register *reg,
const struct ir_value *v) {
if (ir_is_int(v->type) && v->type <= VALUE_I64) {
return reg->flags & JIT_REG_I64;
} else if (ir_is_float(v->type) && v->type <= VALUE_F64) {
return reg->flags & JIT_REG_F64;
} else if (ir_is_vector(v->type) && v->type <= VALUE_V128) {
return reg->flags & JIT_REG_V128;
if (reg->flags & JIT_ALLOCATE) {
if (ir_is_int(v->type) && v->type <= VALUE_I64) {
return reg->flags & JIT_REG_I64;
} else if (ir_is_float(v->type) && v->type <= VALUE_F64) {
return reg->flags & JIT_REG_F64;
} else if (ir_is_vector(v->type) && v->type <= VALUE_V128) {
return reg->flags & JIT_REG_V128;
}
}
return 0;
}
@ -222,7 +224,7 @@ static void ra_validate(struct ra *ra, struct ir *ir, struct ir_block *block) {
for (int i = 0; i < ra->num_registers; i++) {
const struct jit_register *reg = &ra->registers[i];
if (reg->flags & JIT_CALLER_SAVED) {
if (reg->flags & JIT_CALLER_SAVE) {
active[i] = NULL;
}
}
@ -315,7 +317,7 @@ static void ra_spill_tmps(struct ra *ra, struct ir *ir,
/* only spill caller-saved regs */
struct ra_bin *bin = ra_get_bin(tmp->value->reg);
if (!(bin->reg->flags & JIT_CALLER_SAVED)) {
if (!(bin->reg->flags & JIT_CALLER_SAVE)) {
continue;
}