tor-browser

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

commit 9af98b31d66efbabb08ca8a8b3965134043895f6
parent 4a9b91c091b5512f9895b72c97663ade8be190a4
Author: André Bargull <andre.bargull@gmail.com>
Date:   Thu, 23 Oct 2025 09:56:09 +0000

Bug 1995494: Optimise branch truncation operations on riscv64. r=spidermonkey-reviewers,iain

Port D234203 to riscv64, which adds an infallible `truncateFloat32ModUint32` method.

Also change `branchTruncateDouble[MaybeModUint32]` and `branchTruncateFloat32ToInt32`
to perform manual error detection, so that `NaN` is supported. See also the ARM64
implementation for comparison.

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

Diffstat:
Mjs/src/jit/riscv64/CodeGenerator-riscv64.cpp | 9+++++----
Mjs/src/jit/riscv64/Lowering-riscv64.cpp | 3+--
Mjs/src/jit/riscv64/MacroAssembler-riscv64-inl.h | 66++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mjs/src/jit/riscv64/MacroAssembler-riscv64.cpp | 25+++++++++++++++++++++++++
Mjs/src/jit/riscv64/MacroAssembler-riscv64.h | 2++
5 files changed, 85 insertions(+), 20 deletions(-)

diff --git a/js/src/jit/riscv64/CodeGenerator-riscv64.cpp b/js/src/jit/riscv64/CodeGenerator-riscv64.cpp @@ -1614,8 +1614,8 @@ void CodeGenerator::visitTruncateDToInt32(LTruncateDToInt32* ins) { } void CodeGenerator::visitTruncateFToInt32(LTruncateFToInt32* ins) { - emitTruncateFloat32(ToFloatRegister(ins->input()), ToRegister(ins->output()), - ins->mir()); + masm.truncateFloat32ModUint32(ToFloatRegister(ins->input()), + ToRegister(ins->output())); } void CodeGenerator::visitWasmBuiltinTruncateDToInt32( @@ -1626,8 +1626,9 @@ void CodeGenerator::visitWasmBuiltinTruncateDToInt32( void CodeGenerator::visitWasmBuiltinTruncateFToInt32( LWasmBuiltinTruncateFToInt32* lir) { - emitTruncateFloat32(ToFloatRegister(lir->input()), ToRegister(lir->output()), - lir->mir()); + MOZ_ASSERT(lir->instance()->isBogus(), "instance not used for riscv64"); + masm.truncateFloat32ModUint32(ToFloatRegister(lir->input()), + ToRegister(lir->output())); } void CodeGenerator::visitWasmTruncateToInt32(LWasmTruncateToInt32* lir) { diff --git a/js/src/jit/riscv64/Lowering-riscv64.cpp b/js/src/jit/riscv64/Lowering-riscv64.cpp @@ -424,8 +424,7 @@ void LIRGeneratorRiscv64::lowerWasmBuiltinTruncateToInt32( } define(new (alloc()) LWasmBuiltinTruncateFToInt32( - useRegister(opd), useFixed(ins->instance(), InstanceReg), - LDefinition::BogusTemp()), + useRegister(opd), LAllocation(), LDefinition::BogusTemp()), ins); } diff --git a/js/src/jit/riscv64/MacroAssembler-riscv64-inl.h b/js/src/jit/riscv64/MacroAssembler-riscv64-inl.h @@ -1242,33 +1242,71 @@ void MacroAssembler::branchTruncateDoubleMaybeModUint32(FloatRegister src, Register dest, Label* fail) { UseScratchRegisterScope temps(this); + + // Convert scalar to signed 64-bit fixed-point, rounding toward zero. + // In the case of overflow or NaN, the output is saturated. + // In the case of -0, the output is zero. + Trunc_l_d(dest, src); + + // Zero if NaN, so we don't take the failure path below. + Clear_if_nan_d(dest, src); + + // Unsigned subtraction of INT64_MAX returns 1 resp. 0 for INT64_{MIN,MAX}. Register scratch = temps.Acquire(); - Trunc_w_d(dest, src, scratch); - ma_b(scratch, Imm32(0), fail, Assembler::Equal); + ma_li(scratch, Imm64(0x7fff'ffff'ffff'ffff)); + ma_sub64(scratch, dest, scratch); + + // Fail if the result is saturated. + branchPtr(Assembler::BelowOrEqual, scratch, ImmWord(1), fail); + + // Clear upper 32 bits. + move32SignExtendToPtr(dest, dest); } void MacroAssembler::branchTruncateDoubleToInt32(FloatRegister src, Register dest, Label* fail) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_w_d(dest, src, scratch); - ma_b(scratch, Imm32(0), fail, Assembler::Equal); + // Convert scalar to signed 64-bit fixed-point, rounding toward zero. + // In the case of overflow or NaN, the output is saturated. + // In the case of -0, the output is zero. + Trunc_l_d(dest, src); + + // Zero if NaN, so we don't take the failure path below. + Clear_if_nan_d(dest, src); + + // Sign extend lower 32 bits to test if the result isn't an Int32. + { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + + move32SignExtendToPtr(dest, scratch); + branchPtr(Assembler::NotEqual, dest, scratch, fail); + } } void MacroAssembler::branchTruncateFloat32MaybeModUint32(FloatRegister src, Register dest, Label* fail) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_w_s(dest, src, scratch); - ma_b(scratch, Imm32(0), fail, Assembler::Equal); + // Infallible operation on riscv64. + truncateFloat32ModUint32(src, dest); } void MacroAssembler::branchTruncateFloat32ToInt32(FloatRegister src, Register dest, Label* fail) { - UseScratchRegisterScope temps(this); - Register scratch = temps.Acquire(); - Trunc_w_s(dest, src, scratch); - ma_b(scratch, Imm32(0), fail, Assembler::Equal); + // Convert scalar to signed 64-bit fixed-point, rounding toward zero. + // In the case of overflow or NaN, the output is saturated. + // In the case of -0, the output is zero. + Trunc_l_s(dest, src); + + // Zero if NaN, so we don't take the failure path below. + Clear_if_nan_s(dest, src); + + // Sign extend lower 32 bits to test if the result isn't an Int32. + { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + + move32SignExtendToPtr(dest, scratch); + branchPtr(Assembler::NotEqual, dest, scratch, fail); + } } void MacroAssembler::branchInt64NotInPtrRange(Register64 src, Label* label) { diff --git a/js/src/jit/riscv64/MacroAssembler-riscv64.cpp b/js/src/jit/riscv64/MacroAssembler-riscv64.cpp @@ -1083,6 +1083,31 @@ void MacroAssemblerRiscv64Compat::convertInt32ToFloat32(const Address& src, fcvt_s_w(dest, scratch); } +void MacroAssemblerRiscv64Compat::truncateFloat32ModUint32(FloatRegister src, + Register dest) { + UseScratchRegisterScope temps(this); + Register scratch = temps.Acquire(); + + // Convert scalar to signed 64-bit fixed-point, rounding toward zero. + // In the case of overflow or NaN, the output is saturated. + // In the case of -0, the output is zero. + Trunc_l_s(dest, src); + + // Unsigned subtraction of INT64_MAX returns 1 resp. 0 for INT64_{MIN,MAX}. + ma_li(scratch, Imm64(0x7fff'ffff'ffff'ffff)); + ma_sub64(scratch, dest, scratch); + + // If scratch u< 2, then scratch = 0; else scratch = -1. + ma_sltu(scratch, scratch, Imm32(2)); + ma_add32(scratch, scratch, Imm32(-1)); + + // Clear |dest| if the truncation result was saturated. + ma_and(dest, dest, scratch); + + // Clear upper 32 bits. + SignExtendWord(dest, dest); +} + void MacroAssemblerRiscv64Compat::movq(Register rj, Register rd) { mv(rd, rj); } // Memory. diff --git a/js/src/jit/riscv64/MacroAssembler-riscv64.h b/js/src/jit/riscv64/MacroAssembler-riscv64.h @@ -637,6 +637,8 @@ class MacroAssemblerRiscv64Compat : public MacroAssemblerRiscv64 { MOZ_CRASH("Not supported for this target"); } + void truncateFloat32ModUint32(FloatRegister src, Register dest); + void movq(Register rj, Register rd); void computeEffectiveAddress(const Address& address, Register dest) {