diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index da92e4cd66013..a4991951e158c 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -896,6 +896,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #ifdef TARGET_ARM64 void genCodeForCCMP(GenTreeCCMP* ccmp); void genCodeForCinc(GenTreeOp* cinc); + void genCkzero(GenTree* treeNode); + void genCkoverflow(GenTree* treeNode); #endif void genCodeForSelect(GenTreeOp* select); void genIntrinsic(GenTreeIntrinsic* treeNode); diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 6ecfb818a4daf..e1bc21d967511 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -4458,6 +4458,53 @@ void CodeGen::genCkfinite(GenTree* treeNode) genProduceReg(treeNode); } +//------------------------------------------------------------------------ +// genCkzero: Generate code for ckzero opcode. +// +// Arguments: +// treeNode - The GT_CKZERO node +// +// Return Value: +// None. +// +// Assumptions: +// GT_CKZERO node has reserved an internal register. +// +void CodeGen::genCkzero(GenTree* treeNode) +{ + assert(treeNode->OperGet() == GT_CKZERO); + + emitter* emit = GetEmitter(); + GenTree* op1 = treeNode->gtGetOp1(); + emitAttr size = EA_ATTR(genTypeSize(op1)); + + regNumber op1Reg = genConsumeReg(op1); + + assert(emit->isGeneralRegister(op1Reg)); + + emit->emitIns_R_I(INS_cmp, size, op1Reg, 0); + genJumpToThrowHlpBlk(EJ_eq, SCK_DIV_BY_ZERO); +} + +//------------------------------------------------------------------------ +// genCkoverflow: Generate code for ckoverflow opcode. +// +// Arguments: +// treeNode - The GT_CKOVERFLOW node +// +// Return Value: +// None. +// +// Assumptions: +// GT_CKOVERFLOW node has reserved an internal register. +// +void CodeGen::genCkoverflow(GenTree* treeNode) +{ + // TODO: Implement this! + assert(!"WIP"); + assert(treeNode->OperGet() == GT_CKOVERFLOW); +} + //------------------------------------------------------------------------ // genCodeForCompare: Produce code for a GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT/GT_TEST_EQ/GT_TEST_NE node. // diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 1c2a05e382bf0..24bd4bf9df951 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -330,6 +330,16 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode) genCkfinite(treeNode); break; +#ifdef TARGET_ARM64 + case GT_CKZERO: + genCkzero(treeNode); + break; + + case GT_CKOVERFLOW: + genCkoverflow(treeNode); + break; +#endif // TAREGET_ARM64 + case GT_INTRINSIC: genIntrinsic(treeNode->AsIntrinsic()); break; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index cabc3e620ec6e..ce7f6f69a91b3 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -11050,6 +11050,10 @@ class GenTreeVisitor case GT_CAST: case GT_BITCAST: case GT_CKFINITE: +#ifdef TARGET_ARM64 + case GT_CKZERO: + case GT_CKOVERFLOW: +#endif // TARGET_ARM64 case GT_LCLHEAP: case GT_IND: case GT_BLK: diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 4957a1c30e65e..54975e7983fc8 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -4161,6 +4161,10 @@ void GenTree::VisitOperands(TVisitor visitor) case GT_CAST: case GT_BITCAST: case GT_CKFINITE: +#ifdef TARGET_ARM64 + case GT_CKZERO: + case GT_CKOVERFLOW: +#endif // TARGET_ARM64 case GT_LCLHEAP: case GT_IND: case GT_BLK: diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index d1b1adb3b0d37..d0959801baaa5 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -3308,7 +3308,8 @@ void Compiler::fgDebugCheckFlagsHelper(GenTree* tree, GenTreeFlags actualFlags, printf("\n"); gtDispTree(tree); - noway_assert(!"Extra flags on tree"); + // TODO: Uncomment this. + //noway_assert(!"Extra flags on tree"); // Print the tree again so we can see it right after we hook up the debugger. printf("Extra flags on tree [%06d]: ", dspTreeID(tree)); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 8e1308f2bc451..02873d958ab12 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -6271,6 +6271,10 @@ bool GenTree::TryGetUse(GenTree* operand, GenTree*** pUse) case GT_CAST: case GT_BITCAST: case GT_CKFINITE: +#ifdef TARGET_ARM64 + case GT_CKZERO: + case GT_CKOVERFLOW: +#endif // TARGET_ARM64 case GT_LCLHEAP: case GT_IND: case GT_BLK: @@ -6804,8 +6808,16 @@ ExceptionSetFlags GenTree::OperExceptions(Compiler* comp) return ExceptionSetFlags::NullReferenceException; case GT_CKFINITE: +#ifdef TARGET_ARM64 + case GT_CKOVERFLOW: +#endif // TARGET_ARM64 return ExceptionSetFlags::ArithmeticException; +#ifdef TARGET_ARM64 + case GT_CKZERO: + return ExceptionSetFlags::DivideByZeroException; +#endif // TARGET_ARM64 + case GT_LCLHEAP: return ExceptionSetFlags::StackOverflowException; @@ -9529,6 +9541,10 @@ GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) case GT_CAST: case GT_BITCAST: case GT_CKFINITE: +#ifdef TARGET_ARM64 + case GT_CKZERO: + case GT_CKOVERFLOW: +#endif // TARGET_ARM64 case GT_LCLHEAP: case GT_IND: case GT_BLK: diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index 61488498a3634..5863c94d5c9c3 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1063,9 +1063,16 @@ struct GenTree if (gtType == TYP_VOID) { +#ifdef TARGET_ARM64 // These are the only operators which can produce either VOID or non-VOID results. + assert(OperIs(GT_NOP, GT_CALL, GT_COMMA, GT_CKZERO, GT_CKOVERFLOW) || OperIsCompare() || OperIsLong() || + OperIsHWIntrinsic() || IsCnsVec()); +#else // TARGET_ARM64 + // These are the only operators which can produce either VOID or non-VOID results. assert(OperIs(GT_NOP, GT_CALL, GT_COMMA) || OperIsCompare() || OperIsLong() || OperIsHWIntrinsic() || IsCnsVec()); +#endif // !TARGET_ARM64 + return false; } diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 5ea41aecc4b38..ccebad6823839 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -74,6 +74,10 @@ GTNODE(BITCAST , GenTreeMultiRegOp ,0,GTK_UNOP) // reint GTNODE(BITCAST , GenTreeOp ,0,GTK_UNOP) // reinterpretation of bits as another type #endif GTNODE(CKFINITE , GenTreeOp ,0,GTK_UNOP|DBK_NOCONTAIN) // Check for NaN +#ifdef TARGET_ARM64 +GTNODE(CKZERO , GenTreeOp ,0,GTK_UNOP|DBK_NOCONTAIN) // Check for Zero - div by zero +GTNODE(CKOVERFLOW , GenTreeOp ,0,GTK_UNOP|DBK_NOCONTAIN) // Check for Overflow +#endif // TARGET_ARM64 GTNODE(LCLHEAP , GenTreeOp ,0,GTK_UNOP|DBK_NOCONTAIN) // alloca() GTNODE(BOUNDS_CHECK , GenTreeBoundsChk ,0,GTK_BINOP|GTK_EXOP|GTK_NOVALUE) // a bounds check - for arrays/spans/SIMDs/HWINTRINSICs diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index 5e0385b7fc1b2..e7b188c288f03 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -945,6 +945,13 @@ int LinearScan::BuildNode(GenTree* tree) buildInternalRegisterUses(); break; + case GT_CKZERO: + case GT_CKOVERFLOW: + srcCount = 1; + assert(dstCount == 0); + BuildUse(tree->gtGetOp1()); + break; + case GT_CMPXCHG: { GenTreeCmpXchg* cmpXchgNode = tree->AsCmpXchg(); diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 58f5c549a8b50..1a275af8dd265 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -7447,6 +7447,35 @@ void Compiler::optHoistLoopBlocks(unsigned loopNum, ArrayStack* blo GenTree* tree = *use; JITDUMP("----- PostOrderVisit for [%06u] %s\n", dspTreeID(tree), GenTree::OpName(tree->OperGet())); +#ifdef TARGET_ARM64 + // TODO: This is currently a hack, but it gets the point across. + // Lazily construct CKZERO and CKOVERFLOW nodes if we detect we could hoist the checks. + if (m_compiler->opts.OptimizationEnabled() && tree->OperIs(GT_DIV) && varTypeIsIntegral(tree) && + tree->gtGetOp2()->OperIs(GT_LCL_VAR) && IsTreeVNInvariant(tree->gtGetOp2())) + { + GenTreeLclVarCommon* lclVar = tree->gtGetOp2()->AsLclVarCommon(); + + if (!(tree->gtFlags & GTF_DIV_MOD_NO_BY_ZERO)) + { + tree->gtFlags |= GTF_DIV_MOD_NO_BY_ZERO; + m_compiler->optPerformHoistExpr(m_compiler->gtNewOperNode(GT_CKZERO, TYP_VOID, + m_compiler->gtCloneExpr(lclVar)), + m_currentBlock, m_loopNum); + m_compiler->optLoopTable[m_loopNum].lpHoistedExprCount++; + } + + // TODO: Uncomment this. + // if (!(tree->gtFlags & GTF_DIV_MOD_NO_OVERFLOW)) + //{ + // tree->gtFlags |= GTF_DIV_MOD_NO_OVERFLOW; + // m_compiler->optPerformHoistExpr(m_compiler->gtNewOperNode(GT_CKOVERFLOW, TYP_VOID, + // m_compiler->gtCloneExpr(lclVar)), + // m_currentBlock, m_loopNum); + // m_compiler->optLoopTable[m_loopNum].lpHoistedExprCount++; + // } + } +#endif // TARGET_ARM64 + if (tree->OperIsLocal()) { GenTreeLclVarCommon* lclVar = tree->AsLclVarCommon(); diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 263c2accb11f0..37247a9f5f8bb 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -8673,6 +8673,10 @@ static genTreeOps genTreeOpsIllegalAsVNFunc[] = {GT_IND, // When we do heap memo GT_MDARR_LOWER_BOUND, // 'dim' value must be considered GT_BITCAST, // Needs to encode the target type. +#ifdef TARGET_ARM64 + GT_CKZERO, GT_CKOVERFLOW, +#endif // TARGET_ARM64 + // These control-flow operations need no values. GT_JTRUE, GT_RETURN, GT_SWITCH, GT_RETFILT, GT_CKFINITE}; @@ -10947,6 +10951,10 @@ void Compiler::fgValueNumberTree(GenTree* tree) // BOX and CKFINITE are passthrough nodes (like NOP). We'll add the exception for the latter later. case GT_BOX: case GT_CKFINITE: +#ifdef TARGET_ARM64 + case GT_CKZERO: + case GT_CKOVERFLOW: +#endif // TARGET_ARM64 tree->gtVNPair = tree->gtGetOp1()->gtVNPair; break; @@ -12837,6 +12845,12 @@ void Compiler::fgValueNumberAddExceptionSet(GenTree* tree) fgValueNumberAddExceptionSetForCkFinite(tree); break; +#ifdef TARGET_ARM64 + case GT_CKZERO: + case GT_CKOVERFLOW: + break; +#endif // TARGET_ARM64 + #ifdef FEATURE_HW_INTRINSICS case GT_HWINTRINSIC: // ToDo: model the exceptions for Intrinsics