tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 3d31165c1d8375f668b5aa91bb01a5241e38915e
parent d18a1d8dfe43eaf4ec9033dcf7c9bed1f5d7ff3e
Author: André Bargull <andre.bargull@gmail.com>
Date:   Fri, 17 Oct 2025 11:25:46 +0000

Bug 1994192: Simplify wasm truncating instructions for riscv64. r=spidermonkey-reviewers,iain

The riscv64 rounding instructions can handle all success cases, so we only need
to jump to the OOL path for non-saturating instructions to execute the correct
trap.

Differential Revision: https://phabricator.services.mozilla.com/D268555

Diffstat:
Mjs/src/jit/riscv64/CodeGenerator-riscv64.cpp | 70+++++++++++++++++++++++++++++++++++++++++++---------------------------
Mjs/src/jit/riscv64/MacroAssembler-riscv64.cpp | 203++++++++++++++++++++++++-------------------------------------------------------
2 files changed, 104 insertions(+), 169 deletions(-)

diff --git a/js/src/jit/riscv64/CodeGenerator-riscv64.cpp b/js/src/jit/riscv64/CodeGenerator-riscv64.cpp @@ -280,6 +280,9 @@ void CodeGeneratorRiscv64::visitOutOfLineTableSwitch( void CodeGeneratorRiscv64::visitOutOfLineWasmTruncateCheck( OutOfLineWasmTruncateCheck* ool) { + MOZ_ASSERT(!ool->isSaturating(), + "saturating case doesn't require an OOL path"); + FloatRegister input = ool->input(); Register output = ool->output(); Register64 output64 = ool->output64(); @@ -308,6 +311,9 @@ void CodeGeneratorRiscv64::visitOutOfLineWasmTruncateCheck( } else { MOZ_CRASH("unexpected type"); } + + // The OOL path is only used to execute the correct trap. + MOZ_ASSERT(!oolRejoin->bound(), "ool path doesn't return"); } void CodeGenerator::visitBox(LBox* box) { @@ -597,13 +603,20 @@ void CodeGenerator::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir) { MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32); - auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output); - addOutOfLineCode(ool, mir); - - Label* oolEntry = ool->entry(); - Label* oolRejoin = ool->rejoin(); bool isSaturating = mir->isSaturating(); + // RISCV saturating instructions don't require an OOL path. + OutOfLineWasmTruncateCheck* ool = nullptr; + Label* oolEntry = nullptr; + Label* oolRejoin = nullptr; + if (!isSaturating) { + ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output); + addOutOfLineCode(ool, mir); + + oolEntry = ool->entry(); + oolRejoin = ool->rejoin(); + } + if (fromType == MIRType::Double) { if (mir->isUnsigned()) { masm.wasmTruncateDoubleToUInt64(input, output, isSaturating, oolEntry, @@ -621,6 +634,10 @@ void CodeGenerator::visitWasmTruncateToInt64(LWasmTruncateToInt64* lir) { oolRejoin, InvalidFloatReg); } } + + // RISCV can handle all success case. The OOL path is only used to execute + // the correct trap. + MOZ_ASSERT(!ool || !ool->rejoin()->bound(), "ool path doesn't return"); } void CodeGenerator::visitInt64ToFloatingPoint(LInt64ToFloatingPoint* lir) { @@ -1621,36 +1638,35 @@ void CodeGenerator::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir) { MOZ_ASSERT(fromType == MIRType::Double || fromType == MIRType::Float32); - auto* ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output); - addOutOfLineCode(ool, mir); + bool isSaturating = mir->isSaturating(); - Label* oolEntry = ool->entry(); - if (mir->isUnsigned()) { - if (fromType == MIRType::Double) { - masm.wasmTruncateDoubleToUInt32(input, output, mir->isSaturating(), - oolEntry); - } else if (fromType == MIRType::Float32) { - masm.wasmTruncateFloat32ToUInt32(input, output, mir->isSaturating(), - oolEntry); - } else { - MOZ_CRASH("unexpected type"); - } + // RISCV saturating instructions don't require an OOL path. + OutOfLineWasmTruncateCheck* ool = nullptr; + Label* oolEntry = nullptr; + if (!isSaturating) { + ool = new (alloc()) OutOfLineWasmTruncateCheck(mir, input, output); + addOutOfLineCode(ool, mir); - masm.bind(ool->rejoin()); - return; + oolEntry = ool->entry(); } if (fromType == MIRType::Double) { - masm.wasmTruncateDoubleToInt32(input, output, mir->isSaturating(), - oolEntry); - } else if (fromType == MIRType::Float32) { - masm.wasmTruncateFloat32ToInt32(input, output, mir->isSaturating(), - oolEntry); + if (mir->isUnsigned()) { + masm.wasmTruncateDoubleToUInt32(input, output, isSaturating, oolEntry); + } else { + masm.wasmTruncateDoubleToInt32(input, output, isSaturating, oolEntry); + } } else { - MOZ_CRASH("unexpected type"); + if (mir->isUnsigned()) { + masm.wasmTruncateFloat32ToUInt32(input, output, isSaturating, oolEntry); + } else { + masm.wasmTruncateFloat32ToInt32(input, output, isSaturating, oolEntry); + } } - masm.bind(ool->rejoin()); + // RISCV can handle all success case. The OOL path is only used to execute + // the correct trap. + MOZ_ASSERT(!ool || !ool->rejoin()->bound(), "ool path doesn't return"); } void CodeGenerator::visitCopySignF(LCopySignF* ins) { diff --git a/js/src/jit/riscv64/MacroAssembler-riscv64.cpp b/js/src/jit/riscv64/MacroAssembler-riscv64.cpp @@ -3698,144 +3698,55 @@ void MacroAssembler::nearbyIntFloat32(RoundingMode mode, FloatRegister src, void MacroAssembler::oolWasmTruncateCheckF32ToI32( FloatRegister input, Register output, TruncFlags flags, const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { + MOZ_ASSERT(!(flags & TRUNC_SATURATING)); + Label notNaN; - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - CompareIsNotNanF32(scratch, input, input); - ma_branch(&notNaN, Equal, scratch, Operand(1)); + BranchFloat32(Assembler::DoubleOrdered, input, input, &notNaN, ShortJump); wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc); bind(&notNaN); - Label isOverflow; - const float two_31 = -float(INT32_MIN); - ScratchFloat32Scope fpscratch(*this); - if (flags & TRUNC_UNSIGNED) { - loadConstantFloat32(two_31 * 2, fpscratch); - ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, - fpscratch); - ma_branch(&isOverflow, Equal, scratch, Operand(1)); - loadConstantFloat32(-1.0f, fpscratch); - ma_compareF32(scratch, Assembler::DoubleGreaterThan, input, fpscratch); - ma_b(scratch, Imm32(1), rejoin, Equal); - } else { - loadConstantFloat32(two_31, fpscratch); - ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, - fpscratch); - ma_branch(&isOverflow, Equal, scratch, Operand(1)); - loadConstantFloat32(-two_31, fpscratch); - ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, - fpscratch); - ma_b(scratch, Imm32(1), rejoin, Equal); - } - bind(&isOverflow); wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc); } void MacroAssembler::oolWasmTruncateCheckF64ToI32( FloatRegister input, Register output, TruncFlags flags, const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { + MOZ_ASSERT(!(flags & TRUNC_SATURATING)); + Label notNaN; - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - CompareIsNotNanF64(scratch, input, input); - ma_branch(&notNaN, Equal, scratch, Operand(1)); + BranchFloat64(Assembler::DoubleOrdered, input, input, &notNaN, ShortJump); wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc); bind(&notNaN); - Label isOverflow; - const double two_31 = -double(INT32_MIN); - ScratchDoubleScope fpscratch(*this); - if (flags & TRUNC_UNSIGNED) { - loadConstantDouble(two_31 * 2, fpscratch); - ma_compareF64(scratch, Assembler::DoubleGreaterThanOrEqual, input, - fpscratch); - ma_branch(&isOverflow, Equal, scratch, Operand(1)); - loadConstantDouble(-1.0, fpscratch); - ma_compareF64(scratch, Assembler::DoubleGreaterThan, input, fpscratch); - ma_b(scratch, Imm32(1), rejoin, Equal); - } else { - loadConstantDouble(two_31, fpscratch); - ma_compareF64(scratch, Assembler::DoubleGreaterThanOrEqual, input, - fpscratch); - ma_branch(&isOverflow, Equal, scratch, Operand(1)); - loadConstantDouble(-two_31 - 1, fpscratch); - ma_compareF64(scratch, Assembler::DoubleGreaterThan, input, fpscratch); - ma_b(scratch, Imm32(1), rejoin, Equal); - } - bind(&isOverflow); wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc); } void MacroAssembler::oolWasmTruncateCheckF32ToI64( FloatRegister input, Register64 output, TruncFlags flags, const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { + MOZ_ASSERT(!(flags & TRUNC_SATURATING)); + Label notNaN; - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - CompareIsNotNanF32(scratch, input, input); - ma_branch(&notNaN, Equal, scratch, Operand(1)); + BranchFloat32(Assembler::DoubleOrdered, input, input, &notNaN, ShortJump); wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc); bind(&notNaN); - Label isOverflow; - const float two_63 = -float(INT64_MIN); - ScratchFloat32Scope fpscratch(*this); - if (flags & TRUNC_UNSIGNED) { - loadConstantFloat32(two_63 * 2, fpscratch); - ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, - fpscratch); - ma_branch(&isOverflow, Equal, scratch, Operand(1)); - loadConstantFloat32(-1.0f, fpscratch); - ma_compareF32(scratch, Assembler::DoubleGreaterThan, input, fpscratch); - ma_b(scratch, Imm32(1), rejoin, Equal); - } else { - loadConstantFloat32(two_63, fpscratch); - ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, - fpscratch); - ma_branch(&isOverflow, Equal, scratch, Operand(1)); - loadConstantFloat32(-two_63, fpscratch); - ma_compareF32(scratch, Assembler::DoubleGreaterThanOrEqual, input, - fpscratch); - ma_b(scratch, Imm32(1), rejoin, Equal); - } - bind(&isOverflow); wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc); } void MacroAssembler::oolWasmTruncateCheckF64ToI64( FloatRegister input, Register64 output, TruncFlags flags, const wasm::TrapSiteDesc& trapSiteDesc, Label* rejoin) { + MOZ_ASSERT(!(flags & TRUNC_SATURATING)); + Label notNaN; - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - CompareIsNotNanF64(scratch, input, input); - ma_branch(&notNaN, Equal, scratch, Operand(1)); + BranchFloat64(Assembler::DoubleOrdered, input, input, &notNaN, ShortJump); wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc); bind(&notNaN); - Label isOverflow; - const double two_63 = -double(INT64_MIN); - ScratchDoubleScope fpscratch(*this); - if (flags & TRUNC_UNSIGNED) { - loadConstantDouble(two_63 * 2, fpscratch); - ma_compareF64(scratch, Assembler::DoubleGreaterThanOrEqual, input, - fpscratch); - ma_branch(&isOverflow, Equal, scratch, Operand(1)); - loadConstantDouble(-1.0, fpscratch); - ma_compareF64(scratch, Assembler::DoubleGreaterThan, input, fpscratch); - ma_b(scratch, Imm32(1), rejoin, Equal); - } else { - loadConstantDouble(two_63, fpscratch); - ma_compareF64(scratch, Assembler::DoubleGreaterThanOrEqual, input, - fpscratch); - ma_branch(&isOverflow, Equal, scratch, Operand(1)); - loadConstantDouble(-two_63, fpscratch); - ma_compareF64(scratch, Assembler::DoubleGreaterThan, input, fpscratch); - ma_b(scratch, Imm32(1), rejoin, Equal); - } - bind(&isOverflow); wasmTrap(wasm::Trap::IntegerOverflow, trapSiteDesc); } + void MacroAssembler::patchCallToNop(uint8_t* call) { uint32_t* p = reinterpret_cast<uint32_t*>(call) - 7; *reinterpret_cast<Instr*>(p) = kNopByte; @@ -4606,47 +4517,50 @@ void MacroAssembler::wasmStoreI64(const wasm::MemoryAccessDesc& access, void MacroAssemblerRiscv64::Clear_if_nan_d(Register rd, FPURegister fs) { UseScratchRegisterScope temps(this); Register scratch = temps.Acquire(); - Label no_nan; + feq_d(scratch, fs, fs); - bnez(scratch, &no_nan); - mv(rd, zero_reg); - bind(&no_nan); + neg(scratch, scratch); + and_(rd, rd, scratch); } void MacroAssemblerRiscv64::Clear_if_nan_s(Register rd, FPURegister fs) { UseScratchRegisterScope temps(this); Register scratch = temps.Acquire(); - Label no_nan; + feq_s(scratch, fs, fs); - bnez(scratch, &no_nan); - mv(rd, zero_reg); - bind(&no_nan); + neg(scratch, scratch); + and_(rd, rd, scratch); } void MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, bool isSaturating, Label* oolEntry) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_w_d(output, input, scratch); if (isSaturating) { + Trunc_w_d(output, input); Clear_if_nan_d(output, input); } else { - ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + + Trunc_l_d(output, input); + + // Sign extend lower 32 bits to test if the result isn't an Int32. + move32SignExtendToPtr(output, scratch); + branchPtr(Assembler::NotEqual, output, scratch, oolEntry); } } void MacroAssembler::wasmTruncateDoubleToInt64( FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, Label* oolRejoin, FloatRegister tempDouble) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_l_d(output.reg, input, scratch); if (isSaturating) { - bind(oolRejoin); + Trunc_l_d(output.reg, input); Clear_if_nan_d(output.reg, input); } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Trunc_l_d(output.reg, input, scratch); ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); } } @@ -4655,12 +4569,13 @@ void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, bool isSaturating, Label* oolEntry) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_uw_d(output, input, scratch); if (isSaturating) { + Trunc_uw_d(output, input); Clear_if_nan_d(output, input); } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Trunc_uw_d(output, input, scratch); ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); } } @@ -4668,13 +4583,13 @@ void MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, void MacroAssembler::wasmTruncateDoubleToUInt64( FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, Label* oolRejoin, FloatRegister tempDouble) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_ul_d(output.reg, input, scratch); if (isSaturating) { - bind(oolRejoin); + Trunc_ul_d(output.reg, input); Clear_if_nan_d(output.reg, input); } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Trunc_ul_d(output.reg, input, scratch); ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); } } @@ -4683,27 +4598,31 @@ void MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, Register output, bool isSaturating, Label* oolEntry) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_w_s(output, input, scratch); if (isSaturating) { + Trunc_w_s(output, input); Clear_if_nan_s(output, input); } else { - ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + + Trunc_l_s(output, input, scratch); + + // Sign extend lower 32 bits to test if the result isn't an Int32. + move32SignExtendToPtr(output, scratch); + branchPtr(Assembler::NotEqual, output, scratch, oolEntry); } } void MacroAssembler::wasmTruncateFloat32ToInt64( FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, Label* oolRejoin, FloatRegister tempFloat) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_l_s(output.reg, input, scratch); - if (isSaturating) { - bind(oolRejoin); + Trunc_l_s(output.reg, input); Clear_if_nan_s(output.reg, input); } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Trunc_l_s(output.reg, input, scratch); ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); } } @@ -4712,12 +4631,13 @@ void MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, bool isSaturating, Label* oolEntry) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_uw_s(output, input, scratch); if (isSaturating) { + Trunc_uw_s(output, input); Clear_if_nan_s(output, input); } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Trunc_uw_s(output, input, scratch); ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); } } @@ -4725,14 +4645,13 @@ void MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, void MacroAssembler::wasmTruncateFloat32ToUInt64( FloatRegister input, Register64 output, bool isSaturating, Label* oolEntry, Label* oolRejoin, FloatRegister tempFloat) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_ul_s(output.reg, input, scratch); - if (isSaturating) { - bind(oolRejoin); + Trunc_ul_s(output.reg, input); Clear_if_nan_s(output.reg, input); } else { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + Trunc_ul_s(output.reg, input, scratch); ma_b(scratch, Imm32(0), oolEntry, Assembler::Equal); } }