From 398c0251ea8958509b4976fe0b3ebddb25228960 Mon Sep 17 00:00:00 2001 From: Jamie Magee Date: Sun, 5 Apr 2026 20:35:28 -0700 Subject: [PATCH] [RISC-V] Use fine-grained fence variants for memory barriers The JIT always emitted fence rw,rw (full barrier) regardless of the requested BarrierKind. RISC-V's fence instruction supports finer predecessor/successor bits, so use them: - BARRIER_FULL -> fence rw,rw (0x33) - unchanged - BARRIER_LOAD_ONLY -> fence r,rw (0x23) - acquire semantics - BARRIER_STORE_ONLY -> fence rw,w (0x31) - release semantics Same idea as ARM64, which uses DMB ISHLD for load-only and DMB ISH for full. Volatile reads and post-CpBlk barriers were paying for a full fence when a lighter one is sufficient under RVWMO. --- src/coreclr/jit/codegenriscv64.cpp | 20 ++++++++++++++++---- src/coreclr/jit/instr.h | 4 +++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 4a4cc06098723b..98d9c955aa443a 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -2200,7 +2200,6 @@ void CodeGen::genCodeForCpObj(GenTreeBlk* cpObjNode) if (cpObjNode->IsVolatile()) { // issue a INS_BARRIER_RMB after a volatile CpObj operation - // TODO-RISCV64: there is only BARRIER_FULL for RISCV64. instGen_MemoryBarrier(BARRIER_FULL); } @@ -6254,7 +6253,7 @@ void CodeGen::genJumpToThrowHlpBlk_la( // instGen_MemoryBarrier: Emit a MemoryBarrier instruction // // Arguments: -// barrierKind - kind of barrier to emit (Only supports the Full now!! This depends on the CPU). +// barrierKind - kind of barrier to emit // // Notes: // All MemoryBarriers instructions can be removed by DOTNET_JitNoMemoryBarriers=1 @@ -6268,8 +6267,21 @@ void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind) } #endif // DEBUG - // TODO-RISCV64: Use the exact barrier type depending on the CPU. - GetEmitter()->emitIns_I(INS_fence, EA_4BYTE, INS_BARRIER_FULL); + insBarrier barrier; + switch (barrierKind) + { + case BARRIER_LOAD_ONLY: + barrier = INS_BARRIER_LOAD_ONLY; + break; + case BARRIER_STORE_ONLY: + barrier = INS_BARRIER_STORE_ONLY; + break; + default: + barrier = INS_BARRIER_FULL; + break; + } + + GetEmitter()->emitIns_I(INS_fence, EA_4BYTE, barrier); } /*----------------------------------------------------------------------------- diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index 82c60d9ac5995b..1edad6b3de6c25 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -566,7 +566,9 @@ enum insOpts : unsigned enum insBarrier : unsigned { - INS_BARRIER_FULL = 0x33, + INS_BARRIER_FULL = 0x33, // fence rw, rw + INS_BARRIER_LOAD_ONLY = 0x23, // fence r, rw + INS_BARRIER_STORE_ONLY = 0x31, // fence rw, w }; #elif defined(TARGET_WASM) enum insOpts : unsigned