From feb29eaff04ffa9804fbdfb8d265d8541b9e8783 Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Thu, 23 Apr 2026 23:41:12 -0400 Subject: [PATCH] Issue #3049 - Wire up loongarch64 SpiderMonkey baseline backend --- js/src/jit/LoongArchMinimalJit.cpp | 2 +- .../jit/loongarch64/Assembler-loongarch64.cpp | 58 +++++++++++++++++++ .../jit/loongarch64/Assembler-loongarch64.h | 23 ++++++++ .../loongarch64/MacroAssembler-loongarch64.h | 17 ++++++ js/src/jit/shared/CodeGenerator-shared.cpp | 3 +- js/src/moz.build | 12 ++-- js/src/old-configure.in | 2 +- js/src/wasm/WasmFrameIterator.cpp | 6 +- js/src/wasm/WasmSignalHandlers.cpp | 2 +- js/src/wasm/WasmStubs.cpp | 9 ++- js/src/wasm/WasmTypes.cpp | 2 +- 11 files changed, 122 insertions(+), 14 deletions(-) diff --git a/js/src/jit/LoongArchMinimalJit.cpp b/js/src/jit/LoongArchMinimalJit.cpp index 9cf4451a6a..31d911cf68 100644 --- a/js/src/jit/LoongArchMinimalJit.cpp +++ b/js/src/jit/LoongArchMinimalJit.cpp @@ -20,7 +20,7 @@ using namespace js; -#if defined(__loongarch64) +#if defined(__loongarch64) && !defined(JS_CODEGEN_LOONGARCH64) namespace js { namespace jit { diff --git a/js/src/jit/loongarch64/Assembler-loongarch64.cpp b/js/src/jit/loongarch64/Assembler-loongarch64.cpp index fd912b9a9b..5ad1ce35f7 100644 --- a/js/src/jit/loongarch64/Assembler-loongarch64.cpp +++ b/js/src/jit/loongarch64/Assembler-loongarch64.cpp @@ -2224,6 +2224,51 @@ void Assembler::TraceJumpRelocations(JSTracer* trc, JitCode* code, } } +static void TraceBufferDataRelocations(JSTracer* trc, + LOONGBufferWithExecutableCopy* buffer, + CompactBufferReader& reader); + +void Assembler::trace(JSTracer* trc) { + for (size_t i = 0; i < jumps_.length(); i++) { + RelativePatch& rp = jumps_[i]; + if (rp.kind == Relocation::JITCODE) { + JitCode* code = JitCode::FromExecutable((uint8_t*)rp.target); + TraceManuallyBarrieredEdge(trc, &code, "masmrel32"); + MOZ_ASSERT(code == JitCode::FromExecutable((uint8_t*)rp.target)); + } + } + if (dataRelocations_.length()) { + CompactBufferReader reader(dataRelocations_); + TraceBufferDataRelocations(trc, &m_buffer, reader); + } +} + +static void TraceOneDataRelocation(JSTracer* trc, Instruction* inst) { + void* ptr = (void*)Assembler::ExtractLoad64Value(inst); + void* prior = ptr; + + // Data relocations can be for Values or for raw pointers. If a Value is + // zero-tagged, we can trace it as if it were a raw pointer. If a Value + // is not zero-tagged, we have to interpret it as a Value to ensure that the + // tag bits are masked off to recover the actual pointer. + uintptr_t word = reinterpret_cast(ptr); + if (word >> JSVAL_TAG_SHIFT) { + // This relocation is a Value with a non-zero tag. + Value v = Value::fromRawBits(word); + TraceManuallyBarrieredEdge(trc, &v, "jit-masm-value"); + ptr = (void*)v.bitsAsPunboxPointer(); + } else { + // This relocation is a raw pointer or a Value with a zero tag. + // No barrier needed since these are constants. + TraceManuallyBarrieredGenericPointerEdge( + trc, reinterpret_cast(&ptr), "jit-masm-ptr"); + } + + if (ptr != prior) { + Assembler::UpdateLoad64Value(inst, uint64_t(ptr)); + } +} + static void TraceOneDataRelocation(JSTracer* trc, mozilla::Maybe& awjc, JitCode* code, Instruction* inst) { @@ -2255,6 +2300,15 @@ static void TraceOneDataRelocation(JSTracer* trc, } } +static void TraceBufferDataRelocations(JSTracer* trc, + LOONGBufferWithExecutableCopy* buffer, + CompactBufferReader& reader) { + while (reader.more()) { + BufferOffset bo(reader.readUnsigned()); + TraceOneDataRelocation(trc, buffer->getInst(bo)); + } +} + /* static */ void Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader) { @@ -2465,6 +2519,10 @@ void Assembler::PatchDataWithValueCheck(CodeLocationLabel label, Assembler::UpdateLoad64Value(inst, uint64_t(newValue.value)); } +void Assembler::PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm) { + Assembler::UpdateLoad64Value((Instruction*)code, uint64_t(imm.value)); +} + uint64_t Assembler::ExtractInstructionImmediate(uint8_t* code) { InstImm* inst = (InstImm*)code; return Assembler::ExtractLoad64Value(inst); diff --git a/js/src/jit/loongarch64/Assembler-loongarch64.h b/js/src/jit/loongarch64/Assembler-loongarch64.h index 12bdd87027..f3d280750c 100644 --- a/js/src/jit/loongarch64/Assembler-loongarch64.h +++ b/js/src/jit/loongarch64/Assembler-loongarch64.h @@ -215,6 +215,12 @@ static constexpr Register WasmJitEntryReturnScratch = t1; static constexpr uint32_t ABIStackAlignment = 16; static constexpr uint32_t CodeAlignment = 16; + +// This boolean indicates whether we support SIMD instructions flavoured for +// this architecture or not. Rather than a method in the LIRGenerator, it is +// here such that it is accessible from the entire codebase. Once full support +// for SIMD is reached on all tier-1 platforms, this constant can be deleted. +static constexpr bool SupportsSimd = false; static constexpr uint32_t JitStackAlignment = 16; static constexpr uint32_t JitStackValueAlignment = @@ -821,6 +827,19 @@ class LOONGBufferWithExecutableCopy : public LOONGBuffer { } } + bool appendBuffer(const LOONGBufferWithExecutableCopy& other) { + if (this->oom() || other.oom()) { + return false; + } + + for (Slice* cur = other.head; cur != nullptr; cur = cur->getNext()) { + if (!appendRawCode(cur->instructions.begin(), cur->length())) { + return false; + } + } + return !this->oom(); + } + bool appendRawCode(const uint8_t* code, size_t numBytes) { if (this->oom()) { return false; @@ -1800,6 +1819,9 @@ class Assembler : public AssemblerLOONGARCH64 { public: Assembler() : AssemblerLOONGARCH64() {} + // MacroAssemblers hold onto gcthings, so they are traced by the GC. + void trace(JSTracer* trc); + static uintptr_t GetPointer(uint8_t*); using AssemblerLOONGARCH64::bind; @@ -1840,6 +1862,7 @@ class Assembler : public AssemblerLOONGARCH64 { PatchedImmPtr newValue, PatchedImmPtr expectedValue); + static void PatchInstructionImmediate(uint8_t* code, PatchedImmPtr imm); static uint64_t ExtractInstructionImmediate(uint8_t* code); static void ToggleCall(CodeLocationLabel inst_, bool enabled); diff --git a/js/src/jit/loongarch64/MacroAssembler-loongarch64.h b/js/src/jit/loongarch64/MacroAssembler-loongarch64.h index 3e3048c5cc..533dad2eca 100644 --- a/js/src/jit/loongarch64/MacroAssembler-loongarch64.h +++ b/js/src/jit/loongarch64/MacroAssembler-loongarch64.h @@ -394,6 +394,18 @@ class MacroAssemblerLOONGARCH64Compat : public MacroAssemblerLOONGARCH64 { MacroAssemblerLOONGARCH64Compat() {} + struct AutoPrepareForPatching { + explicit AutoPrepareForPatching(MacroAssemblerLOONGARCH64Compat&) {} + }; + + bool asmMergeWith(const MacroAssemblerLOONGARCH64Compat& other) { + if (!AssemblerShared::asmMergeWith(size(), other)) + return false; + return m_buffer.appendBuffer(other.m_buffer); + } + + size_t labelToPatchOffset(CodeOffset label) { return label.offset(); } + void convertBoolToInt32(Register src, Register dest) { ma_and(dest, src, Imm32(0xff)); }; @@ -502,6 +514,11 @@ class MacroAssemblerLOONGARCH64Compat : public MacroAssemblerLOONGARCH64 { void push(FloatRegister reg) { ma_push(reg); } void pop(Register reg) { ma_pop(reg); } void pop(FloatRegister reg) { ma_pop(reg); } + void pop(const Address& address) { + ScratchRegisterScope scratch(asMasm()); + ma_pop(scratch); + storePtr(scratch, address); + } // Emit a branch that can be toggled to a non-operation. On LOONGARCH64 we use // "andi" instruction to toggle the branch. diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 0d5b10bfeb..78f66bb9da 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -1620,7 +1620,8 @@ CodeGeneratorShared::getJumpLabelForBranch(MBasicBlock* block) } // This function is not used for MIPS/MIPS64. MIPS has branchToBlock. -#if !defined(JS_CODEGEN_MIPS32) && !defined(JS_CODEGEN_MIPS64) +#if !defined(JS_CODEGEN_MIPS32) && !defined(JS_CODEGEN_MIPS64) && \ + !defined(JS_CODEGEN_LOONGARCH64) void CodeGeneratorShared::jumpToBlock(MBasicBlock* mir, Assembler::Condition cond) { diff --git a/js/src/moz.build b/js/src/moz.build index 23db100e7b..4c160585e4 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -349,7 +349,6 @@ main_deunified_sources = [ 'vm/WeakMapPtr.cpp', 'vm/Xdr.cpp', 'wasm/AsmJS.cpp', - 'wasm/WasmBaselineCompile.cpp', 'wasm/WasmBinaryFormat.cpp', 'wasm/WasmBinaryIterator.cpp', 'wasm/WasmBinaryToAST.cpp', @@ -361,7 +360,6 @@ main_deunified_sources = [ 'wasm/WasmFrameIterator.cpp', 'wasm/WasmGenerator.cpp', 'wasm/WasmInstance.cpp', - 'wasm/WasmIonCompile.cpp', 'wasm/WasmJS.cpp', 'wasm/WasmModule.cpp', 'wasm/WasmSignalHandlers.cpp', @@ -372,6 +370,12 @@ main_deunified_sources = [ 'wasm/WasmTypes.cpp' ] +if not CONFIG['JS_CODEGEN_LOONGARCH64']: + SOURCES += [ + 'wasm/WasmBaselineCompile.cpp', + 'wasm/WasmIonCompile.cpp', + ] + # jsarray.cpp and jsatom.cpp cannot be built in unified mode because # xpcshell is broken during packaging when compiled with gcc-4.8.2 # builtin/RegExp.cpp cannot be built in unified mode because it is built @@ -427,7 +431,7 @@ if CONFIG['ENABLE_TRACE_LOGGING']: jit_ioncheck1_deunified_sources = [] jit_ioncheck2_deunified_sources = [] jit_ioncheck3_deunified_sources = [] -if not CONFIG['ENABLE_ION']: +if CONFIG['JS_CODEGEN_NONE']: jit_ioncheck1_deunified_sources += [ 'jit/none/Trampoline-none.cpp' ] @@ -538,9 +542,9 @@ elif CONFIG['JS_CODEGEN_LOONGARCH64']: 'jit/loongarch64/BaselineCompiler-loongarch64.cpp', 'jit/loongarch64/BaselineIC-loongarch64.cpp', 'jit/loongarch64/MacroAssembler-loongarch64.cpp', - 'jit/mips64/MoveEmitter-mips64.cpp', 'jit/loongarch64/SharedIC-loongarch64.cpp', 'jit/loongarch64/Trampoline-loongarch64.cpp', + 'jit/mips64/MoveEmitter-mips64.cpp', ] elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']: jit_ioncheck1_deunified_sources += [ diff --git a/js/src/old-configure.in b/js/src/old-configure.in index 8838524219..13aac470cf 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -1728,7 +1728,7 @@ if test -n "$JS_SIMULATOR"; then esac fi -if test -z "$ENABLE_ION"; then +if test -z "$ENABLE_ION" && test "$CPU_ARCH" != "loongarch64"; then AC_DEFINE(JS_CODEGEN_NONE) JS_CODEGEN_NONE=1 elif test "$JS_SIMULATOR" = arm; then diff --git a/js/src/wasm/WasmFrameIterator.cpp b/js/src/wasm/WasmFrameIterator.cpp index 427cece69b..4538e46bbd 100644 --- a/js/src/wasm/WasmFrameIterator.cpp +++ b/js/src/wasm/WasmFrameIterator.cpp @@ -242,7 +242,7 @@ static const unsigned PushedRetAddr = 8; static const unsigned PushedFP = 32; static const unsigned StoredFP = 36; static const unsigned PostStorePrePopFP = 4; -#elif defined(JS_CODEGEN_NONE) +#elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_LOONGARCH64) # if defined(DEBUG) static const unsigned PushedRetAddr = 0; static const unsigned PostStorePrePopFP = 0; @@ -826,7 +826,7 @@ wasm::ToggleProfiling(const Code& code, const CallSite& callSite, bool enabled) BOffImm16 calleeOffset; callerInsn->extractImm16(&calleeOffset); void* callee = calleeOffset.getDest(reinterpret_cast(caller)); -#elif defined(JS_CODEGEN_NONE) +#elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_LOONGARCH64) MOZ_CRASH(); void* callee = nullptr; #else @@ -853,7 +853,7 @@ wasm::ToggleProfiling(const Code& code, const CallSite& callSite, bool enabled) MOZ_CRASH(); #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64) new (caller) InstImm(op_regimm, zero, rt_bgezal, BOffImm16(to - caller)); -#elif defined(JS_CODEGEN_NONE) +#elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_LOONGARCH64) MOZ_CRASH(); #else # error "Missing architecture" diff --git a/js/src/wasm/WasmSignalHandlers.cpp b/js/src/wasm/WasmSignalHandlers.cpp index 37afea41de..aaeaf59b3c 100644 --- a/js/src/wasm/WasmSignalHandlers.cpp +++ b/js/src/wasm/WasmSignalHandlers.cpp @@ -399,7 +399,7 @@ struct macos_aarch64_context { static uint8_t** ContextToPC(CONTEXT* context) { -#ifdef JS_CODEGEN_NONE +#if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_LOONGARCH64) MOZ_CRASH(); #else return reinterpret_cast(&PC_sig(context)); diff --git a/js/src/wasm/WasmStubs.cpp b/js/src/wasm/WasmStubs.cpp index 67d744e585..7fd674826a 100644 --- a/js/src/wasm/WasmStubs.cpp +++ b/js/src/wasm/WasmStubs.cpp @@ -83,7 +83,7 @@ static const LiveRegisterSet NonVolatileRegs = static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) + NonVolatileRegs.fpus().getPushSizeInBytes() + sizeof(double); -#elif defined(JS_CODEGEN_NONE) +#elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_LOONGARCH64) static const unsigned FramePushedAfterSave = 0; #else static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) @@ -586,6 +586,10 @@ 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): @@ -808,6 +812,7 @@ 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. @@ -1049,7 +1054,7 @@ wasm::GenerateInterruptExit(MacroAssembler& masm, Label* throwLabel) masm.ret(); #elif defined(JS_CODEGEN_ARM64) MOZ_CRASH(); -#elif defined (JS_CODEGEN_NONE) +#elif defined (JS_CODEGEN_NONE) || defined(JS_CODEGEN_LOONGARCH64) MOZ_CRASH(); #else # error "Unknown architecture!" diff --git a/js/src/wasm/WasmTypes.cpp b/js/src/wasm/WasmTypes.cpp index 993c4903be..f92609b84f 100644 --- a/js/src/wasm/WasmTypes.cpp +++ b/js/src/wasm/WasmTypes.cpp @@ -399,7 +399,7 @@ GetCPUID() #elif defined(JS_CODEGEN_MIPS64) MOZ_ASSERT(jit::GetMIPSFlags() <= (UINT32_MAX >> ARCH_BITS)); return MIPS64 | (jit::GetMIPSFlags() << ARCH_BITS); -#elif defined(JS_CODEGEN_NONE) +#elif defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_LOONGARCH64) return 0; #else # error "unknown architecture"