From 5ddfaf5238a7ecc4f350c55c4b772ccdb96a35e7 Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Wed, 23 Sep 2026 14:33:27 +0100 Subject: [PATCH] JavaScriptCore: sign-extend i32 C call arguments on RISCV64 The RISC-V psABI requires a 32-bit integer argument to be sign-extended to its full 64-bit argument register. BBQ loads i32 values from canonical slots with lwu, which zero-extends, so a C call made from generated wasm code can receive a wrong value for any i32 argument with bit 31 set. Add emitSignExtendI32ArgsForCCall(), which emits sext.w over every I32 argument that ends up in a register, and call it from both emitCCall() overloads. It is a no-op on every other architecture. Signed-off-by: Daniel Golle --- Source/JavaScriptCore/wasm/WasmBBQJIT.cpp | 19 +++++++++++++++++++ Source/JavaScriptCore/wasm/WasmBBQJIT.h | 6 ++++++ Source/JavaScriptCore/wasm/WasmBBQJIT64.h | 2 ++ 3 files changed, 27 insertions(+) --- a/Source/JavaScriptCore/wasm/WasmBBQJIT.cpp +++ b/Source/JavaScriptCore/wasm/WasmBBQJIT.cpp @@ -4396,6 +4396,25 @@ void BBQJIT::restoreValuesAfterCall(cons // whenever they are next used. } +void BBQJIT::emitSignExtendI32ArgsForCCall(const CallInformation& callInfo, const RTT& signature) +{ +#if CPU(RISCV64) + for (size_t i = 0; i < callInfo.params.size(); ++i) { + auto type = signature.argumentType(i); + if (type.kind != TypeKind::I32) + continue; + Location loc = Location::fromArgumentLocation(callInfo.params[i], type.kind); + if (!loc.isGPR()) + continue; + // sext.w rd, rs lowers via signExtend32To64 -> rv_addiw rd, rs, 0 + m_jit.signExtend32To64(loc.asGPR(), loc.asGPR()); + } +#else + UNUSED_PARAM(callInfo); + UNUSED_PARAM(signature); +#endif +} + template void BBQJIT::returnValuesFromCall(Vector& results, const RTT& functionType, const CallInformation& callInfo) { --- a/Source/JavaScriptCore/wasm/WasmBBQJIT.h +++ b/Source/JavaScriptCore/wasm/WasmBBQJIT.h @@ -2089,6 +2089,12 @@ public: template void saveValuesAcrossCallAndPassArguments(const Args& arguments, const CallInformation&, const RTT& signature); + // On RISC-V the psABI requires 32-bit integer arguments to be sign-extended + // in their 64-bit argument registers; BBQ otherwise zero-extends them when + // loading from canonical i32 slots (lwu). Emit sext.w on any I32 arg that + // ends up in a register. No-op on other architectures. + void emitSignExtendI32ArgsForCCall(const CallInformation& callInfo, const RTT& signature); + void slowPathSpillBindings(const RegisterBindings&); void slowPathRestoreBindings(const RegisterBindings&); void NODELETE restoreValuesAfterCall(const CallInformation&); --- a/Source/JavaScriptCore/wasm/WasmBBQJIT64.h +++ b/Source/JavaScriptCore/wasm/WasmBBQJIT64.h @@ -581,6 +581,7 @@ void BBQJIT::emitCCall(Func function, st // Preserve caller-saved registers and other info prepareForExceptions(); saveValuesAcrossCallAndPassArguments(arguments, callInfo, functionRTT.get()); + emitSignExtendI32ArgsForCCall(callInfo, functionRTT.get()); // Materialize address of native function and call register void* taggedFunctionPtr = tagCFunctionPtr(function); @@ -611,6 +612,7 @@ void BBQJIT::emitCCall(Func function, st // Preserve caller-saved registers and other info prepareForExceptions(); saveValuesAcrossCallAndPassArguments(arguments, callInfo, functionRTT.get()); + emitSignExtendI32ArgsForCCall(callInfo, functionRTT.get()); // Materialize address of native function and call register void* taggedFunctionPtr = tagCFunctionPtr(function);