From 0326799f3f938645c5437d9967c9422aed418c2a Mon Sep 17 00:00:00 2001 From: cassuto Date: Sat, 29 Aug 2026 15:08:51 -0600 Subject: [PATCH] simx: clear bit 0 of the JALR target address The RISC-V ISA requires JALR to clear bit 0 of the computed target (rs1 + imm). SimX carried the bit into the warp PC, while release RTL builds drop it implicitly through from_fullPC() PC truncation - a SimX<->RTL divergence reachable from hand-written assembly. Adds a conform test that lands on an intentionally odd JALR target via an overlapped instruction word, so fixed and unfixed builds both decode valid instructions but produce different pass/fail signatures. Runs in CI through the kernel category (simx + rtlsim, both XLENs). Rebase of PR #339 (original targeted the retired sim/simx/execute.cpp). Co-Authored-By: Claude Fable 5 --- sim/simx/alu_unit.cpp | 4 ++- tests/kernel/conform/main.cpp | 4 ++- tests/kernel/conform/tests.cpp | 58 +++++++++++++++++++++++++++++++++- tests/kernel/conform/tests.h | 2 ++ 4 files changed, 65 insertions(+), 3 deletions(-) diff --git a/sim/simx/alu_unit.cpp b/sim/simx/alu_unit.cpp index bb0a6fe2b9..648729c0ad 100644 --- a/sim/simx/alu_unit.cpp +++ b/sim/simx/alu_unit.cpp @@ -397,7 +397,9 @@ void AluUnit::execute(instr_trace_t* trace) { if (!tmask.test(t)) continue; rd_data[t].i = link_pc; } - warp.PC = rs1_data[thread_last].i + offset; + // JALR clears bit 0 of the computed target (RISC-V ISA); the RTL + // release build drops it implicitly via from_fullPC() truncation. + warp.PC = (rs1_data[thread_last].u + offset) & ~Word(1); core_->perf_stats().branches += 1; } break; case BrType::SYS: diff --git a/tests/kernel/conform/main.cpp b/tests/kernel/conform/main.cpp index f8a780b803..ef18e59704 100644 --- a/tests/kernel/conform/main.cpp +++ b/tests/kernel/conform/main.cpp @@ -29,6 +29,8 @@ int main() { errors += test_shfl(); errors += test_qshfl(); + errors += test_jalr(); + if (0 == errors) { PRINTF("Passed!\n"); } else { @@ -36,4 +38,4 @@ int main() { } return errors; -} \ No newline at end of file +} diff --git a/tests/kernel/conform/tests.cpp b/tests/kernel/conform/tests.cpp index f1cd22ab1f..f00a60466c 100644 --- a/tests/kernel/conform/tests.cpp +++ b/tests/kernel/conform/tests.cpp @@ -448,4 +448,60 @@ int test_qshfl() { errors += check_error(qshfl_buffer, 0, QSHFL_GROUP_SZ); return errors; -} \ No newline at end of file +} + +/////////////////////////////////////////////////////////////////////////////// + +#define JALR_PASS_VALUE 0x13579bdf + +uint32_t __attribute__((noinline)) do_jalr_misaligned() { + uint32_t value = 0; + asm volatile( + // x1 is prepared so that the misaligned decode path can immediately + // execute a valid JALR to label 3 and produce a deterministic failure + // value instead of aborting in the decoder. + "la x1, 3f\n" + // 1744 matches the immediate embedded in the overlapped misaligned + // instruction below: jalr x0, -1744(x1). + "addi x1, x1, 1744\n" + "la t0, 1f\n" + // Add 1 on purpose. A correct JALR implementation must clear bit 0 and + // land at 1f. An incorrect implementation enters at 1f + 1 instead. + "addi t0, t0, 1\n" + "jalr x0, t0, 0\n" + ".balign 4\n" + "1:\n" + // 0x00806713 is a hand-picked overlapped instruction word: + // - from the aligned entry at 1f it decodes as: ori x14, x0, 8 + // - from the misaligned entry at 1f + 1 it decodes as: + // jalr x0, -1744(x1) + // That lets both fixed and unfixed builds decode valid instructions + // while still producing different results. + ".4byte 0x00806713\n" + // next aligned instruction after the pass-path overlap word + ".4byte 0x00000093\n" + "j 4f\n" + "3:\n" + // fail signature: JALR did not clear bit 0 + "li %[value], 0x2468ace0\n" + "j 5f\n" + "4:\n" + // pass signature: JALR correctly cleared bit 0 + "li %[value], 0x13579bdf\n" + "5:\n" + : [value] "=r" (value) + : + : "x1", "t0", "x14"); + return value; +} + +int test_jalr() { + PRINTF("JALR Test\n"); + vx_tmc_one(); + uint32_t value = do_jalr_misaligned(); + if (value != JALR_PASS_VALUE) { + PRINTF("*** error: jalr value=0x%x, expected=0x%x\n", value, JALR_PASS_VALUE); + return 1; + } + return 0; +} diff --git a/tests/kernel/conform/tests.h b/tests/kernel/conform/tests.h index a791735746..a398c2809b 100644 --- a/tests/kernel/conform/tests.h +++ b/tests/kernel/conform/tests.h @@ -28,4 +28,6 @@ int test_vote(); int test_shfl(); int test_qshfl(); +int test_jalr(); + #endif