[DYNAREC] Added dynablock in_used tracking and removed DYNAREC_PURGE (#4405)

With DYNAREC_CALLRET>1, a callee returns directly into the caller block. If that block is invalidated and its JIT memory reused before all pending returns are done, the returning thread jumps into random data. in_used is now tracked and the deferred free only recycles blocks with in_used==0.
DYNAREC_PURGE and tick tracking were removed, they were marked as legacy and now it's a good time to remove.
This commit is contained in:
Yang Liu authored and GitHub committed 2026-09-17 09:32:51 +02:00
1 parent b30ad56e97
commit 92527ded34
22 files changed
+140 -218

No files matched your search

-14
View File
@@ -823,20 +823,6 @@ Add an environment variable.
## Fragile or Legacy
### BOX64_DYNAREC_PURGE
Purge DynaBlocks that haven't been executed recently, based on tick age threshold (BOX64_DYNAREC_PURGE_AGE).
* 0: DynaRec will not purge old unused DynaBlocks. [Default]
* 1: DynaRec will purge old unused DynaBlocks based on tick age.
### BOX64_DYNAREC_PURGE_AGE
Tick age threshold for DynaBlock purging (requires BOX64_DYNAREC_PURGE=1).
* 4096: Default age threshold. [Default]
* XXXX: Custom age threshold (range: 10-65536).
### BOX64_DYNAREC_WAIT
Wait or not for the building of a DynaRec code block to be ready. Available in WowBox64.
-14
View File
@@ -246,20 +246,6 @@ Skip N first CPU cores (Is apply before MAXCPU) 在 WowBox64 中可用。
## 不稳定或遗留选项
### BOX64_DYNAREC_PURGE
根据年龄阈值(BOX64_DYNAREC_PURGE_AGE)清除最近未执行的代码块。
* 0: DynaRec 不会清除旧的未使用代码块。 [默认值]
* 1: DynaRec 将根据年龄阈值清除旧的未使用代码块。
### BOX64_DYNAREC_PURGE_AGE
代码块清除的年龄阈值(需要 BOX64_DYNAREC_PURGE=1)。
* 4096: 默认年龄阈值。 [默认值]
* XXXX: 自定义年龄阈值(范围:10-65536)。
### BOX64_DYNAREC_WAIT
是否等待代码块构建完成。 在 WowBox64 中可用。
-16
View File
@@ -338,22 +338,6 @@ Generate map file for Linux perf tool.
* 1 : DynaRec will generate perf map.
=item B<BOX64_DYNAREC_PURGE> =I<0|1>
Purge DynaBlocks that haven't been executed recently, based on tick age threshold (BOX64_DYNAREC_PURGE_AGE).
* 0 : DynaRec will not purge old unused DynaBlocks. [Default]
* 1 : DynaRec will purge old unused DynaBlocks based on tick age.
=item B<BOX64_DYNAREC_PURGE_AGE> =I<4096|XXXX>
Tick age threshold for DynaBlock purging (requires BOX64_DYNAREC_PURGE=1).
* 4096 : Default age threshold. [Default]
* XXXX : Custom age threshold (range: 10-65536).
=item B<BOX64_DYNAREC_SAFEFLAGS> =I<0|1|2>
Behaviour of flags emulation on CALL/RET opcodes and other edge cases. Available in WowBox64.
+1 -39
View File
@@ -736,44 +736,6 @@
}
]
},
{
"name": "BOX64_DYNAREC_PURGE",
"description": "Purge DynaBlocks that haven't been executed recently, based on tick age threshold (BOX64_DYNAREC_PURGE_AGE).",
"category": "Fragile or Legacy",
"wine": false,
"configurator": false,
"options": [
{
"key": "0",
"description": "DynaRec will not purge old unused DynaBlocks.",
"default": true
},
{
"key": "1",
"description": "DynaRec will purge old unused DynaBlocks based on tick age.",
"default": false
}
]
},
{
"name": "BOX64_DYNAREC_PURGE_AGE",
"description": "Tick age threshold for DynaBlock purging (requires BOX64_DYNAREC_PURGE=1).",
"category": "Fragile or Legacy",
"wine": false,
"configurator": false,
"options": [
{
"key": "4096",
"description": "Default age threshold.",
"default": true
},
{
"key": "XXXX",
"description": "Custom age threshold (range: 10-65536).",
"default": false
}
]
},
{
"name": "BOX64_DYNAREC_SAFEFLAGS",
"description": "Behaviour of flags emulation on CALL/RET opcodes and other edge cases.",
@@ -2489,4 +2451,4 @@
}
]
}
]
]
+1 -15
View File
@@ -252,20 +252,6 @@
"1": "DynaRec 将生成 perf 映射。"
}
},
"BOX64_DYNAREC_PURGE": {
"description": "根据年龄阈值(BOX64_DYNAREC_PURGE_AGE)清除最近未执行的代码块。",
"options": {
"0": "DynaRec 不会清除旧的未使用代码块。",
"1": "DynaRec 将根据年龄阈值清除旧的未使用代码块。"
}
},
"BOX64_DYNAREC_PURGE_AGE": {
"description": "代码块清除的年龄阈值(需要 BOX64_DYNAREC_PURGE=1)。",
"options": {
"4096": "默认年龄阈值。",
"XXXX": "自定义年龄阈值(范围:10-65536)。"
}
},
"BOX64_DYNAREC_SAFEFLAGS": {
"description": "在 CALL/RET 指令和其他边缘情况下 FLAGS 模拟的行为。",
"options": {
@@ -874,4 +860,4 @@
"1": "不为 32 位二进制文件切换 personality。"
}
}
}
}
+12 -85
View File
@@ -1697,68 +1697,6 @@ dynablock_t* FindDynablockFromNativeAddress(void* p)
return NULL;
}
// To measure speed at wich blocks are created, to avoid purge when lots of blocks are created in burst
static uint64_t start_time = 0;
static uint32_t start_tick = 0;
static uint64_t tick_freq = 0;
static uint64_t cur_speed = 0;
void UpdateBlockCreationSpeed()
{
if(!tick_freq) {
tick_freq = ReadTSCFrequency(NULL);
start_tick = my_context->tick;
start_time = ReadTSC(NULL);
}
uint64_t cur_time = ReadTSC(NULL);
// compute elapsed time is micro seconds
uint64_t elapsed_time = (cur_time-start_time)*1000000LL/tick_freq;
if(my_context->tick-start_tick>100 || elapsed_time>1000) {
// update speed each 100 blocks created or 1ms elapsed
cur_speed = (my_context->tick-start_tick)*1000000LL/elapsed_time;
start_tick = my_context->tick;
start_time = cur_time;
}
}
int PurgeDynarecMap(mmaplist_t* list, size_t size)
{
if(cur_speed>100) return 0; // 100 blocks / sec is a burst!
// check all blocks where tick is old enough and in_used==0, then delete them
// return 1 as soon as one block has been deleted, 0 else
// beware that tick=0 blocks means they were never executed and should not be touched
int ret = 0;
for(int i=0; i<list->size && !ret; ++i) {
blocklist_t* bl = list->chunks[i];
blockmark_t* p = bl->block;
blockmark_t* end = bl->block + bl->size - sizeof(blockmark_t);
while(p<end) {
blockmark_t *n = NEXT_BLOCK(p);
if(p->next.fill) {
dynablock_t* dynablock = *(dynablock_t**)p->mark;
int tick = native_lock_get_d(&dynablock->tick);
if(tick && dynablock->done && (my_context->tick > tick) && ((my_context->tick-tick)>=BOX64ENV(dynarec_purge_age))) {
int in_used = native_lock_get_d(&dynablock->in_used);
if(!in_used) {
// free the block, but unreference it first
//if(setJumpTableDefaultIfRef64(dynablock->x64_addr, dynablock->block))
{
dynarec_log(LOG_INFO/*LOG_DEBUG*/, " PurgeDynablock %p\n", dynablock);
if((n<end) && !n->next.fill )
n = NEXT_BLOCK(n); //because the block will be agglomerated
FreeDynablock(dynablock, 0, 1);
if((bl->maxfree>=size))
ret = 1;
} // fail to set default jump, so skipping
}
}
}
p = n;
}
}
return ret;
}
#ifdef TRACE_MEMSTAT
static uint64_t dynarec_allocated = 0;
#endif
@@ -1769,11 +1707,6 @@ uintptr_t AllocDynarecMap(uintptr_t x64_addr, size_t size, int is_new)
size = roundSize(size);
if(BOX64ENV(dynarec_purge)) {
__atomic_fetch_add(&my_context->tick, 1, __ATOMIC_RELAXED);
UpdateBlockCreationSpeed();
}
mmaplist_t* list = GetMmaplistByAddr(x64_addr);
if(!list)
list = mmaplist;
@@ -1783,26 +1716,20 @@ uintptr_t AllocDynarecMap(uintptr_t x64_addr, size_t size, int is_new)
list->dirty = 1;
// check if there is space in current open ones
uintptr_t sz = size + 2*sizeof(blockmark_t);
int recheck = 0;
do {
for(int i=0; i<list->size; ++i) {
if(list->chunks[i]->maxfree>=size) {
// looks free, try to alloc!
size_t rsize = 0;
void* sub = getFirstBlock(list->chunks[i]->block, size, &rsize, list->chunks[i]->first);
if(sub) {
void* ret = allocBlock(list->chunks[i]->block, sub, size, &list->chunks[i]->first);
if(rsize==list->chunks[i]->maxfree)
list->chunks[i]->maxfree = getMaxFreeBlock(list->chunks[i]->block, list->chunks[i]->size, list->chunks[i]->first);
//rb_set_64(list->chunks[i].tree, (uintptr_t)ret, (uintptr_t)ret+size, (uintptr_t)ret);
return (uintptr_t)ret;
}
for(int i=0; i<list->size; ++i) {
if(list->chunks[i]->maxfree>=size) {
// looks free, try to alloc!
size_t rsize = 0;
void* sub = getFirstBlock(list->chunks[i]->block, size, &rsize, list->chunks[i]->first);
if(sub) {
void* ret = allocBlock(list->chunks[i]->block, sub, size, &list->chunks[i]->first);
if(rsize==list->chunks[i]->maxfree)
list->chunks[i]->maxfree = getMaxFreeBlock(list->chunks[i]->block, list->chunks[i]->size, list->chunks[i]->first);
//rb_set_64(list->chunks[i].tree, (uintptr_t)ret, (uintptr_t)ret+size, (uintptr_t)ret);
return (uintptr_t)ret;
}
}
// check if we can remove blocks before allocating a new one
if(BOX64ENV(dynarec_purge))
recheck = recheck?0:PurgeDynarecMap(list, size); // don't do purge all the time, it's too time consuming
} while(recheck);
}
// need to add a new
if(list->size == list->cap) {
list->cap+=4;
+3 -9
View File
@@ -343,7 +343,7 @@ void jump_to_epilog(dynarec_arm_t* dyn, uintptr_t ip, int reg, int ninst)
TABLE64C(x2, const_epilog);
SMEND();
CHECK_DFNONE(0);
if(dyn->have_purge)
if (BOX64DRENV(dynarec_callret) >= 2)
doLeaveBlock(dyn, ninst, x4, x5, x6);
BR(x2);
}
@@ -419,7 +419,7 @@ void jump_to_next(dynarec_arm_t* dyn, uintptr_t ip, int reg, int ninst, int is32
if(reg!=x1) {
MOVx_REG(x1, xRIP);
}
if(dyn->have_purge && !dyn->insts[ninst].x64.has_callret)
if(BOX64DRENV(dynarec_callret) >= 2 && !dyn->insts[ninst].x64.has_callret)
doLeaveBlock(dyn, ninst, x4, x5, x6);
// #ifdef HAVE_TRACE
// The BLR form is only for gdb backtraces, but it breaks the
@@ -445,7 +445,7 @@ void ret_to_next(dynarec_arm_t* dyn, uintptr_t ip, int ninst, rex_t rex)
// pop the actual return address for ARM stack
LDPx_S7_postindex(xLR, x6, xSP, 16);
SUBx_REG(x6, x6, xRIP); // is it the right address?
if(dyn->have_purge)
if (BOX64DRENV(dynarec_callret) >= 2)
doLeaveBlock(dyn, ninst, x4, x5, x3);
CBNZx(x6, 2*4);
RET(xLR);
@@ -454,8 +454,6 @@ void ret_to_next(dynarec_arm_t* dyn, uintptr_t ip, int ninst, rex_t rex)
}
NOTEST(x2);
int dest = indirect_lookup(dyn, ninst, rex.is32bits, x2, x3);
if(dyn->have_purge && !BOX64DRENV(dynarec_callret))
doLeaveBlock(dyn, ninst, x4, x5, x6);
#ifdef HAVE_TRACE
BLR(dest);
#else
@@ -2872,10 +2870,6 @@ void doEnterBlock(dynarec_arm_t* dyn, int ninst, int s1, int s2, int s3)
STLXRw(s3, s2, s1);
CBNZw(s3, -3*4);
}
// set tick
LDRx_U12(s2, xEmu, offsetof(x64emu_t, context));
LDRw_U12(s2, s2, offsetof(box64context_t, tick));
STRw_U12(s2, s1, offsetof(dynablock_t, tick)-offsetof(dynablock_t, in_used));
MESSAGE(LOG_INFO, "-------- doEnter\n");
}
void doLeaveBlock(dynarec_arm_t* dyn, int ninst, int s1, int s2, int s3)
@@ -214,7 +214,6 @@ typedef struct dynarec_arm_s {
uint8_t use_mmx:1;
uint8_t use_xmm:1;
uint8_t use_ymm:1;
uint8_t have_purge:1; // set to 1 if block can be purged
uint8_t is_file_mapped:1; // if the memory is a mapped file (probably binary, not a memory)
void* gdbjit_block;
uint32_t need_x87check; // needs x87 precision control check if non-null, or 0 if not
-1
View File
@@ -46,7 +46,6 @@ void* create_updateflags()
// pass 0
updateflags_pass0(&helper, jmp_df);
// check if all flags are handled
helper.have_purge = 0; // force no purge
int ok = 1;
for(int i=d_none; i<d_unknown; ++i)
if(!jmp_df[i]) {
+37 -5
View File
@@ -118,13 +118,45 @@ static void DeferFreeDynablockMap(dynablock_t* db)
{
// enq for a deferred free so any threads still running in this block has a better chance to finish.
if (my_context->db_zombie_count == DB_ZOMBIE_SIZE) {
if(my_context->db_zombie[my_context->db_zombie_head])
FreeDynarecMap((uintptr_t)my_context->db_zombie[my_context->db_zombie_head]->actual_block);
// Queue full: free the oldest queued block that no running dynarec chain holds (in_used), compact the queue.
int idx = -1; // queue position of the entry to drop, 0 = oldest
int idx_in_used = 0; // in_used count of that entry
for (int i = 0; i < DB_ZOMBIE_SIZE; ++i) {
int slot = (my_context->db_zombie_head - DB_ZOMBIE_SIZE + i + DB_ZOMBIE_SIZE) % DB_ZOMBIE_SIZE;
dynablock_t* z = my_context->db_zombie[slot];
if (!z)
continue;
int cnt = native_lock_get_d(&z->in_used);
if (cnt <= 0) {
idx = i;
idx_in_used = cnt;
break;
}
if (idx < 0) {
idx = i; // remember the oldest held block, in case all of them are held
idx_in_used = cnt;
}
}
if (idx < 0)
idx = 0; // queue is full of empty slots: just drop the oldest one
// free the picked entry
int slot = (my_context->db_zombie_head - DB_ZOMBIE_SIZE + idx + DB_ZOMBIE_SIZE) % DB_ZOMBIE_SIZE;
if (idx_in_used > 0)
printf_log(LOG_INFO, "Warning, all %d queued dynablocks are still in use, freeing the oldest one anyway (in_used=%d)\n", DB_ZOMBIE_SIZE, idx_in_used);
if (my_context->db_zombie[slot])
FreeDynarecMap((uintptr_t)my_context->db_zombie[slot]->actual_block);
// shift all newer entries over the dropped one, then queue db at the tail
for (int i = idx; i < DB_ZOMBIE_SIZE - 1; ++i) {
int cur = (my_context->db_zombie_head - DB_ZOMBIE_SIZE + i + DB_ZOMBIE_SIZE) % DB_ZOMBIE_SIZE;
int nxt = (my_context->db_zombie_head - DB_ZOMBIE_SIZE + i + 1 + DB_ZOMBIE_SIZE) % DB_ZOMBIE_SIZE;
my_context->db_zombie[cur] = my_context->db_zombie[nxt];
}
my_context->db_zombie[(my_context->db_zombie_head - 1 + DB_ZOMBIE_SIZE) % DB_ZOMBIE_SIZE] = db;
} else {
my_context->db_zombie[my_context->db_zombie_head] = db;
my_context->db_zombie_head = (my_context->db_zombie_head + 1) % DB_ZOMBIE_SIZE;
my_context->db_zombie_count++;
}
my_context->db_zombie[my_context->db_zombie_head] = db;
my_context->db_zombie_head = (my_context->db_zombie_head + 1) % DB_ZOMBIE_SIZE;
}
void DeferFreeDynablockClearRange(void* addr, size_t sz)
@@ -264,7 +296,7 @@ int FreeRangeDynablock(dynablock_t* db, uintptr_t addr, uintptr_t size)
void dynablock_leave_runtime(dynablock_t* db)
{
if(!db) return;
if(!db->tick) return;
if(BOX64ENV(dynarec_callret) < 2) return;
__atomic_fetch_sub(&db->in_used, 1, __ATOMIC_ACQ_REL);
}
-1
View File
@@ -22,7 +22,6 @@ typedef struct dynablock_s {
void* block; // block-sizeof(void*) == self
void* actual_block; // the actual start of the block (so block-sizeof(void*))
uint32_t in_used;// will be 0 if not in_used, >0 if used be some code
uint32_t tick; // last "tick" when dynablock was run
void* x64_addr;
uintptr_t x64_size;
size_t native_size;
+6 -1
View File
@@ -302,8 +302,13 @@ void EmuRun(x64emu_t* emu, int use_dynarec, int no_alt)
if(!jblock) {
printf_log(LOG_NONE, "Warning, cannot find Secondary Entry Point %p in dynablock %p\n", (void*)R_RIP, block);
skip = 1;
} else
} else {
#if defined(ARM64) || defined(LA64) || defined(RV64)
if (jblock != block->block && BOX64ENV(dynarec_callret) >= 2)
__atomic_fetch_add(&block->in_used, 1, __ATOMIC_ACQ_REL);
#endif
native_prolog(emu, jblock);
}
}
if(emu->fork) {
int forktype = emu->fork;
+8 -4
View File
@@ -116,9 +116,6 @@ uintptr_t native_pass(dynarec_native_t* dyn, uintptr_t addr, int alternate, int
int dynarec_dirty = BOX64ENV(dynarec_dirty);
#if STEP == 0
memset(&dyn->insts[ninst], 0, sizeof(instruction_native_t));
#ifdef ARM64
dyn->have_purge = BOX64ENV(dynarec_purge);
#endif
#endif
fpu_reset(dyn);
ARCH_INIT();
@@ -173,7 +170,7 @@ uintptr_t native_pass(dynarec_native_t* dyn, uintptr_t addr, int alternate, int
ip = addr;
#ifdef ARM64
if(!ninst) {
if(dyn->have_purge)
if (BOX64DRENV(dynarec_callret) >= 2)
doEnterBlock(dyn, 0, x1, x2, x3);
if(dyn->always_test)
checkCRC(dyn, 0);
@@ -183,6 +180,8 @@ uintptr_t native_pass(dynarec_native_t* dyn, uintptr_t addr, int alternate, int
}
#elif defined(LA64)
if(!ninst) {
if (BOX64DRENV(dynarec_callret) >= 2)
doEnterBlock(dyn, 0, x1, x2, x3);
if(dyn->always_test)
checkCRC(dyn, 0);
if(dyn->insts[0].preload_xmmymm) {
@@ -190,6 +189,11 @@ uintptr_t native_pass(dynarec_native_t* dyn, uintptr_t addr, int alternate, int
}
ENDPREFIX;
}
#elif defined(RV64)
if(!ninst) {
if (BOX64DRENV(dynarec_callret) >= 2)
doEnterBlock(dyn, 0, x1, x2, x3);
}
#endif
fpu_propagate_stack(dyn, ninst);
if (reset_n!=-1) {
+32
View File
@@ -379,6 +379,8 @@ void jump_to_epilog(dynarec_la64_t* dyn, uintptr_t ip, int reg, int ninst)
TABLE64C(x2, const_epilog);
SMEND();
CHECK_DFNONE(0);
if (BOX64DRENV(dynarec_callret) >= 2)
doLeaveBlock(dyn, ninst, x4, x5, x6);
BR(x2);
}
@@ -399,9 +401,35 @@ void jump_to_epilog_fast(dynarec_la64_t* dyn, uintptr_t ip, int reg, int ninst)
TABLE64C(x2, const_epilog_fast);
SMEND();
CHECK_DFNONE(0);
if (BOX64DRENV(dynarec_callret) >= 2)
doLeaveBlock(dyn, ninst, x4, x5, x6);
BR(x2);
}
void doEnterBlock(dynarec_la64_t* dyn, int ninst, int s1, int s2, int s3)
{
MESSAGE(LOG_DUMP, "doEnter --------\n");
int delta = -(dyn->native_size + sizeof(void*));
PCADDU12I(s1, SPLIT20(delta));
ADDI_D(s1, s1, SPLIT12(delta));
LD_D(s1, s1, 0); // load the dynablock pointer
ADDI_D(s1, s1, offsetof(dynablock_t, in_used));
MOV32w(s3, 1);
AMADD_W(s2, s3, s1);
}
void doLeaveBlock(dynarec_la64_t* dyn, int ninst, int s1, int s2, int s3)
{
MESSAGE(LOG_DUMP, "doLeave --------\n");
int delta = -(dyn->native_size + sizeof(void*));
PCADDU12I(s1, SPLIT20(delta));
ADDI_D(s1, s1, SPLIT12(delta));
LD_D(s1, s1, 0); // load the dynablock pointer
ADDI_D(s1, s1, offsetof(dynablock_t, in_used));
MOV32w(s3, -1);
AMADD_W(s2, s3, s1);
}
static int indirect_lookup(dynarec_la64_t* dyn, int ninst, int is32bits, int s1, int s2)
{
@@ -461,6 +489,8 @@ void jump_to_next(dynarec_la64_t* dyn, uintptr_t ip, int reg, int ninst, int is3
}
CLEARIP();
SMEND();
if (BOX64DRENV(dynarec_callret) >= 2 && !dyn->insts[ninst].x64.has_callret)
doLeaveBlock(dyn, ninst, x4, x5, x6);
// The JIRL(xRA, ...) form is only for gdb backtraces, but it breaks the
// return-address-stack prediction on every block-to-block jump, which is
// not worth it. Uncomment temporarily when debugging with gdb.
@@ -484,6 +514,8 @@ void ret_to_next(dynarec_la64_t* dyn, uintptr_t ip, int ninst, rex_t rex)
LD_D(xRA, xSP, 0); // native addr
LD_D(x6, xSP, 8); // x86 addr
ADDI_D(xSP, xSP, 16); // pop
if (BOX64DRENV(dynarec_callret) >= 2)
doLeaveBlock(dyn, ninst, x4, x5, x7);
BNE(x6, xRIP, 2 * 4); // is it the right address?
BR(xRA);
// not the correct return address, regular jump, but purge the stack first, it's unsync now...
+4
View File
@@ -1799,6 +1799,8 @@ void emit_inline_leaf(dynarec_la64_t* dyn, int ninst);
#define jump_to_epilog_fast STEPNAME(jump_to_epilog_fast)
#define jump_to_next STEPNAME(jump_to_next)
#define ret_to_next STEPNAME(ret_to_next)
#define doEnterBlock STEPNAME(doEnterBlock)
#define doLeaveBlock STEPNAME(doLeaveBlock)
#define iret_to_next STEPNAME(iret_to_next)
#define call_c STEPNAME(call_c)
#define call_n STEPNAME(call_n)
@@ -1974,6 +1976,8 @@ uintptr_t geted16(dynarec_la64_t* dyn, uintptr_t addr, int ninst, uint8_t nextop
// generic x64 helper
void jump_to_epilog(dynarec_la64_t* dyn, uintptr_t ip, int reg, int ninst);
void jump_to_epilog_fast(dynarec_la64_t* dyn, uintptr_t ip, int reg, int ninst);
void doEnterBlock(dynarec_la64_t* dyn, int ninst, int s1, int s2, int s3);
void doLeaveBlock(dynarec_la64_t* dyn, int ninst, int s1, int s2, int s3);
void jump_to_next(dynarec_la64_t* dyn, uintptr_t ip, int reg, int ninst, int is32bits);
void ret_to_next(dynarec_la64_t* dyn, uintptr_t ip, int ninst, rex_t rex);
void iret_to_next(dynarec_la64_t* dyn, uintptr_t ip, int ninst, int is32bits, int is64bits);
@@ -197,7 +197,6 @@ typedef struct dynarec_ppc64le_s {
uint8_t use_mmx:1;
uint8_t use_xmm:1;
uint8_t use_ymm:1;
uint8_t have_purge:1; // set to 1 if block can be purged
uint8_t is_file_mapped:1; // if the memory is a mapped file (probably binary, not a memory)
void* gdbjit_block;
uint32_t need_x87check; // needs x87 precision control check if non-null, or 0 if not
+32
View File
@@ -381,6 +381,8 @@ void jump_to_epilog(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst)
TABLE64C(x2, const_epilog);
SMEND();
CHECK_DFNONE(0);
if (BOX64DRENV(dynarec_callret) >= 2)
doLeaveBlock(dyn, ninst, x4, x5, x6);
BR(x2);
}
@@ -401,8 +403,34 @@ void jump_to_epilog_fast(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst)
TABLE64C(x2, const_epilog_fast);
SMEND();
CHECK_DFNONE(0);
if (BOX64DRENV(dynarec_callret) >= 2)
doLeaveBlock(dyn, ninst, x4, x5, x6);
BR(x2);
}
void doEnterBlock(dynarec_rv64_t* dyn, int ninst, int s1, int s2, int s3)
{
MESSAGE(LOG_DUMP, "doEnter --------\n");
int delta = -(dyn->native_size + sizeof(void*));
AUIPC(s1, SPLIT20(delta));
ADDI(s1, s1, SPLIT12(delta));
LD(s1, s1, 0); // load the dynablock pointer
ADDI(s1, s1, offsetof(dynablock_t, in_used));
MOV32w(s3, 1);
AMOADD_W(s2, s3, s1, 0, 0);
}
void doLeaveBlock(dynarec_rv64_t* dyn, int ninst, int s1, int s2, int s3)
{
MESSAGE(LOG_DUMP, "doLeave --------\n");
int delta = -(dyn->native_size + sizeof(void*));
AUIPC(s1, SPLIT20(delta));
ADDI(s1, s1, SPLIT12(delta));
LD(s1, s1, 0); // load the dynablock pointer
ADDI(s1, s1, offsetof(dynablock_t, in_used));
MOV32w(s3, -1);
AMOADD_W(s2, s3, s1, 0, 0);
}
#ifdef JMPTABLE_SHIFT4
#error TODO!
#endif
@@ -485,6 +513,8 @@ void jump_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst, int is3
}
CLEARIP();
SMEND();
if (BOX64DRENV(dynarec_callret) >= 2 && !dyn->insts[ninst].x64.has_callret)
doLeaveBlock(dyn, ninst, x4, x5, x6);
// #ifdef HAVE_TRACE
// The JALR(xRA, ...) form is only for gdb backtraces, but it breaks the
// return-address-stack prediction on every block-to-block jump, which is
@@ -508,6 +538,8 @@ void ret_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int ninst, rex_t rex)
LD(xRA, xSP, 0); // native addr
LD(x6, xSP, 8); // x86 addr
ADDI(xSP, xSP, 16); // pop
if (BOX64DRENV(dynarec_callret) >= 2)
doLeaveBlock(dyn, ninst, x4, x5, x7);
BNE(x6, xRIP, 2 * 4); // is it the right address?
BR(xRA);
// not the correct return address, regular jump, but purge the stack first, it's unsync now...
+4
View File
@@ -1337,6 +1337,8 @@
#define jump_to_epilog_fast STEPNAME(jump_to_epilog_fast)
#define jump_to_next STEPNAME(jump_to_next)
#define ret_to_next STEPNAME(ret_to_next)
#define doEnterBlock STEPNAME(doEnterBlock)
#define doLeaveBlock STEPNAME(doLeaveBlock)
#define iret_to_next STEPNAME(iret_to_next)
#define call_c STEPNAME(call_c)
#define call_n STEPNAME(call_n)
@@ -1524,6 +1526,8 @@ uintptr_t geted16(dynarec_rv64_t* dyn, uintptr_t addr, int ninst, uint8_t nextop
// generic x64 helper
void jump_to_epilog(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst);
void jump_to_epilog_fast(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst);
void doEnterBlock(dynarec_rv64_t* dyn, int ninst, int s1, int s2, int s3);
void doLeaveBlock(dynarec_rv64_t* dyn, int ninst, int s1, int s2, int s3);
void jump_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int reg, int ninst, int is32bits);
void ret_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int ninst, rex_t rex);
void iret_to_next(dynarec_rv64_t* dyn, uintptr_t ip, int ninst, int is32bits, int is64bits);
-1
View File
@@ -97,7 +97,6 @@ typedef struct base_segment_s {
} base_segment_t;
typedef struct box64context_s {
uint32_t tick; // for dynarec age
path_collection_t box64_path; // PATH env. variable
path_collection_t box64_ld_lib; // LD_LIBRARY_PATH env. variable
-2
View File
@@ -80,8 +80,6 @@ extern char* ftrace_name;
INTEGER(BOX64_DYNAREC_WEAKBARRIER, dynarec_weakbarrier, 1, 0, 2, 1, 2) \
INTEGER(BOX64_DYNAREC_X87DOUBLE, dynarec_x87double, 0, 0, 2, 1, 2) \
BOOLEAN(BOX64_DYNAREC_INTERP_SIGNAL, dynarec_interp_signal, 0, 0, 0) \
BOOLEAN(BOX64_DYNAREC_PURGE, dynarec_purge, 0, 0, 0) \
INTEGER(BOX64_DYNAREC_PURGE_AGE, dynarec_purge_age, 4096, 10, 65536, 0, 0) \
BOOLEAN(BOX64_NODYNAREC_DELAY, nodynarec_delay, 0, 1, 0) \
STRING(BOX64_EMULATED_LIBS, emulated_libs, 0, 0) \
INTEGER(BOX64_DYNAREC_NOARCH, dynarec_noarch, 0, 0, 2, 1, 2) \
-5
View File
@@ -441,11 +441,6 @@ void SerializeMmaplist(mapping_t* mapping)
return;
if((!mapping->env || !mapping->env->is_dynacache_overridden) && box64env.dynacache!=1)
return;
// don't do serialize for program that needs purge=1
if(mapping->env && mapping->env->is_dynarec_purge_overridden && mapping->env->dynarec_purge)
return;
if((!mapping->env || !mapping->env->is_dynarec_purge_overridden) && box64env.dynarec_purge)
return;
// don't do serialize for program that needs dirty=1
if(mapping->env && mapping->env->is_dynarec_dirty_overridden && mapping->env->dynarec_dirty)
return;
-4
View File
@@ -729,7 +729,6 @@ BOX64_DYNACACHE=0 #something upset this app
BOX64_DYNAREC_STRONGMEM=1
BOX64_DYNAREC_SAFEFLAGS=2
BOX64_DYNAREC_BIGBLOCK=0
BOX64_DYNAREC_PURGE=1
[Link2EA.exe]
BOX64_DYNAREC_STRONGMEM=1
@@ -804,7 +803,6 @@ BOX64_DYNAREC_STRONGMEM=1
BOX64_DYNAREC_BIGBLOCK=0
BOX64_DYNAREC_SAFEFLAGS=2
BOX64_DYNAREC_CALLRET=0
BOX64_DYNAREC_PURGE=1
[GCU2.exe]
BOX64_DYNAREC_BIGBLOCK=3
@@ -1170,7 +1168,6 @@ BOX64_DYNAREC_BIGBLOCK=3
BOX64_DYNAREC_CALLRET=1
BOX64_DYNAREC_SAFEFLAGS=0
BOX64_DYNAREC_FASTROUND=0
BOX64_DYNAREC_PURGE=1
# Steam for LA64
[steam.exe]
@@ -1341,7 +1338,6 @@ BOX64_DYNAREC_FASTNAN=0
BOX64_DYNAREC_FASTROUND=0
BOX64_SSE_FLUSHTO0=1
BOX64_DYNAREC_STRONGMEM=1
BOX64_DYNAREC_PURGE=1
[UplayService.exe]
BOX64_DYNAREC_SAFEFLAGS=2