From f796b3f7f1643e2619d691252d8514e4cd2b5eae Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Fri, 24 Apr 2026 16:22:59 -0400 Subject: [PATCH] Issue #3049 - Enable loongarch64 wasm runtime and Ion patching --- dom/workers/RuntimeService.cpp | 3 +- js/src/jit/MacroAssembler.cpp | 6 ++- .../jit/loongarch64/Assembler-loongarch64.cpp | 20 ++++++++-- .../jit/loongarch64/Assembler-loongarch64.h | 6 +++ js/src/wasm/WasmJS.cpp | 2 +- js/src/wasm/WasmSignalHandlers.cpp | 7 ---- js/src/wasm/WasmStubs.cpp | 38 ++++++++++++++++--- js/xpconnect/src/XPCJSContext.cpp | 4 +- modules/libpref/init/all.js | 4 +- 9 files changed, 63 insertions(+), 27 deletions(-) diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index 1b812c48b5..3eb769a150 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -295,9 +295,8 @@ LoadContextOptions(const char* aPrefName, void* /* aClosure */) bool useIon = GetWorkerPref(NS_LITERAL_CSTRING("ion")); #if defined(JS_CODEGEN_LOONGARCH64) - // asm.js and wasm are still disabled on loongarch64 workers. + // asm.js is still disabled on loongarch64 workers. useAsmJS = false; - useWasm = false; #endif bool useNativeRegExp = GetWorkerPref(NS_LITERAL_CSTRING("native_regexp")); diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index 9519e8f0eb..cab426d708 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -1875,7 +1875,8 @@ MacroAssembler::outOfLineTruncateSlow(FloatRegister src, Register dest, bool wid bool compilingWasm) { #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ - defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) + defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ + defined(JS_CODEGEN_LOONGARCH64) if (widenFloatToDouble) { convertFloat32ToDouble(src, ScratchDoubleReg); src = ScratchDoubleReg; @@ -1905,7 +1906,8 @@ MacroAssembler::outOfLineTruncateSlow(FloatRegister src, Register dest, bool wid storeCallInt32Result(dest); #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \ - defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) + defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) || \ + defined(JS_CODEGEN_LOONGARCH64) // Nothing #elif defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) if (widenFloatToDouble) diff --git a/js/src/jit/loongarch64/Assembler-loongarch64.cpp b/js/src/jit/loongarch64/Assembler-loongarch64.cpp index 5ad1ce35f7..9612d62362 100644 --- a/js/src/jit/loongarch64/Assembler-loongarch64.cpp +++ b/js/src/jit/loongarch64/Assembler-loongarch64.cpp @@ -20,10 +20,22 @@ using namespace js::jit; void js::jit::PatchJump(CodeLocationJump& jump_, CodeLocationLabel label, ReprotectCode reprotect) { - (void)jump_; - (void)label; - (void)reprotect; - MOZ_CRASH("Ion patching is not supported on loongarch64"); + Instruction* inst = reinterpret_cast(jump_.raw()); + + // jumpWithPatch() reserves four instructions: + // beq zero, zero, 0 + // INVALID_OFFSET + // nop + // nop + // Patch that block into the long-jump form used elsewhere in the backend: + // load64 scratch, target + // jirl zero, scratch, 0 + MaybeAutoWritableJitCode awjc(inst, 4 * sizeof(uint32_t), reprotect); + Assembler::WriteLoad64Instructions(inst, ScratchRegister, + uintptr_t(label.raw())); + inst[3] = InstImm(op_jirl, BOffImm16(0), ScratchRegister, zero); + + AutoFlushICache::flush(uintptr_t(inst), 4 * sizeof(uint32_t)); } // Note this is used for inter-wasm calls and may pass arguments and results diff --git a/js/src/jit/loongarch64/Assembler-loongarch64.h b/js/src/jit/loongarch64/Assembler-loongarch64.h index 81b7929f2b..1428dc9161 100644 --- a/js/src/jit/loongarch64/Assembler-loongarch64.h +++ b/js/src/jit/loongarch64/Assembler-loongarch64.h @@ -209,6 +209,12 @@ static constexpr Register WasmTableCallScratchReg1 = ABINonArgReg1; static constexpr Register WasmTableCallScratchReg = WasmTableCallScratchReg0; static constexpr Register WasmTableCallSigReg = ABINonArgReg2; static constexpr Register WasmTableCallIndexReg = ABINonArgReg3; +static constexpr Register WasmIonExitRegCallee = t0; +static constexpr Register WasmIonExitRegE0 = a0; +static constexpr Register WasmIonExitRegE1 = a1; +static constexpr Register WasmIonExitRegD0 = a0; +static constexpr Register WasmIonExitRegD1 = a1; +static constexpr Register WasmIonExitRegD2 = t0; // Register used as a scratch along the return path in the fast js -> wasm stub // code. This must not overlap ReturnReg, JSReturnOperand, or InstanceReg. diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index be953f0aa5..dea2bddc46 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -66,7 +66,7 @@ wasm::HasCompilerSupport(ExclusiveContext* cx) return false; #endif -#if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64) || defined(JS_CODEGEN_LOONGARCH64) +#if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64) return false; #else return true; diff --git a/js/src/wasm/WasmSignalHandlers.cpp b/js/src/wasm/WasmSignalHandlers.cpp index 704223069c..a1eec6c9b7 100644 --- a/js/src/wasm/WasmSignalHandlers.cpp +++ b/js/src/wasm/WasmSignalHandlers.cpp @@ -1363,13 +1363,6 @@ ProcessHasSignalHandlers() # endif #endif -#if defined(JS_CODEGEN_LOONGARCH64) - // The loongarch64 port is baseline-only for now: no wasm compiler support - // and no Ion interrupt machinery that depends on these process-wide - // signal handlers. Keeping them installed risks misrouting ordinary faults. - return false; -#endif - // The interrupt handler allows the main thread to be paused from another // thread (see InterruptRunningJitCode). #if defined(XP_WIN) diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp index c6d66a2947..0fa62e608c 100644 --- a/js/src/wasm/WasmStubs.cpp +++ b/js/src/wasm/WasmStubs.cpp @@ -588,10 +588,6 @@ static const unsigned SavedTlsReg = sizeof(void*); ProfilingOffsets wasm::GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* throwLabel) { -#if defined(JS_CODEGEN_LOONGARCH64) - MOZ_CRASH("wasm import JIT exits are unsupported on loongarch64"); - return ProfilingOffsets(); -#else masm.setFramePushed(0); // JIT calls use the following stack layout (sp grows to the left): @@ -814,7 +810,6 @@ wasm::GenerateImportJitExit(MacroAssembler& masm, const FuncImport& fi, Label* t offsets.end = masm.currentOffset(); return offsets; -#endif } // Generate a stub that calls into ReportTrap with the right trap reason. @@ -1056,7 +1051,38 @@ wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel) masm.ret(); #elif defined(JS_CODEGEN_ARM64) MOZ_CRASH(); -#elif defined (JS_CODEGEN_NONE) || defined(JS_CODEGEN_LOONGARCH64) +#elif defined(JS_CODEGEN_LOONGARCH64) + // Reserve space to store the resumePC. We restore all machine state from + // the saved register image and use the reserved `rx` register as the final + // jump scratch to resume execution. + masm.subFromStackPtr(Imm32(sizeof(intptr_t))); + masm.setFramePushed(0); + static_assert(!SupportsSimd, "high lanes of SIMD registers need to be saved too."); + masm.PushRegsInMask(AllRegsExceptSP); + + // Save the unaligned stack pointer in a non-volatile register, then align + // for the C++ call into HandleExecutionInterrupt. + masm.moveStackPtrTo(s0); + masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1))); + + // Store resumePC into the reserved space above the saved register block. + masm.loadWasmActivationFromSymbolicAddress(IntArgReg0); + masm.loadPtr(Address(IntArgReg0, WasmActivation::offsetOfResumePC()), IntArgReg1); + masm.storePtr(IntArgReg1, Address(s0, masm.framePushed())); + + masm.assertStackAlignment(ABIStackAlignment); + masm.call(SymbolicAddress::HandleExecutionInterrupt); + + masm.branchIfFalseBool(ReturnReg, throwLabel); + + // Restore the interrupted machine state before resuming execution. + masm.moveToStackPtr(s0); + masm.PopRegsInMask(AllRegsExceptSP); + + masm.loadPtr(Address(StackPointer, 0), rx); + masm.addToStackPtr(Imm32(sizeof(intptr_t))); + masm.jump(rx); +#elif defined (JS_CODEGEN_NONE) MOZ_CRASH(); #else # error "Unknown architecture!" diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp index 09ac6dd75e..cda4893717 100644 --- a/js/xpconnect/src/XPCJSContext.cpp +++ b/js/xpconnect/src/XPCJSContext.cpp @@ -1421,10 +1421,8 @@ ReloadPrefsCallback(const char* pref, void* data) bool useNativeRegExp = Preferences::GetBool(JS_OPTIONS_DOT_STR "native_regexp") && !safeMode; #if defined(JS_CODEGEN_LOONGARCH64) - // asm.js, wasm, and native regexp JIT are still disabled on loongarch64. + // asm.js and native regexp JIT are still disabled on loongarch64. useAsmJS = false; - useWasm = false; - useWasmBaseline = false; useNativeRegExp = false; #endif diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index f61f068f54..6621a5b7f6 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1266,7 +1266,7 @@ pref("javascript.options.baselinejit", true); #if defined(JS_CODEGEN_LOONGARCH64) pref("javascript.options.ion", true); pref("javascript.options.asmjs", false); -pref("javascript.options.wasm", false); +pref("javascript.options.wasm", true); #else pref("javascript.options.ion", true); // JIT warm-up thresholds (-1 keeps engine defaults). @@ -1282,7 +1282,7 @@ pref("javascript.options.ion.inlining", true); // only enable it by default for 64bit builds #ifdef HAVE_64BIT_BUILD # if defined(JS_CODEGEN_LOONGARCH64) -pref("javascript.options.wasm_baselinejit", false); +pref("javascript.options.wasm_baselinejit", true); # else pref("javascript.options.wasm_baselinejit", true); # endif