From 0b212e1fe495030d7953e3b078973282dc1b913d Mon Sep 17 00:00:00 2001 From: win7-7 Date: Tue, 30 Dec 2025 13:45:26 +0200 Subject: [PATCH] 1339089 - Inline floor/ceil/trunc/nearest for floating-point values when we have adequate SSE support 1339089: Inline floor/ceil/trunc/nearest in Ion when we have sse4. 1339089: Inline wasm::floor/trunc/nearest/round in the baseline compiler too; --- js/src/jit/IonTypes.h | 8 ++ js/src/jit/Lowering.cpp | 16 ++++ js/src/jit/Lowering.h | 1 + js/src/jit/MCallOptimize.cpp | 75 ++++++++++++------- js/src/jit/MIR.cpp | 18 +++++ js/src/jit/MIR.h | 62 ++++++++++++++- js/src/jit/MOpcodes.h | 4 +- js/src/jit/arm/Assembler-arm.h | 2 + js/src/jit/arm64/Assembler-arm64.h | 2 + .../jit/mips-shared/Assembler-mips-shared.h | 4 + js/src/jit/none/MacroAssembler-none.h | 2 + js/src/jit/shared/LIR-shared.h | 49 ++++++++++-- js/src/jit/shared/LOpcodes-shared.h | 4 +- js/src/jit/x86-shared/Assembler-x86-shared.h | 27 +++++++ .../x86-shared/CodeGenerator-x86-shared.cpp | 20 +++++ .../jit/x86-shared/CodeGenerator-x86-shared.h | 2 + js/src/wasm/WasmBaselineCompile.cpp | 60 +++++++++++++-- js/src/wasm/WasmIonCompile.cpp | 40 +++++++++- js/src/wasm/WasmTypes.cpp | 25 +++++++ js/src/wasm/WasmTypes.h | 8 +- 20 files changed, 379 insertions(+), 50 deletions(-) diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index 95e6aaf459..f24f2a230c 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -806,6 +806,14 @@ enum class BarrierKind : uint32_t { enum ReprotectCode { Reprotect = true, DontReprotect = false }; +// Rounding modes for round instructions. +enum class RoundingMode { + Down, + Up, + NearestTiesToEven, + TowardsZero +}; + } // namespace jit } // namespace js diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index c0ae566ae7..0180e8518c 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1405,6 +1405,22 @@ LIRGenerator::visitRound(MRound* ins) define(lir, ins); } +void +LIRGenerator::visitNearbyInt(MNearbyInt* ins) +{ + MIRType inputType = ins->input()->type(); + MOZ_ASSERT(IsFloatingPointType(inputType)); + MOZ_ASSERT(ins->type() == inputType); + + LInstructionHelper<1, 1, 0>* lir; + if (inputType == MIRType::Double) + lir = new(alloc()) LNearbyInt(useRegisterAtStart(ins->input())); + else + lir = new(alloc()) LNearbyIntF(useRegisterAtStart(ins->input())); + + define(lir, ins); +} + void LIRGenerator::visitMinMax(MMinMax* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 499dc2b403..879592a0a0 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -137,6 +137,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitFloor(MFloor* ins); void visitCeil(MCeil* ins); void visitRound(MRound* ins); + void visitNearbyInt(MNearbyInt* ins); void visitMinMax(MMinMax* ins); void visitAbs(MAbs* ins); void visitClz(MClz* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index f07e69f4c8..14452edf33 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -923,20 +923,30 @@ IonBuilder::inlineMathFloor(CallInfo& callInfo) return InliningStatus_Inlined; } - if (IsFloatingPointType(argType) && returnType == MIRType::Int32) { - callInfo.setImplicitlyUsedUnchecked(); - MFloor* ins = MFloor::New(alloc(), callInfo.getArg(0)); - current->add(ins); - current->push(ins); - return InliningStatus_Inlined; - } + if (IsFloatingPointType(argType)) { + if (returnType == MIRType::Int32) { + callInfo.setImplicitlyUsedUnchecked(); + MFloor* ins = MFloor::New(alloc(), callInfo.getArg(0)); + current->add(ins); + current->push(ins); + return InliningStatus_Inlined; + } - if (IsFloatingPointType(argType) && returnType == MIRType::Double) { - callInfo.setImplicitlyUsedUnchecked(); - MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor, nullptr); - current->add(ins); - current->push(ins); - return InliningStatus_Inlined; + if (returnType == MIRType::Double) { + callInfo.setImplicitlyUsedUnchecked(); + + MInstruction* ins = nullptr; + if (MNearbyInt::HasAssemblerSupport(RoundingMode::Down)) { + ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType, RoundingMode::Down); + } else { + ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Floor, + /* cache */ nullptr); + } + + current->add(ins); + current->push(ins); + return InliningStatus_Inlined; + } } return InliningStatus_NotInlined; @@ -967,20 +977,30 @@ IonBuilder::inlineMathCeil(CallInfo& callInfo) return InliningStatus_Inlined; } - if (IsFloatingPointType(argType) && returnType == MIRType::Int32) { - callInfo.setImplicitlyUsedUnchecked(); - MCeil* ins = MCeil::New(alloc(), callInfo.getArg(0)); - current->add(ins); - current->push(ins); - return InliningStatus_Inlined; - } + if (IsFloatingPointType(argType)) { + if (returnType == MIRType::Int32) { + callInfo.setImplicitlyUsedUnchecked(); + MCeil* ins = MCeil::New(alloc(), callInfo.getArg(0)); + current->add(ins); + current->push(ins); + return InliningStatus_Inlined; + } - if (IsFloatingPointType(argType) && returnType == MIRType::Double) { - callInfo.setImplicitlyUsedUnchecked(); - MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil, nullptr); - current->add(ins); - current->push(ins); - return InliningStatus_Inlined; + if (returnType == MIRType::Double) { + callInfo.setImplicitlyUsedUnchecked(); + + MInstruction* ins = nullptr; + if (MNearbyInt::HasAssemblerSupport(RoundingMode::Up)) { + ins = MNearbyInt::New(alloc(), callInfo.getArg(0), argType, RoundingMode::Up); + } else { + ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Ceil, + /* cache */ nullptr); + } + + current->add(ins); + current->push(ins); + return InliningStatus_Inlined; + } } return InliningStatus_NotInlined; @@ -1045,7 +1065,8 @@ IonBuilder::inlineMathRound(CallInfo& callInfo) if (IsFloatingPointType(argType) && returnType == MIRType::Double) { callInfo.setImplicitlyUsedUnchecked(); - MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round, nullptr); + MMathFunction* ins = MMathFunction::New(alloc(), callInfo.getArg(0), MMathFunction::Round, + /* cache */ nullptr); current->add(ins); current->push(ins); return InliningStatus_Inlined; diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 2204307c69..10f8171c16 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -1272,6 +1272,18 @@ MAssertRange::printOpcode(GenericPrinter& out) const assertedRange()->dump(out); } +void MNearbyInt::printOpcode(GenericPrinter& out) const +{ + MDefinition::printOpcode(out); + const char* roundingModeStr = nullptr; + switch (roundingMode_) { + case RoundingMode::Up: roundingModeStr = "(up)"; break; + case RoundingMode::Down: roundingModeStr = "(down)"; break; + case RoundingMode::NearestTiesToEven: roundingModeStr = "(nearest ties even)"; break; + case RoundingMode::TowardsZero: roundingModeStr = "(towards zero)"; break; + } + out.printf(" %s", roundingModeStr); +} const char* MMathFunction::FunctionName(Function function) { @@ -1647,6 +1659,12 @@ MRound::trySpecializeFloat32(TempAllocator& alloc) specialization_ = MIRType::Float32; } +void +MNearbyInt::trySpecializeFloat32(TempAllocator& alloc) +{ + if (EnsureFloatInputOrConvert(this, alloc)) + specialization_ = MIRType::Float32; +} MTableSwitch* MTableSwitch::New(TempAllocator& alloc, MDefinition* ins, int32_t low, int32_t high) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index d2247b56eb..795a9bdc45 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -11064,7 +11064,7 @@ class MStringLength ALLOW_CLONE(MStringLength) }; -// Inlined version of Math.floor(). +// Inlined assembly for Math.floor(double | float32) -> int32. class MFloor : public MUnaryInstruction, public FloatingPointPolicy<0>::Data @@ -11105,7 +11105,7 @@ class MFloor ALLOW_CLONE(MFloor) }; -// Inlined version of Math.ceil(). +// Inlined version of Math.round(double | float32) -> int32. class MCeil : public MUnaryInstruction, public FloatingPointPolicy<0>::Data @@ -11146,7 +11146,7 @@ class MCeil ALLOW_CLONE(MCeil) }; -// Inlined version of Math.round(). +// Inlined version of Math.round(double | float32) -> int32. class MRound : public MUnaryInstruction, public FloatingPointPolicy<0>::Data @@ -11188,6 +11188,62 @@ class MRound ALLOW_CLONE(MRound) }; +// NearbyInt rounds the floating-point input to the nearest integer, according +// to the RoundingMode. +class MNearbyInt + : public MUnaryInstruction, + public FloatingPointPolicy<0>::Data +{ + RoundingMode roundingMode_; + + explicit MNearbyInt(MDefinition* num, MIRType resultType, RoundingMode roundingMode) + : MUnaryInstruction(classOpcode, num), + roundingMode_(roundingMode) + { + MOZ_ASSERT(HasAssemblerSupport(roundingMode)); + + MOZ_ASSERT(IsFloatingPointType(resultType)); + setResultType(resultType); + specialization_ = resultType; + + setMovable(); + } + + public: + INSTRUCTION_HEADER(NearbyInt) + TRIVIAL_NEW_WRAPPERS + + static bool HasAssemblerSupport(RoundingMode mode) { + return Assembler::HasRoundInstruction(mode); + } + + RoundingMode roundingMode() const { return roundingMode_; } + + AliasSet getAliasSet() const override { + return AliasSet::None(); + } + + bool isFloat32Commutative() const override { + return true; + } + void trySpecializeFloat32(TempAllocator& alloc) override; +#ifdef DEBUG + bool isConsistentFloat32Use(MUse* use) const override { + return true; + } +#endif + + bool congruentTo(const MDefinition* ins) const override { + return congruentIfOperandsEqual(ins) && + ins->toNearbyInt()->roundingMode() == roundingMode_; + } + + void printOpcode(GenericPrinter& out) const override; + + ALLOW_CLONE(MNearbyInt) +}; + + class MIteratorStart : public MUnaryInstruction, public BoxExceptPolicy<0, MIRType::Object>::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 00bc75f5f1..a5c090e123 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -242,7 +242,9 @@ namespace jit { _(Floor) \ _(Ceil) \ _(Round) \ - _(In) \ + _(NearbyInt) \ + _(InCache) \ + _(HasOwnCache) \ _(InstanceOf) \ _(CallInstanceOf) \ _(InterruptCheck) \ diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index 7c314a0c32..98d26131c9 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -1725,6 +1725,8 @@ class Assembler : public AssemblerShared return js::jit::SupportsSimd; } + static bool HasRoundInstruction(RoundingMode mode) { return false; } + protected: void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) { enoughMemory_ &= jumps_.append(RelativePatch(target.value, kind)); diff --git a/js/src/jit/arm64/Assembler-arm64.h b/js/src/jit/arm64/Assembler-arm64.h index 5a07900002..a2f412bd5f 100644 --- a/js/src/jit/arm64/Assembler-arm64.h +++ b/js/src/jit/arm64/Assembler-arm64.h @@ -290,6 +290,8 @@ class Assembler : public vixl::Assembler static bool SupportsUnalignedAccesses() { return true; } static bool SupportsSimd() { return js::jit::SupportsSimd; } + static bool HasRoundInstruction(RoundingMode mode) { return false; } + // Tracks a jump that is patchable after finalization. void addJumpRelocation(BufferOffset src, Relocation::Kind reloc); diff --git a/js/src/jit/mips-shared/Assembler-mips-shared.h b/js/src/jit/mips-shared/Assembler-mips-shared.h index 23fb60c95d..0aa9a40710 100644 --- a/js/src/jit/mips-shared/Assembler-mips-shared.h +++ b/js/src/jit/mips-shared/Assembler-mips-shared.h @@ -1247,6 +1247,10 @@ class AssemblerMIPSShared : public AssemblerShared return js::jit::SupportsSimd; } + static bool HasRoundInstruction(RoundingMode mode) { + return false; + } + protected: InstImm invertBranch(InstImm branch, BOffImm16 skipOffset); void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) { diff --git a/js/src/jit/none/MacroAssembler-none.h b/js/src/jit/none/MacroAssembler-none.h index b501f9a7d8..026b3e4bb0 100644 --- a/js/src/jit/none/MacroAssembler-none.h +++ b/js/src/jit/none/MacroAssembler-none.h @@ -192,6 +192,8 @@ class MacroAssemblerNone : public Assembler static bool SupportsSimd() { return false; } static bool SupportsUnalignedAccesses() { return false; } + static bool HasRoundInstruction(RoundingMode) { return false; } + void executableCopy(void*) { MOZ_CRASH(); } void copyJumpRelocationTable(uint8_t*) { MOZ_CRASH(); } void copyDataRelocationTable(uint8_t*) { MOZ_CRASH(); } diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 3e7b7bdb02..797a69ff29 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -6260,7 +6260,8 @@ class LStringLength : public LInstructionHelper<1, 1, 0> } }; -// Take the floor of a double precision number. Implements Math.floor(). +// Take the floor of a double precision number and converts it to an int32. +// Implements Math.floor(). class LFloor : public LInstructionHelper<1, 1, 0> { public: @@ -6271,7 +6272,9 @@ class LFloor : public LInstructionHelper<1, 1, 0> } }; -// Take the floor of a single precision number. Implements Math.floor(). + +// Take the floor of a single precision number and converts it to an int32. +// Implements Math.floor(). class LFloorF : public LInstructionHelper<1, 1, 0> { public: @@ -6282,7 +6285,8 @@ class LFloorF : public LInstructionHelper<1, 1, 0> } }; -// Take the ceiling of a double precision number. Implements Math.ceil(). +// Take the ceiling of a double precision number and converts it to an int32. +// Implements Math.ceil(). class LCeil : public LInstructionHelper<1, 1, 0> { public: @@ -6293,7 +6297,8 @@ class LCeil : public LInstructionHelper<1, 1, 0> } }; -// Take the ceiling of a single precision number. Implements Math.ceil(). +// Take the ceiling of a single precision number and converts it to an int32. +// Implements Math.ceil(). class LCeilF : public LInstructionHelper<1, 1, 0> { public: @@ -6304,7 +6309,8 @@ class LCeilF : public LInstructionHelper<1, 1, 0> } }; -// Round a double precision number. Implements Math.round(). +// Round a double precision number and converts it to an int32. +// Implements Math.round(). class LRound : public LInstructionHelper<1, 1, 1> { public: @@ -6323,7 +6329,8 @@ class LRound : public LInstructionHelper<1, 1, 1> } }; -// Round a single precision number. Implements Math.round(). +// Round a single precision number and converts it to an int32. +// Implements Math.round(). class LRoundF : public LInstructionHelper<1, 1, 1> { public: @@ -6342,6 +6349,36 @@ class LRoundF : public LInstructionHelper<1, 1, 1> } }; +// Rounds a double precision number accordingly to mir()->roundingMode(), +// and keeps a double output. +class LNearbyInt : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(NearbyInt) + + explicit LNearbyInt(const LAllocation& num) { + setOperand(0, num); + } + MNearbyInt* mir() const { + return mir_->toNearbyInt(); + } +}; + +// Rounds a single precision number accordingly to mir()->roundingMode(), +// and keeps a single output. +class LNearbyIntF : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(NearbyIntF) + + explicit LNearbyIntF(const LAllocation& num) { + setOperand(0, num); + } + MNearbyInt* mir() const { + return mir_->toNearbyInt(); + } +}; + // Load a function's call environment. class LFunctionEnvironment : public LInstructionHelper<1, 1, 0> { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index e400281eb4..048cdfaf21 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -329,7 +329,9 @@ _(CeilF) \ _(Round) \ _(RoundF) \ - _(In) \ + _(NearbyInt) \ + _(NearbyIntF) \ + _(InCache) \ _(InArray) \ _(InstanceOfO) \ _(InstanceOfV) \ diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index 1947caddb2..bafdf86834 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -1111,6 +1111,17 @@ class AssemblerX86Shared : public AssemblerShared static bool SupportsSimd() { return CPUInfo::IsSSE2Present(); } static bool HasAVX() { return CPUInfo::IsAVXPresent(); } + static bool HasRoundInstruction(RoundingMode mode) { + switch (mode) { + case RoundingMode::Up: + case RoundingMode::Down: + case RoundingMode::NearestTiesToEven: + case RoundingMode::TowardsZero: + return CPUInfo::IsSSE41Present(); + } + MOZ_CRASH("unexpected mode"); + } + void cmpl(Register rhs, Register lhs) { masm.cmpl_rr(rhs.encoding(), lhs.encoding()); } @@ -3349,6 +3360,22 @@ class AssemblerX86Shared : public AssemblerShared MOZ_ASSERT(HasSSE2()); masm.vsqrtss_rr(src1.encoding(), src0.encoding(), dest.encoding()); } + + static X86Encoding::RoundingMode + ToX86RoundingMode(RoundingMode mode) { + switch (mode) { + case RoundingMode::Up: + return X86Encoding::RoundUp; + case RoundingMode::Down: + return X86Encoding::RoundDown; + case RoundingMode::NearestTiesToEven: + return X86Encoding::RoundToNearest; + case RoundingMode::TowardsZero: + return X86Encoding::RoundToZero; + } + MOZ_CRASH("unexpected mode"); + } + void vroundsd(X86Encoding::RoundingMode mode, FloatRegister src1, FloatRegister src0, FloatRegister dest) { MOZ_ASSERT(HasSSE41()); masm.vroundsd_irr(mode, src1.encoding(), src0.encoding(), dest.encoding()); diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 704674a4cd..87dd165f65 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -2330,6 +2330,26 @@ CodeGeneratorX86Shared::visitRoundF(LRoundF* lir) masm.bind(&end); } +void +CodeGeneratorX86Shared::visitNearbyInt(LNearbyInt* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister output = ToFloatRegister(lir->output()); + + RoundingMode roundingMode = lir->mir()->roundingMode(); + masm.vroundsd(Assembler::ToX86RoundingMode(roundingMode), input, output, output); +} + +void +CodeGeneratorX86Shared::visitNearbyIntF(LNearbyIntF* lir) +{ + FloatRegister input = ToFloatRegister(lir->input()); + FloatRegister output = ToFloatRegister(lir->output()); + + RoundingMode roundingMode = lir->mir()->roundingMode(); + masm.vroundss(Assembler::ToX86RoundingMode(roundingMode), input, output, output); +} + void CodeGeneratorX86Shared::visitGuardShape(LGuardShape* guard) { diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index d990f662c4..6b86086a02 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -204,6 +204,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared virtual void visitCeilF(LCeilF* lir); virtual void visitRound(LRound* lir); virtual void visitRoundF(LRoundF* lir); + virtual void visitNearbyInt(LNearbyInt* lir); + virtual void visitNearbyIntF(LNearbyIntF* lir); virtual void visitGuardShape(LGuardShape* guard); virtual void visitGuardObjectGroup(LGuardObjectGroup* guard); virtual void visitGuardClass(LGuardClass* guard); diff --git a/js/src/wasm/WasmBaselineCompile.cpp b/js/src/wasm/WasmBaselineCompile.cpp index 0476c9dd38..ffaa9edec1 100644 --- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -3777,6 +3777,36 @@ ScratchI32 tmp(*this); return true; } + [[nodiscard]] bool + supportsRoundInstruction(RoundingMode mode) + { +#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) + return Assembler::HasRoundInstruction(mode); +#else + return false; +#endif + } + + void + roundF32(RoundingMode roundingMode, RegF32 f0) + { +#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) + masm.vroundss(Assembler::ToX86RoundingMode(roundingMode), f0, f0, f0); +#else + MOZ_CRASH("NYI"); +#endif + } + + void + roundF64(RoundingMode roundingMode, RegF64 f0) + { +#if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86) + masm.vroundsd(Assembler::ToX86RoundingMode(roundingMode), f0, f0, f0); +#else + MOZ_CRASH("NYI"); +#endif + } + //////////////////////////////////////////////////////////// // Generally speaking, ABOVE this point there should be no value @@ -4028,13 +4058,7 @@ ScratchI32 tmp(*this); #endif void emitReinterpretI32AsF32(); void emitReinterpretI64AsF64(); -#if defined(JS_CODEGEN_LOONGARCH64) - [[nodiscard]] bool emitAtomicLoad(); - [[nodiscard]] bool emitAtomicStore(); - [[nodiscard]] bool emitAtomicBinOp(); - [[nodiscard]] bool emitAtomicCompareExchange(); - [[nodiscard]] bool emitAtomicExchange(); -#endif + void emitRound(RoundingMode roundingMode, ValType operandType); [[nodiscard]] bool emitGrowMemory(); [[nodiscard]] bool emitCurrentMemory(); }; @@ -6069,6 +6093,22 @@ BaseCompiler::emitCommonMathCall(uint32_t lineOrBytecode, SymbolicAddress callee return true; } +void +BaseCompiler::emitRound(RoundingMode roundingMode, ValType operandType) +{ + if (operandType == ValType::F32) { + RegF32 f0 = popF32(); + roundF32(roundingMode, f0); + pushF32(f0); + } else if (operandType == ValType::F64) { + RegF64 f0 = popF64(); + roundF64(roundingMode, f0); + pushF64(f0); + } else { + MOZ_CRASH("unexpected type"); + } +} + bool BaseCompiler::emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandType) { @@ -6077,6 +6117,12 @@ BaseCompiler::emitUnaryMathBuiltinCall(SymbolicAddress callee, ValType operandTy if (deadCode_) return true; + RoundingMode roundingMode; + if (IsRoundingFunction(callee, &roundingMode) && supportsRoundInstruction(roundingMode)) { + emitRound(roundingMode, operandType); + return true; + } + return emitCommonMathCall(lineOrBytecode, callee, operandType == ValType::F32 ? SigF_ : SigD_, operandType == ValType::F32 ? ExprType::F32 : ExprType::F64); diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index e25cbefa43..b64c13df37 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -385,6 +385,16 @@ class FunctionCompiler return ins; } + MDefinition* nearbyInt(MDefinition* input, RoundingMode roundingMode) + { + if (inDeadCode()) + return nullptr; + + auto* ins = MNearbyInt::New(alloc(), input, input->type(), roundingMode); + curBlock_->add(ins); + return ins; + } + MDefinition* minMax(MDefinition* lhs, MDefinition* rhs, MIRType type, bool isMax) { if (inDeadCode()) return nullptr; @@ -2188,19 +2198,41 @@ EmitTeeStoreWithCoercion(FunctionCompiler& f, ValType resultType, Scalar::Type v return true; } +static bool +TryInlineUnaryBuiltin(FunctionCompiler& f, SymbolicAddress callee, MDefinition* input) +{ + if (!input) + return false; + + MOZ_ASSERT(IsFloatingPointType(input->type())); + + RoundingMode mode; + if (!IsRoundingFunction(callee, &mode)) + return false; + + if (!MNearbyInt::HasAssemblerSupport(mode)) + return false; + + f.iter().setResult(f.nearbyInt(input, mode)); + return true; +} + static bool EmitUnaryMathBuiltinCall(FunctionCompiler& f, SymbolicAddress callee, ValType operandType) { uint32_t lineOrBytecode = f.readCallSiteLineOrBytecode(); - CallCompileState call(f, lineOrBytecode); - if (!f.startCall(&call)) - return false; - MDefinition* input; if (!f.iter().readUnary(operandType, &input)) return false; + if (TryInlineUnaryBuiltin(f, callee, input)) + return true; + + CallCompileState call(f, lineOrBytecode); + if (!f.startCall(&call)) + return false; + if (!f.passArg(input, operandType, &call)) return false; diff --git a/js/src/wasm/WasmTypes.cpp b/js/src/wasm/WasmTypes.cpp index 439b40a06d..0bca947732 100644 --- a/js/src/wasm/WasmTypes.cpp +++ b/js/src/wasm/WasmTypes.cpp @@ -329,6 +329,31 @@ FuncCast(F* pf, ABIFunctionType type) return pv; } +bool +wasm::IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode) +{ + switch (callee) { + case SymbolicAddress::FloorD: + case SymbolicAddress::FloorF: + *mode = jit::RoundingMode::Down; + return true; + case SymbolicAddress::CeilD: + case SymbolicAddress::CeilF: + *mode = jit::RoundingMode::Up; + return true; + case SymbolicAddress::TruncD: + case SymbolicAddress::TruncF: + *mode = jit::RoundingMode::TowardsZero; + return true; + case SymbolicAddress::NearbyIntD: + case SymbolicAddress::NearbyIntF: + *mode = jit::RoundingMode::NearestTiesToEven; + return true; + default: + return false; + } +} + void* wasm::AddressOf(SymbolicAddress imm, ExclusiveContext* cx) { diff --git a/js/src/wasm/WasmTypes.h b/js/src/wasm/WasmTypes.h index 6676b92c52..c026fbd396 100644 --- a/js/src/wasm/WasmTypes.h +++ b/js/src/wasm/WasmTypes.h @@ -41,7 +41,10 @@ namespace js { class PropertyName; -namespace jit { struct BaselineScript; } +namespace jit { + struct BaselineScript; + enum class RoundingMode; +} // This is a widespread header, so lets keep out the core wasm impl types. @@ -885,6 +888,9 @@ enum class SymbolicAddress Limit }; +bool +IsRoundingFunction(SymbolicAddress callee, jit::RoundingMode* mode); + void* AddressOf(SymbolicAddress imm, ExclusiveContext* cx);