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:
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(¬NaN, Equal, scratch, Operand(1));
+ BranchFloat32(Assembler::DoubleOrdered, input, input, ¬NaN, ShortJump);
wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc);
bind(¬NaN);
- 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(¬NaN, Equal, scratch, Operand(1));
+ BranchFloat64(Assembler::DoubleOrdered, input, input, ¬NaN, ShortJump);
wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc);
bind(¬NaN);
- 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(¬NaN, Equal, scratch, Operand(1));
+ BranchFloat32(Assembler::DoubleOrdered, input, input, ¬NaN, ShortJump);
wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc);
bind(¬NaN);
- 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(¬NaN, Equal, scratch, Operand(1));
+ BranchFloat64(Assembler::DoubleOrdered, input, input, ¬NaN, ShortJump);
wasmTrap(wasm::Trap::InvalidConversionToInteger, trapSiteDesc);
bind(¬NaN);
- 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);
}
}