From 5e35e48ec76dbea1479eced18f9f929302ca4698 Mon Sep 17 00:00:00 2001 From: SarahW Date: Sun, 5 May 2019 14:49:10 +0100 Subject: [PATCH] XMM registers are not preserved across function calls, and should be flushed & discarded by the recompiler prior to calling external functions. Fixes graphics issues in some games (eg World Cup 98 title/menus). --- src/codegen_backend.h | 18 ++++++- src/codegen_backend_arm.c | 34 ++++++------- src/codegen_backend_arm64.c | 40 ++++++++-------- src/codegen_backend_x86-64.c | 34 ++++++++----- src/codegen_backend_x86-64_defs.h | 2 +- src/codegen_backend_x86.c | 25 +++++----- src/codegen_reg.c | 80 +++++++++++++++++-------------- 7 files changed, 135 insertions(+), 98 deletions(-) diff --git a/src/codegen_backend.h b/src/codegen_backend.h index 7a5c5c0..45668df 100644 --- a/src/codegen_backend.h +++ b/src/codegen_backend.h @@ -1,3 +1,6 @@ +#ifndef _CODEGEN_BACKEND_H_ +#define _CODEGEN_BACKEND_H_ + //#ifdef __amd64__ //#include "codegen_x86-64.h" #if defined __amd64__ @@ -25,5 +28,16 @@ typedef int (*uOpFn)(codeblock_t *codeblock, struct uop_t *uop); extern const uOpFn uop_handlers[]; -extern int codegen_host_reg_list[CODEGEN_HOST_REGS]; -extern int codegen_host_fp_reg_list[CODEGEN_HOST_FP_REGS]; +/*Register will not be preserved across function calls*/ +#define HOST_REG_FLAG_VOLATILE (1 << 0) + +typedef struct host_reg_def_t +{ + int reg; + int flags; +} host_reg_def_t; + +extern host_reg_def_t codegen_host_reg_list[CODEGEN_HOST_REGS]; +extern host_reg_def_t codegen_host_fp_reg_list[CODEGEN_HOST_FP_REGS]; + +#endif diff --git a/src/codegen_backend_arm.c b/src/codegen_backend_arm.c index 0c1c2c8..aa9345c 100644 --- a/src/codegen_backend_arm.c +++ b/src/codegen_backend_arm.c @@ -38,27 +38,27 @@ void *codegen_fp_round; void *codegen_gpf_rout; void *codegen_exit_rout; -int codegen_host_reg_list[CODEGEN_HOST_REGS] = +host_reg_def_t codegen_host_reg_list[CODEGEN_HOST_REGS] = { - REG_R4, - REG_R5, - REG_R6, - REG_R7, - REG_R8, - REG_R9, - REG_R11, + {REG_R4, 0}, + {REG_R5, 0}, + {REG_R6, 0}, + {REG_R7, 0}, + {REG_R8, 0}, + {REG_R9, 0}, + {REG_R11, 0} }; -int codegen_host_fp_reg_list[CODEGEN_HOST_FP_REGS] = +host_reg_def_t codegen_host_fp_reg_list[CODEGEN_HOST_FP_REGS] = { - REG_D8, - REG_D9, - REG_D10, - REG_D11, - REG_D12, - REG_D13, - REG_D14, - REG_D15 + {REG_D8, 0}, + {REG_D9, 0}, + {REG_D10, 0}, + {REG_D11, 0}, + {REG_D12, 0}, + {REG_D13, 0}, + {REG_D14, 0}, + {REG_D15, 0} }; static void build_load_routine(codeblock_t *block, int size, int is_float) diff --git a/src/codegen_backend_arm64.c b/src/codegen_backend_arm64.c index eb50aa6..1885e61 100644 --- a/src/codegen_backend_arm64.c +++ b/src/codegen_backend_arm64.c @@ -39,30 +39,30 @@ void *codegen_fp_round_quad; void *codegen_gpf_rout; void *codegen_exit_rout; -int codegen_host_reg_list[CODEGEN_HOST_REGS] = +host_reg_def_t codegen_host_reg_list[CODEGEN_HOST_REGS] = { - REG_X19, - REG_X20, - REG_X21, - REG_X22, - REG_X23, - REG_X24, - REG_X25, - REG_X26, - REG_X27, - REG_X28 + {REG_X19, 0}, + {REG_X20, 0}, + {REG_X21, 0}, + {REG_X22, 0}, + {REG_X23, 0}, + {REG_X24, 0}, + {REG_X25, 0}, + {REG_X26, 0}, + {REG_X27, 0}, + {REG_X28, 0} }; -int codegen_host_fp_reg_list[CODEGEN_HOST_FP_REGS] = +host_reg_def_t codegen_host_fp_reg_list[CODEGEN_HOST_FP_REGS] = { - REG_V8, - REG_V9, - REG_V10, - REG_V11, - REG_V12, - REG_V13, - REG_V14, - REG_V15 + {REG_V8, 0}, + {REG_V9, 0}, + {REG_V10, 0}, + {REG_V11, 0}, + {REG_V12, 0}, + {REG_V13, 0}, + {REG_V14, 0}, + {REG_V15, 0} }; static void build_load_routine(codeblock_t *block, int size, int is_float) diff --git a/src/codegen_backend_x86-64.c b/src/codegen_backend_x86-64.c index 9d92f2f..03cde31 100644 --- a/src/codegen_backend_x86-64.c +++ b/src/codegen_backend_x86-64.c @@ -35,22 +35,32 @@ void *codegen_mem_store_double; void *codegen_gpf_rout; void *codegen_exit_rout; -int codegen_host_reg_list[CODEGEN_HOST_REGS] = +host_reg_def_t codegen_host_reg_list[CODEGEN_HOST_REGS] = { - REG_EAX, - REG_EBX, - REG_EDX + /*Note: while EAX and EDX are normally volatile registers under x86 + calling conventions, the recompiler will explicitly save and restore + them across funcion calls*/ + {REG_EAX, 0}, + {REG_EBX, 0}, + {REG_EDX, 0} }; -int codegen_host_fp_reg_list[CODEGEN_HOST_FP_REGS] = +host_reg_def_t codegen_host_fp_reg_list[CODEGEN_HOST_FP_REGS] = { - REG_XMM0, - REG_XMM1, - REG_XMM2, - REG_XMM3, - REG_XMM4, - REG_XMM5, - REG_XMM6 +#if WIN64 + /*Windows x86-64 calling convention preserves XMM6-XMM15*/ + {REG_XMM6, 0}, + {REG_XMM7, 0}, +#else + /*System V AMD64 calling convention does not preserve any XMM registers*/ + {REG_XMM6, HOST_REG_FLAG_VOLATILE}, + {REG_XMM7, HOST_REG_FLAG_VOLATILE}, +#endif + {REG_XMM1, HOST_REG_FLAG_VOLATILE}, + {REG_XMM2, HOST_REG_FLAG_VOLATILE}, + {REG_XMM3, HOST_REG_FLAG_VOLATILE}, + {REG_XMM4, HOST_REG_FLAG_VOLATILE}, + {REG_XMM5, HOST_REG_FLAG_VOLATILE} }; static void build_load_routine(codeblock_t *block, int size, int is_float) diff --git a/src/codegen_backend_x86-64_defs.h b/src/codegen_backend_x86-64_defs.h index a368651..8856887 100644 --- a/src/codegen_backend_x86-64_defs.h +++ b/src/codegen_backend_x86-64_defs.h @@ -44,7 +44,7 @@ #define REG_XMM6 6 #define REG_XMM7 7 -#define REG_XMM_TEMP REG_XMM7 +#define REG_XMM_TEMP REG_XMM0 #define CODEGEN_HOST_REGS 3 #define CODEGEN_HOST_FP_REGS 7 diff --git a/src/codegen_backend_x86.c b/src/codegen_backend_x86.c index 1700eb0..2fd0565 100644 --- a/src/codegen_backend_x86.c +++ b/src/codegen_backend_x86.c @@ -36,21 +36,24 @@ void *codegen_mem_store_double; void *codegen_gpf_rout; void *codegen_exit_rout; -int codegen_host_reg_list[CODEGEN_HOST_REGS] = +host_reg_def_t codegen_host_reg_list[CODEGEN_HOST_REGS] = { - REG_EAX, - REG_EBX, - REG_EDX + /*Note: while EAX and EDX are normally volatile registers under x86 + calling conventions, the recompiler will explicitly save and restore + them across funcion calls*/ + {REG_EAX, 0}, + {REG_EBX, 0}, + {REG_EDX, 0} }; -int codegen_host_fp_reg_list[CODEGEN_HOST_FP_REGS] = +host_reg_def_t codegen_host_fp_reg_list[CODEGEN_HOST_FP_REGS] = { - REG_XMM0, - REG_XMM1, - REG_XMM2, - REG_XMM3, - REG_XMM4, - REG_XMM5 + {REG_XMM0, HOST_REG_FLAG_VOLATILE}, + {REG_XMM1, HOST_REG_FLAG_VOLATILE}, + {REG_XMM2, HOST_REG_FLAG_VOLATILE}, + {REG_XMM3, HOST_REG_FLAG_VOLATILE}, + {REG_XMM4, HOST_REG_FLAG_VOLATILE}, + {REG_XMM5, HOST_REG_FLAG_VOLATILE} }; static void build_load_routine(codeblock_t *block, int size, int is_float) diff --git a/src/codegen_reg.c b/src/codegen_reg.c index 2601b5a..e28068b 100644 --- a/src/codegen_reg.c +++ b/src/codegen_reg.c @@ -22,7 +22,7 @@ typedef struct host_reg_set_t { ir_reg_t *regs; uint8_t *dirty; - int *reg_list; + host_reg_def_t *reg_list; uint16_t locked; int nr_regs; } host_reg_set_t; @@ -274,72 +274,72 @@ static void codegen_reg_load(host_reg_set_t *reg_set, codeblock_t *block, int c, if (ireg_data[IREG_GET_REG(ir_reg.reg)].type != REG_INTEGER) fatal("codegen_reg_load - REG_WORD !REG_INTEGER\n"); if ((uintptr_t)ireg_data[IREG_GET_REG(ir_reg.reg)].p < 256) - codegen_direct_read_16_stack(block, reg_set->reg_list[c], (int)ireg_data[IREG_GET_REG(ir_reg.reg)].p); + codegen_direct_read_16_stack(block, reg_set->reg_list[c].reg, (int)ireg_data[IREG_GET_REG(ir_reg.reg)].p); else - codegen_direct_read_16(block, reg_set->reg_list[c], ireg_data[IREG_GET_REG(ir_reg.reg)].p); + codegen_direct_read_16(block, reg_set->reg_list[c].reg, ireg_data[IREG_GET_REG(ir_reg.reg)].p); break; case REG_DWORD: if (ireg_data[IREG_GET_REG(ir_reg.reg)].type != REG_INTEGER) fatal("codegen_reg_load - REG_DWORD !REG_INTEGER\n"); if ((uintptr_t)ireg_data[IREG_GET_REG(ir_reg.reg)].p < 256) - codegen_direct_read_32_stack(block, reg_set->reg_list[c], (int)ireg_data[IREG_GET_REG(ir_reg.reg)].p); + codegen_direct_read_32_stack(block, reg_set->reg_list[c].reg, (int)ireg_data[IREG_GET_REG(ir_reg.reg)].p); else - codegen_direct_read_32(block, reg_set->reg_list[c], ireg_data[IREG_GET_REG(ir_reg.reg)].p); + codegen_direct_read_32(block, reg_set->reg_list[c].reg, ireg_data[IREG_GET_REG(ir_reg.reg)].p); break; case REG_QWORD: if (ireg_data[IREG_GET_REG(ir_reg.reg)].type != REG_FP) fatal("codegen_reg_load - REG_QWORD !REG_FP\n"); if ((uintptr_t)ireg_data[IREG_GET_REG(ir_reg.reg)].p < 256) - codegen_direct_read_64_stack(block, reg_set->reg_list[c], (int)ireg_data[IREG_GET_REG(ir_reg.reg)].p); + codegen_direct_read_64_stack(block, reg_set->reg_list[c].reg, (int)ireg_data[IREG_GET_REG(ir_reg.reg)].p); else - codegen_direct_read_64(block, reg_set->reg_list[c], ireg_data[IREG_GET_REG(ir_reg.reg)].p); + codegen_direct_read_64(block, reg_set->reg_list[c].reg, ireg_data[IREG_GET_REG(ir_reg.reg)].p); break; case REG_POINTER: if (ireg_data[IREG_GET_REG(ir_reg.reg)].type != REG_INTEGER) fatal("codegen_reg_load - REG_POINTER !REG_INTEGER\n"); if ((uintptr_t)ireg_data[IREG_GET_REG(ir_reg.reg)].p < 256) - codegen_direct_read_pointer_stack(block, reg_set->reg_list[c], (int)ireg_data[IREG_GET_REG(ir_reg.reg)].p); + codegen_direct_read_pointer_stack(block, reg_set->reg_list[c].reg, (int)ireg_data[IREG_GET_REG(ir_reg.reg)].p); else - codegen_direct_read_pointer(block, reg_set->reg_list[c], ireg_data[IREG_GET_REG(ir_reg.reg)].p); + codegen_direct_read_pointer(block, reg_set->reg_list[c].reg, ireg_data[IREG_GET_REG(ir_reg.reg)].p); break; case REG_DOUBLE: if (ireg_data[IREG_GET_REG(ir_reg.reg)].type != REG_FP) fatal("codegen_reg_load - REG_DOUBLE !REG_FP\n"); if ((uintptr_t)ireg_data[IREG_GET_REG(ir_reg.reg)].p < 256) - codegen_direct_read_double_stack(block, reg_set->reg_list[c], (int)ireg_data[IREG_GET_REG(ir_reg.reg)].p); + codegen_direct_read_double_stack(block, reg_set->reg_list[c].reg, (int)ireg_data[IREG_GET_REG(ir_reg.reg)].p); else - codegen_direct_read_double(block, reg_set->reg_list[c], ireg_data[IREG_GET_REG(ir_reg.reg)].p); + codegen_direct_read_double(block, reg_set->reg_list[c].reg, ireg_data[IREG_GET_REG(ir_reg.reg)].p); break; case REG_FPU_ST_BYTE: if (ireg_data[IREG_GET_REG(ir_reg.reg)].type != REG_INTEGER) fatal("codegen_reg_load - REG_FPU_ST_BYTE !REG_INTEGER\n"); if (block->flags & CODEBLOCK_STATIC_TOP) - codegen_direct_read_8(block, reg_set->reg_list[c], &cpu_state.tag[ir_reg.reg & 7]); + codegen_direct_read_8(block, reg_set->reg_list[c].reg, &cpu_state.tag[ir_reg.reg & 7]); else - codegen_direct_read_st_8(block, reg_set->reg_list[c], &cpu_state.tag[0], ir_reg.reg & 7); + codegen_direct_read_st_8(block, reg_set->reg_list[c].reg, &cpu_state.tag[0], ir_reg.reg & 7); break; case REG_FPU_ST_QWORD: if (ireg_data[IREG_GET_REG(ir_reg.reg)].type != REG_FP) fatal("codegen_reg_load - REG_FPU_ST_QWORD !REG_FP\n"); if (block->flags & CODEBLOCK_STATIC_TOP) - codegen_direct_read_64(block, reg_set->reg_list[c], &cpu_state.MM[ir_reg.reg & 7]); + codegen_direct_read_64(block, reg_set->reg_list[c].reg, &cpu_state.MM[ir_reg.reg & 7]); else - codegen_direct_read_st_64(block, reg_set->reg_list[c], &cpu_state.MM[0], ir_reg.reg & 7); + codegen_direct_read_st_64(block, reg_set->reg_list[c].reg, &cpu_state.MM[0], ir_reg.reg & 7); break; case REG_FPU_ST_DOUBLE: if (ireg_data[IREG_GET_REG(ir_reg.reg)].type != REG_FP) fatal("codegen_reg_load - REG_FPU_ST_DOUBLE !REG_FP\n"); if (block->flags & CODEBLOCK_STATIC_TOP) - codegen_direct_read_double(block, reg_set->reg_list[c], &cpu_state.ST[ir_reg.reg & 7]); + codegen_direct_read_double(block, reg_set->reg_list[c].reg, &cpu_state.ST[ir_reg.reg & 7]); else - codegen_direct_read_st_double(block, reg_set->reg_list[c], &cpu_state.ST[0], ir_reg.reg & 7); + codegen_direct_read_st_double(block, reg_set->reg_list[c].reg, &cpu_state.ST[0], ir_reg.reg & 7); break; default: @@ -367,7 +367,7 @@ static void codegen_reg_writeback(host_reg_set_t *reg_set, codeblock_t *block, i fatal("codegen_reg_writeback - REG_BYTE !REG_INTEGER\n"); if ((uintptr_t)p < 256) fatal("codegen_reg_writeback - REG_BYTE %p\n", p); - codegen_direct_write_8(block, p, reg_set->reg_list[c]); + codegen_direct_write_8(block, p, reg_set->reg_list[c].reg); break; case REG_WORD: @@ -375,25 +375,25 @@ static void codegen_reg_writeback(host_reg_set_t *reg_set, codeblock_t *block, i fatal("codegen_reg_writeback - REG_WORD !REG_INTEGER\n"); if ((uintptr_t)p < 256) fatal("codegen_reg_writeback - REG_WORD %p\n", p); - codegen_direct_write_16(block, p, reg_set->reg_list[c]); + codegen_direct_write_16(block, p, reg_set->reg_list[c].reg); break; case REG_DWORD: if (ireg_data[ir_reg].type != REG_INTEGER) fatal("codegen_reg_writeback - REG_DWORD !REG_INTEGER\n"); if ((uintptr_t)p < 256) - codegen_direct_write_32_stack(block, (int)p, reg_set->reg_list[c]); + codegen_direct_write_32_stack(block, (int)p, reg_set->reg_list[c].reg); else - codegen_direct_write_32(block, p, reg_set->reg_list[c]); + codegen_direct_write_32(block, p, reg_set->reg_list[c].reg); break; case REG_QWORD: if (ireg_data[ir_reg].type != REG_FP) fatal("codegen_reg_writeback - REG_QWORD !REG_FP\n"); if ((uintptr_t)p < 256) - codegen_direct_write_64_stack(block, (int)p, reg_set->reg_list[c]); + codegen_direct_write_64_stack(block, (int)p, reg_set->reg_list[c].reg); else - codegen_direct_write_64(block, p, reg_set->reg_list[c]); + codegen_direct_write_64(block, p, reg_set->reg_list[c].reg); break; case REG_POINTER: @@ -401,43 +401,43 @@ static void codegen_reg_writeback(host_reg_set_t *reg_set, codeblock_t *block, i fatal("codegen_reg_writeback - REG_POINTER !REG_INTEGER\n"); if ((uintptr_t)p < 256) fatal("codegen_reg_writeback - REG_POINTER %p\n", p); - codegen_direct_write_ptr(block, p, reg_set->reg_list[c]); + codegen_direct_write_ptr(block, p, reg_set->reg_list[c].reg); break; case REG_DOUBLE: if (ireg_data[ir_reg].type != REG_FP) fatal("codegen_reg_writeback - REG_DOUBLE !REG_FP\n"); if ((uintptr_t)p < 256) - codegen_direct_write_double_stack(block, (int)p, reg_set->reg_list[c]); + codegen_direct_write_double_stack(block, (int)p, reg_set->reg_list[c].reg); else - codegen_direct_write_double(block, p, reg_set->reg_list[c]); + codegen_direct_write_double(block, p, reg_set->reg_list[c].reg); break; case REG_FPU_ST_BYTE: if (ireg_data[ir_reg].type != REG_INTEGER) fatal("codegen_reg_writeback - REG_FPU_ST_BYTE !REG_INTEGER\n"); if (block->flags & CODEBLOCK_STATIC_TOP) - codegen_direct_write_8(block, &cpu_state.tag[reg_set->regs[c].reg & 7], reg_set->reg_list[c]); + codegen_direct_write_8(block, &cpu_state.tag[reg_set->regs[c].reg & 7], reg_set->reg_list[c].reg); else - codegen_direct_write_st_8(block, &cpu_state.tag[0], reg_set->regs[c].reg & 7, reg_set->reg_list[c]); + codegen_direct_write_st_8(block, &cpu_state.tag[0], reg_set->regs[c].reg & 7, reg_set->reg_list[c].reg); break; case REG_FPU_ST_QWORD: if (ireg_data[ir_reg].type != REG_FP) fatal("codegen_reg_writeback - REG_FPU_ST_QWORD !REG_FP\n"); if (block->flags & CODEBLOCK_STATIC_TOP) - codegen_direct_write_64(block, &cpu_state.MM[reg_set->regs[c].reg & 7], reg_set->reg_list[c]); + codegen_direct_write_64(block, &cpu_state.MM[reg_set->regs[c].reg & 7], reg_set->reg_list[c].reg); else - codegen_direct_write_st_64(block, &cpu_state.MM[0], reg_set->regs[c].reg & 7, reg_set->reg_list[c]); + codegen_direct_write_st_64(block, &cpu_state.MM[0], reg_set->regs[c].reg & 7, reg_set->reg_list[c].reg); break; case REG_FPU_ST_DOUBLE: if (ireg_data[ir_reg].type != REG_FP) fatal("codegen_reg_writeback - REG_FPU_ST_DOUBLE !REG_FP\n"); if (block->flags & CODEBLOCK_STATIC_TOP) - codegen_direct_write_double(block, &cpu_state.ST[reg_set->regs[c].reg & 7], reg_set->reg_list[c]); + codegen_direct_write_double(block, &cpu_state.ST[reg_set->regs[c].reg & 7], reg_set->reg_list[c].reg); else - codegen_direct_write_st_double(block, &cpu_state.ST[0], reg_set->regs[c].reg & 7, reg_set->reg_list[c]); + codegen_direct_write_st_double(block, &cpu_state.ST[0], reg_set->regs[c].reg & 7, reg_set->reg_list[c].reg); break; default: @@ -595,7 +595,7 @@ ir_host_reg_t codegen_reg_alloc_read_reg(codeblock_t *block, ir_reg_t ir_reg, in if (host_reg_idx) *host_reg_idx = c; // pclog(" codegen_reg_alloc_read_reg: %i.%i %i %02x.%i %i\n", ir_reg.reg, ir_reg.version, codegen_host_reg_list[c], reg_set->regs[c].reg,reg_set->regs[c].version, c); - return reg_set->reg_list[c] | IREG_GET_SIZE(ir_reg.reg); + return reg_set->reg_list[c].reg | IREG_GET_SIZE(ir_reg.reg); } ir_host_reg_t codegen_reg_alloc_write_reg(codeblock_t *block, ir_reg_t ir_reg) @@ -623,7 +623,7 @@ ir_host_reg_t codegen_reg_alloc_write_reg(codeblock_t *block, ir_reg_t ir_reg) reg_set->regs[c].version = ir_reg.version; reg_set->dirty[c] = 1; // pclog(" codegen_reg_alloc_write_reg: partial %i.%i %i\n", ir_reg.reg, ir_reg.version, codegen_host_reg_list[c]); - return reg_set->reg_list[c] | IREG_GET_SIZE(ir_reg.reg); + return reg_set->reg_list[c].reg | IREG_GET_SIZE(ir_reg.reg); } /*Search for previous version in host register*/ @@ -668,7 +668,7 @@ ir_host_reg_t codegen_reg_alloc_write_reg(codeblock_t *block, ir_reg_t ir_reg) reg_set->regs[c].version = ir_reg.version; reg_set->dirty[c] = 1; // pclog(" codegen_reg_alloc_write_reg: %i.%i %i\n", ir_reg.reg, ir_reg.version, codegen_host_reg_list[c]); - return reg_set->reg_list[c] | IREG_GET_SIZE(ir_reg.reg); + return reg_set->reg_list[c].reg | IREG_GET_SIZE(ir_reg.reg); } void codegen_reg_flush(ir_data_t *ir, codeblock_t *block) @@ -683,6 +683,11 @@ void codegen_reg_flush(ir_data_t *ir, codeblock_t *block) { codegen_reg_writeback(reg_set, block, c, 0); } + if (reg_set->reg_list[c].flags & HOST_REG_FLAG_VOLATILE) + { + reg_set->regs[c] = invalid_ir_reg; + reg_set->dirty[c] = 0; + } } reg_set = &host_fp_reg_set; @@ -692,6 +697,11 @@ void codegen_reg_flush(ir_data_t *ir, codeblock_t *block) { codegen_reg_writeback(reg_set, block, c, 0); } + if (reg_set->reg_list[c].flags & HOST_REG_FLAG_VOLATILE) + { + reg_set->regs[c] = invalid_ir_reg; + reg_set->dirty[c] = 0; + } } }