diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index e8f750d8f9e9dd..cca0f36aa403d3 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -868,6 +868,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) genCatchArg(treeNode); break; + case GT_CKFINITE: + genCkfinite(treeNode); + break; + default: #ifdef DEBUG if (JitConfig.JitWasmNyiToR2RUnsupported()) @@ -1845,6 +1849,70 @@ void CodeGen::genCodeForBitCast(GenTreeOp* tree) WasmProduceReg(tree); } +//------------------------------------------------------------------------ +// genCkfinite: Generate code for ckfinite opcode. +// +// Arguments: +// treeNode - The GT_CKFINITE node +// +// Notes: +// The operand is expected to be marked MultiplyUsed so that codegen can +// re-use its value (via "local.get") after the check. +// +void CodeGen::genCkfinite(GenTree* treeNode) +{ + assert(treeNode->OperIs(GT_CKFINITE)); + + GenTree* op1 = treeNode->AsOp()->gtOp1; + var_types targetType = treeNode->TypeGet(); + assert(varTypeIsFloating(targetType)); + + // Push the operand value on the wasm stack. Because op1 was flagged as + // MultiplyUsed during lowering, "WasmProduceReg" will tee it into a + // temporary local so we can re-read it for the exponent check. + // + genConsumeOperands(treeNode->AsOp()); + + // Re-read the operand to feed the finiteness check. + // + regNumber op1Reg = GetMultiUseOperandReg(op1); + emitter* emit = GetEmitter(); + + // Compute "!(|x| < +Inf)". This is true for NaN and +/-Inf, false for + // every finite value. We rely on wasm's IEEE-754 comparison semantics + // where any comparison involving a NaN (other than "ne") returns 0. + // + // Note: IF_F32/IF_F64 both expect the +Inf constant as a double bit + // pattern (IF_F32 reinterprets and truncates to float during emission). + // + const int64_t infBits = 0x7FF0000000000000LL; + if (targetType == TYP_FLOAT) + { + emit->emitIns_I(INS_local_get, EA_4BYTE, WasmRegToIndex(op1Reg)); + emit->emitIns(INS_f32_abs); + emit->emitIns_I(INS_f32_const, EA_4BYTE, infBits); + emit->emitIns(INS_f32_lt); + } + else + { + assert(targetType == TYP_DOUBLE); + emit->emitIns_I(INS_local_get, EA_8BYTE, WasmRegToIndex(op1Reg)); + emit->emitIns(INS_f64_abs); + emit->emitIns_I(INS_f64_const, EA_8BYTE, infBits); + emit->emitIns(INS_f64_lt); + } + emit->emitIns(INS_i32_eqz); + + // If "!(|x| < +Inf)", the value is NaN or +/-Inf; throw. + // + genJumpToThrowHlpBlk(SCK_ARITH_EXCPN); + + // The operand value is still on the wasm stack from genConsumeOperands; + // produce it as the result of the GT_CKFINITE node. + // + WasmProduceReg(treeNode); +} + //------------------------------------------------------------------------ // genCodeForNegNot: Generate code for a neg/not // diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index b122b8de775259..165985601485f4 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -710,6 +710,10 @@ GenTree* Lowering::LowerNode(GenTree* node) case GT_INDEX_ADDR: LowerIndexAddr(node->AsIndexAddr()); break; + + case GT_CKFINITE: + LowerCkfinite(node->AsOp()); + break; #endif // defined(TARGET_WASM) default: diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index e7c59dca277742..a782a1b9e5aeca 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -458,6 +458,7 @@ class Lowering final : public Phase static void SetMultiplyUsed(GenTree* node DEBUGARG(const char* reason)); GenTree* LowerNeg(GenTreeOp* node); void LowerIndexAddr(GenTreeIndexAddr* indexAddr); + void LowerCkfinite(GenTreeOp* node); #endif bool TryCreateAddrMode(GenTree* addr, bool isContainable, GenTree* parent); diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp index 750fd6bed58612..585c3346e8f956 100644 --- a/src/coreclr/jit/lowerwasm.cpp +++ b/src/coreclr/jit/lowerwasm.cpp @@ -370,6 +370,21 @@ void Lowering::LowerRotate(GenTree* tree) ContainCheckShiftRotate(tree->AsOp()); } +//------------------------------------------------------------------------ +// LowerCkfinite: Lowers a GT_CKFINITE node. +// +// Mark the operand as multiply-used since codegen needs to read it twice: +// once for the finiteness check and once for the produced value. +// +// Arguments: +// node - the GT_CKFINITE node to be lowered +// +void Lowering::LowerCkfinite(GenTreeOp* node) +{ + assert(node->OperIs(GT_CKFINITE)); + SetMultiplyUsed(node->gtGetOp1() DEBUGARG("LowerCkfinite op1 (finiteness check)")); +} + //------------------------------------------------------------------------ // LowerIndexAddr: Lowers a GT_INDEX_ADDR node // diff --git a/src/coreclr/jit/regallocwasm.cpp b/src/coreclr/jit/regallocwasm.cpp index e6e4a0969f3ba0..45addb5b57c544 100644 --- a/src/coreclr/jit/regallocwasm.cpp +++ b/src/coreclr/jit/regallocwasm.cpp @@ -498,6 +498,10 @@ void WasmRegAlloc::CollectReferencesForNode(GenTree* node) CollectReferencesForIndexAddr(node->AsIndexAddr()); break; + case GT_CKFINITE: + ConsumeTemporaryRegForOperand(node->gtGetOp1() DEBUGARG("ckfinite finiteness check")); + break; + default: assert(!node->OperIsLocalStore()); break;