Skip to content

Commit d68e05e

Browse files
committed
Fix JIT non-deterministic execution on Arm64
The JIT compiler produced inconsistent results on Apple Silicon due to several issues in register liveness tracking and cache maintenance: 1. Store instructions (sb, sh, sw, csw) were not tracking rs2 liveness, causing the register allocator to potentially evict the data register before the store executed. 2. I-cache invalidation now occurs BEFORE re-enabling write protection to avoid a race window where executable code is not yet coherent. Previous ordering risked stale instruction execution. 3. Removed redundant ISB barriers since sys_icache_invalidate already includes the required DSB/ISB sequence on macOS/arm64. 4. Added final __builtin___clear_cache for the entire translated region to guarantee all instructions are visible before execution. The cache maintenance sequence now follows the correct ordering: write → sys_icache_invalidate → pthread_jit_write_protect_np(true) This ensures the I-cache is invalidated while the page is still writable, closing the race window that existed in the previous implementation.
1 parent 3d6acf5 commit d68e05e

1 file changed

Lines changed: 19 additions & 0 deletions

File tree

src/jit.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ static void emit_bytes(struct jit_state *state, void *data, uint32_t len)
313313
pthread_jit_write_protect_np(false);
314314
#endif
315315
memcpy(state->buf + state->offset, data, len);
316+
/* I-cache invalidation while page is still writable to avoid race.
317+
* sys_icache_invalidate includes required DSB/ISB barriers.
318+
*/
316319
sys_icache_invalidate(state->buf + state->offset, len);
317320
#if defined(__APPLE__) && defined(__aarch64__)
318321
pthread_jit_write_protect_np(true);
@@ -611,6 +614,10 @@ static void update_branch_imm(struct jit_state *state,
611614
pthread_jit_write_protect_np(false);
612615
#endif
613616
memcpy(state->buf + offset, &insn, sizeof(uint32_t));
617+
/* I-cache invalidation while page is still writable to avoid race.
618+
* sys_icache_invalidate includes required DSB/ISB barriers.
619+
*/
620+
sys_icache_invalidate(state->buf + offset, sizeof(uint32_t));
614621
#if defined(__APPLE__) && defined(__aarch64__)
615622
pthread_jit_write_protect_np(true);
616623
#endif
@@ -1675,6 +1682,7 @@ static inline void liveness_calc(block_t *block)
16751682
case rv_insn_sh:
16761683
case rv_insn_sw:
16771684
liveness[ir->rs1] = idx;
1685+
liveness[ir->rs2] = idx;
16781686
break;
16791687
case rv_insn_addi:
16801688
case rv_insn_slti:
@@ -1725,6 +1733,7 @@ static inline void liveness_calc(block_t *block)
17251733
break;
17261734
case rv_insn_csw:
17271735
liveness[ir->rs1] = idx;
1736+
liveness[ir->rs2] = idx;
17281737
break;
17291738
case rv_insn_cnop:
17301739
break;
@@ -2321,6 +2330,16 @@ void jit_translate(riscv_t *rv, block_t *block)
23212330
goto restart;
23222331
}
23232332
resolve_jumps(state);
2333+
#if defined(__APPLE__) && defined(__aarch64__)
2334+
/* Final cache flush for the entire translated region ensures all
2335+
* instructions are visible to execution. This covers any edge cases
2336+
* where per-instruction invalidation might have been insufficient.
2337+
* __builtin___clear_cache is portable and performs the full ARM64
2338+
* cache maintenance sequence (DC CVAU, DSB ISH, IC IVAU, DSB ISH, ISB).
2339+
*/
2340+
__builtin___clear_cache((char *) (state->buf + block->offset),
2341+
(char *) (state->buf + state->offset));
2342+
#endif
23242343
block->hot = true;
23252344
}
23262345

0 commit comments

Comments
 (0)