Skip to content

Commit 97314af

Browse files
committed
Fix T2C memory visibility race
The T2C compilation thread could set hot2 flag before compiled code was fully visible to the main thread, causing execution of invalid function pointers. This resulted in incorrect calculation results in the pi test. Problem: - volatile alone doesn't provide memory ordering guarantees - CPU could reorder operations, making hot2=true visible before block->func - Led to executing stale or partially written function pointers Solution: - Use __atomic_store_n with __ATOMIC_RELEASE when setting hot2 - Use __atomic_load_n with __ATOMIC_ACQUIRE when reading hot2 - Release-Acquire synchronization ensures proper ordering This creates a happens-before relationship: when the main thread observes hot2=true via acquire, it is guaranteed to see all writes (including block->func) that happened before the release.
1 parent 21484fb commit 97314af

File tree

3 files changed

+7
-7
lines changed

3 files changed

+7
-7
lines changed

src/emulate.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ static block_t *block_alloc(riscv_t *rv)
278278
#if RV32_HAS(JIT)
279279
block->translatable = true;
280280
block->hot = false;
281-
block->hot2 = false;
281+
__atomic_store_n(&block->hot2, false, __ATOMIC_RELAXED);
282282
block->has_loops = false;
283283
block->n_invoke = 0;
284284
INIT_LIST_HEAD(&block->list);
@@ -1121,8 +1121,8 @@ void rv_step(void *arg)
11211121
#if RV32_HAS(JIT)
11221122
#if RV32_HAS(T2C)
11231123
/* executed through the tier-2 JIT compiler */
1124-
if (block->hot2) {
1125-
/* hot2 is volatile, ensuring visibility across threads */
1124+
if (__atomic_load_n(&block->hot2, __ATOMIC_ACQUIRE)) {
1125+
/* Acquire ensures we see all writes from before hot2 was set */
11261126
((exec_t2c_func_t) block->func)(rv);
11271127
prev = NULL;
11281128
continue;

src/riscv_private.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ typedef struct block {
8585

8686
rv_insn_t *ir_head, *ir_tail; /**< the first and last ir for this block */
8787
#if RV32_HAS(JIT)
88-
bool hot; /**< Determine the block is potential hotspot or not */
89-
volatile bool hot2; /**< Determine the block is strong hotspot or not */
88+
bool hot; /**< Determine the block is potential hotspot or not */
89+
bool hot2; /**< Determine the block is strong hotspot or not */
9090
bool
9191
translatable; /**< Determine the block has RV32AF insturctions or not */
9292
bool has_loops; /**< Determine the block has loop or not */

src/t2c.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,8 @@ void t2c_compile(riscv_t *rv, block_t *block)
346346

347347
jit_cache_update(rv->jit_cache, key, block->func);
348348

349-
/* hot2 is declared volatile, ensuring visibility across threads */
350-
block->hot2 = true;
349+
/* Ensure all previous writes are visible before setting hot2 */
350+
__atomic_store_n(&block->hot2, true, __ATOMIC_RELEASE);
351351
}
352352

353353
struct jit_cache *jit_cache_init()

0 commit comments

Comments
 (0)