From 4c0052da6d5f8522d9c52317eb12d9502c22c52e Mon Sep 17 00:00:00 2001 From: Basilisk-Dev Date: Sat, 25 Apr 2026 03:40:38 -0400 Subject: [PATCH] Issue #3049 - Fix loongarch64 wasm JIT dispatch and truncation --- .../loongarch64/CodeGenerator-loongarch64.cpp | 198 ++++++++++++++++++ .../loongarch64/CodeGenerator-loongarch64.h | 6 + .../MacroAssembler-loongarch64.cpp | 4 +- js/src/wasm/WasmBaselineCompile.cpp | 26 ++- 4 files changed, 221 insertions(+), 13 deletions(-) diff --git a/js/src/jit/loongarch64/CodeGenerator-loongarch64.cpp b/js/src/jit/loongarch64/CodeGenerator-loongarch64.cpp index 47be6e5725..1f5ba250ab 100644 --- a/js/src/jit/loongarch64/CodeGenerator-loongarch64.cpp +++ b/js/src/jit/loongarch64/CodeGenerator-loongarch64.cpp @@ -17,6 +17,88 @@ using namespace js; using namespace js::jit; +static constexpr uint32_t WasmTruncateInvalidCauseMask = + 1u << (Assembler::CauseV - Assembler::CauseI); + +static void +ExtractWasmTruncateInvalidCause(MacroAssemblerLOONGARCH64Compat& masm, Register reg) +{ + masm.as_cfc1(reg, Assembler::FCSR); + masm.as_bstrpick_d(reg, reg, Assembler::CauseV, Assembler::CauseI); + masm.as_andi(reg, reg, WasmTruncateInvalidCauseMask); +} + +class js::jit::OutOfLineTableSwitch : public OutOfLineCodeBase +{ + MTableSwitch* mir_; + CodeLabel jumpLabel_; + + void accept(CodeGeneratorLoongArch64* codegen) { + codegen->visitOutOfLineTableSwitch(this); + } + + public: + explicit OutOfLineTableSwitch(MTableSwitch* mir) + : mir_(mir) + {} + + MTableSwitch* mir() const { + return mir_; + } + + CodeLabel* jumpLabel() { + return &jumpLabel_; + } +}; + +void +CodeGeneratorLoongArch64::visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool) +{ + MTableSwitch* mir = ool->mir(); + + masm.haltingAlign(sizeof(void*)); + masm.bind(ool->jumpLabel()->target()); + masm.addCodeLabel(*ool->jumpLabel()); + + for (size_t i = 0; i < mir->numCases(); i++) { + LBlock* caseblock = skipTrivialBlocks(mir->getCase(i))->lir(); + Label* caseheader = caseblock->label(); + uint32_t caseoffset = caseheader->offset(); + + // Keep loongarch64 table entries at a fixed 32-byte stride so dispatch + // can jump to a stub entry and let the stub jump to the case body. + CodeLabel cl; + masm.ma_li(ScratchRegister, cl.patchAt()); + masm.jump(ScratchRegister); + masm.as_nop(); + masm.as_nop(); + masm.as_nop(); + masm.as_nop(); + cl.target()->bind(caseoffset); + masm.addCodeLabel(cl); + } +} + +void +CodeGeneratorLoongArch64::emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base) +{ + Label* defaultcase = skipTrivialBlocks(mir->getDefault())->lir()->label(); + + if (mir->low() != 0) + masm.subPtr(Imm32(mir->low()), index); + + int32_t cases = mir->numCases(); + masm.branch32(Assembler::AboveOrEqual, index, Imm32(cases), defaultcase); + + OutOfLineTableSwitch* ool = new(alloc()) OutOfLineTableSwitch(mir); + addOutOfLineCode(ool, mir); + + masm.ma_li(base, ool->jumpLabel()->patchAt()); + masm.lshiftPtr(Imm32(5), index); + masm.addPtr(index, base); + masm.jump(base); +} + void CodeGeneratorLoongArch64::visitCompare(LCompare* comp) { @@ -287,6 +369,122 @@ CodeGeneratorLoongArch64::visitUDivOrModI64(LUDivOrModI64* lir) masm.bind(&done); } +void +CodeGeneratorLoongArch64::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir) +{ + auto input = ToFloatRegister(lir->input()); + auto output = ToRegister(lir->output()); + + MWasmTruncateToInt32* mir = lir->mir(); + MIRType fromType = mir->input()->type(); + + auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input); + addOutOfLineCode(ool, mir); + + if (mir->isUnsigned()) { + if (fromType == MIRType::Double) + masm.as_truncld(ScratchDoubleReg, input); + else if (fromType == MIRType::Float32) + masm.as_truncls(ScratchDoubleReg, input); + else + MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); + + masm.moveFromDoubleHi(ScratchDoubleReg, output); + ExtractWasmTruncateInvalidCause(masm, ScratchRegister); + masm.ma_or(output, ScratchRegister); + masm.ma_b(output, Imm32(0), ool->entry(), Assembler::NotEqual); + + masm.moveFromFloat32(ScratchDoubleReg, output); + return; + } + + if (fromType == MIRType::Double) + masm.as_truncwd(ScratchFloat32Reg, input); + else if (fromType == MIRType::Float32) + masm.as_truncws(ScratchFloat32Reg, input); + else + MOZ_CRASH("unexpected type in visitWasmTruncateToInt32"); + + ExtractWasmTruncateInvalidCause(masm, output); + masm.ma_b(output, Imm32(0), ool->entry(), Assembler::NotEqual); + + masm.bind(ool->rejoin()); + masm.moveFromFloat32(ScratchFloat32Reg, output); +} + +void +CodeGeneratorLoongArch64::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + Register output = ToRegister(lir->output()); + + MWasmTruncateToInt64* mir = lir->mir(); + MIRType fromType = mir->input()->type(); + + MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32); + + auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input); + addOutOfLineCode(ool, mir); + + if (mir->isUnsigned()) { + Label isLarge, done; + + if (fromType == MIRType::Double) { + masm.loadConstantDouble(double(INT64_MAX), ScratchDoubleReg); + masm.ma_bc1d(ScratchDoubleReg, input, &isLarge, + Assembler::DoubleLessThanOrEqual, ShortJump); + + masm.as_truncld(ScratchDoubleReg, input); + } else { + masm.loadConstantFloat32(float(INT64_MAX), ScratchFloat32Reg); + masm.ma_bc1s(ScratchFloat32Reg, input, &isLarge, + Assembler::DoubleLessThanOrEqual, ShortJump); + + masm.as_truncls(ScratchDoubleReg, input); + } + + masm.moveFromDouble(ScratchDoubleReg, output); + ExtractWasmTruncateInvalidCause(masm, ScratchRegister); + masm.ma_dsrl(SecondScratchReg, output, Imm32(63)); + masm.ma_or(SecondScratchReg, ScratchRegister); + masm.ma_b(SecondScratchReg, Imm32(0), ool->entry(), Assembler::NotEqual); + + masm.ma_b(&done, ShortJump); + + masm.bind(&isLarge); + if (fromType == MIRType::Double) { + masm.as_subd(ScratchDoubleReg, input, ScratchDoubleReg); + masm.as_truncld(ScratchDoubleReg, ScratchDoubleReg); + } else { + masm.as_subs(ScratchDoubleReg, input, ScratchDoubleReg); + masm.as_truncls(ScratchDoubleReg, ScratchDoubleReg); + } + + masm.moveFromDouble(ScratchDoubleReg, output); + ExtractWasmTruncateInvalidCause(masm, ScratchRegister); + masm.ma_dsrl(SecondScratchReg, output, Imm32(63)); + masm.ma_or(SecondScratchReg, ScratchRegister); + masm.ma_b(SecondScratchReg, Imm32(0), ool->entry(), Assembler::NotEqual); + + masm.ma_li(ScratchRegister, Imm32(1)); + masm.ma_dins(output, ScratchRegister, Imm32(63), Imm32(1)); + + masm.bind(&done); + return; + } + + if (fromType == MIRType::Double) + masm.as_truncld(ScratchDoubleReg, input); + else + masm.as_truncls(ScratchDoubleReg, input); + + ExtractWasmTruncateInvalidCause(masm, output); + masm.ma_b(output, Imm32(0), ool->entry(), Assembler::NotEqual); + + masm.bind(ool->rejoin()); + masm.moveFromDouble(ScratchDoubleReg, output); +} + void CodeGeneratorLoongArch64::visitWasmAddOffset(LWasmAddOffset* lir) { diff --git a/js/src/jit/loongarch64/CodeGenerator-loongarch64.h b/js/src/jit/loongarch64/CodeGenerator-loongarch64.h index 5f8fac6453..782a2abdb7 100644 --- a/js/src/jit/loongarch64/CodeGenerator-loongarch64.h +++ b/js/src/jit/loongarch64/CodeGenerator-loongarch64.h @@ -19,12 +19,15 @@ class CodeGeneratorLoongArch64 : public CodeGeneratorMIPS64 using CodeGeneratorMIPS64::CodeGeneratorMIPS64; void visitCompare(LCompare* comp); + void visitOutOfLineTableSwitch(OutOfLineTableSwitch* ool); void visitCompareAndBranch(LCompareAndBranch* comp); void visitDivI(LDivI* ins); void visitModI(LModI* ins); void visitUDivOrMod(LUDivOrMod* ins); void visitDivOrModI64(LDivOrModI64* lir); void visitUDivOrModI64(LUDivOrModI64* lir); + void visitWasmTruncateToInt32(LWasmTruncateToInt32* lir); + void visitWasmTruncateToInt64(LWasmTruncateToInt64* lir); void visitWasmAddOffset(LWasmAddOffset* lir); void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins); void visitAsmJSAtomicExchangeHeap(LAsmJSAtomicExchangeHeap* ins); @@ -34,6 +37,9 @@ class CodeGeneratorLoongArch64 : public CodeGeneratorMIPS64 void visitAtomicTypedArrayElementBinopForEffect(LAtomicTypedArrayElementBinopForEffect* lir); void visitCompareExchangeTypedArrayElement(LCompareExchangeTypedArrayElement* lir); void visitAtomicExchangeTypedArrayElement(LAtomicExchangeTypedArrayElement* lir); + + protected: + void emitTableSwitchDispatch(MTableSwitch* mir, Register index, Register base); }; typedef CodeGeneratorLoongArch64 CodeGeneratorSpecific; diff --git a/js/src/jit/loongarch64/MacroAssembler-loongarch64.cpp b/js/src/jit/loongarch64/MacroAssembler-loongarch64.cpp index 0536008882..c74909b907 100644 --- a/js/src/jit/loongarch64/MacroAssembler-loongarch64.cpp +++ b/js/src/jit/loongarch64/MacroAssembler-loongarch64.cpp @@ -357,9 +357,7 @@ void MacroAssemblerLOONGARCH64Compat::convertFloat32ToInt32( as_ftintrz_w_s(fpscratch, src); as_movfcsr2gr(scratch); moveFromFloat32(fpscratch, dest); - MOZ_ASSERT(CauseBitPos + CauseBitCount < 33); - MOZ_ASSERT(CauseBitPos < 32); - as_bstrpick_w(scratch, scratch, CauseBitPos + CauseBitCount - 1, CauseBitPos); + as_bstrpick_d(scratch, scratch, CauseBitPos + CauseBitCount - 1, CauseBitPos); as_andi(scratch, scratch, CauseIOrVMask); ma_b(scratch, zero, fail, Assembler::NotEqual); } diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index 8623697638..d17bad15c5 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -2400,15 +2400,22 @@ class BaseCompiler } void jumpTable(LabelVector& labels) { -#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM) || \ - defined(JS_CODEGEN_LOONGARCH64) +#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_ARM) for (uint32_t i = 0; i < labels.length(); i++) { CodeLabel cl; -# if defined(JS_CODEGEN_LOONGARCH64) - masm.writeCodePointer(&cl); -# else masm.writeCodePointer(cl.patchAt()); -# endif + cl.target()->bind(labels[i]->offset()); + masm.addCodeLabel(cl); + } +#elif defined(JS_CODEGEN_LOONGARCH64) + for (uint32_t i = 0; i < labels.length(); i++) { + CodeLabel cl; + masm.ma_li(ScratchRegister, cl.patchAt()); + masm.jump(ScratchRegister); + masm.as_nop(); + masm.as_nop(); + masm.as_nop(); + masm.as_nop(); cl.target()->bind(labels[i]->offset()); masm.addCodeLabel(cl); } @@ -2450,16 +2457,15 @@ class BaseCompiler Assembler::Always); #elif defined(JS_CODEGEN_LOONGARCH64) ScratchI32 scratch(*this); - SecondScratchRegisterScope target(masm); CodeLabel tableCl; masm.ma_li(scratch, tableCl.patchAt()); tableCl.target()->bind(theTable->offset()); masm.addCodeLabel(tableCl); - masm.computeEffectiveAddress(BaseIndex(scratch, switchValue.reg, ScalePointer), scratch); - masm.loadPtr(Address(scratch, 0), target); - masm.jump(target); + masm.lshiftPtr(Imm32(5), switchValue.reg); + masm.addPtr(switchValue.reg, scratch); + masm.jump(scratch); #else MOZ_CRASH("BaseCompiler platform hook: tableSwitch"); #endif