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:
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) {