Issue #3049 - Fix loongarch64 wasm JIT dispatch and truncation

This commit is contained in:
Basilisk-Dev 2026-04-25 03:40:38 -04:00 committed by wuggy
commit 4c0052da6d
4 changed files with 221 additions and 13 deletions

View file

@ -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<CodeGeneratorLoongArch64>
{
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)
{

View file

@ -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;

View file

@ -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);
}

View file

@ -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