diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 27df00a92e667d..60389763455d30 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -2086,9 +2086,79 @@ void Compiler::optAssertionGen(GenTree* tree) assert(thisArg != nullptr); assertionInfo = optCreateAssertion(thisArg, nullptr, /*equals*/ false); } + else if (!optLocalAssertionProp) + { + // Array allocation creates an assertion that the length argument is non-negative. + // + // var arr = new int[n]; - creates n >= 0 assertion + // + GenTree* lenArg = getArrayLengthFromAllocation(call); + if (lenArg != nullptr) + { + ValueNum lenVN = vnStore->VNIgnoreIntToLongCast(optConservativeNormalVN(lenArg)); + if ((lenVN != ValueNumStore::NoVN) && !vnStore->IsVNConstant(lenVN) && + (vnStore->TypeOfVN(lenVN) == TYP_INT)) + { + ValueNum zeroVN = vnStore->VNZeroForType(TYP_INT); + assertionInfo = optAddAssertion(AssertionDsc::CreateConstantBound(this, VNF_GE, lenVN, zeroVN)); + break; + } + } + + // CORINFO_HELP_ARRADDR_ST(arrRef, idx, value) creates an assertion that "idx" is within the bounds of + // "arrRef" array + // + // arr[idx] = value; - creates idx is within bounds of arr assertion + // + CorInfoHelpFunc helperId = eeGetHelperNum(call->gtCallMethHnd); + if ((helperId == CORINFO_HELP_ARRADDR_ST) || (helperId == CORINFO_HELP_LDELEMA_REF)) + { + assert(call->gtArgs.CountUserArgs() == 3); + GenTree* arrRef = call->gtArgs.GetUserArgByIndex(0)->GetNode(); + GenTree* idx = call->gtArgs.GetUserArgByIndex(1)->GetNode(); + + ValueNum idxVN = vnStore->VNIgnoreIntToLongCast(optConservativeNormalVN(idx)); + if ((idxVN != ValueNumStore::NoVN) && (vnStore->TypeOfVN(idxVN) == TYP_INT)) + { + ValueNum arrRefVN = optConservativeNormalVN(arrRef); + if (arrRefVN != ValueNumStore::NoVN) + { + // Compose a VN_ARR_LENGTH VN for the array reference and use it + // to create a bounds check assertion on the index. + ValueNum lenVN = vnStore->VNForFunc(TYP_INT, VNF_ARR_LENGTH, arrRefVN); + assertionInfo = optAddAssertion(AssertionDsc::CreateNoThrowArrBnd(this, idxVN, lenVN)); + break; + } + } + } + } } break; + case GT_DIV: + case GT_UDIV: + case GT_MOD: + case GT_UMOD: + if (!optLocalAssertionProp) + { + // For division/modulo, we can create an assertion that the divisor is not zero + // + // c = a / b; - creates b != 0 assertion + // + ValueNum divisorVN = optConservativeNormalVN(tree->AsOp()->gtGetOp2()); + if ((divisorVN != ValueNumStore::NoVN) && !vnStore->IsVNConstant(divisorVN)) + { + var_types divisorType = vnStore->TypeOfVN(divisorVN); + if (varTypeIsIntegral(divisorType)) + { + ValueNum zeroVN = vnStore->VNZeroForType(divisorType); + assertionInfo = + optAddAssertion(AssertionDsc::CreateConstantBound(this, VNF_NE, divisorVN, zeroVN)); + } + } + } + break; + case GT_JTRUE: assertionInfo = optAssertionGenJtrue(tree); break; diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 6d63894fa8f8ea..f2d55a5cb078f7 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -7595,7 +7595,7 @@ class Compiler typedef JitHashTable, GenTree*> LocalNumberToNullCheckTreeMap; - GenTree* getArrayLengthFromAllocation(GenTree* tree DEBUGARG(BasicBlock* block)); + GenTree* getArrayLengthFromAllocation(GenTree* tree); GenTree* optPropGetValueRec(unsigned lclNum, unsigned ssaNum, optPropKind valueKind, int walkDepth); GenTree* optPropGetValue(unsigned lclNum, unsigned ssaNum, optPropKind valueKind); GenTree* optEarlyPropRewriteTree(GenTree* tree, LocalNumberToNullCheckTreeMap* nullCheckMap); diff --git a/src/coreclr/jit/earlyprop.cpp b/src/coreclr/jit/earlyprop.cpp index b606e66399d894..0c6efd0985ee70 100644 --- a/src/coreclr/jit/earlyprop.cpp +++ b/src/coreclr/jit/earlyprop.cpp @@ -281,7 +281,7 @@ GenTree* Compiler::optPropGetValueRec(unsigned lclNum, unsigned ssaNum, optPropK { if (valueKind == optPropKind::OPK_ARRAYLEN) { - value = getArrayLengthFromAllocation(defValue DEBUGARG(ssaVarDsc->GetBlock())); + value = getArrayLengthFromAllocation(defValue); if (value != nullptr) { if (!value->IsCnsIntOrI()) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 036014e4baaaa0..e8bc0806675b01 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -2050,13 +2050,12 @@ bool GenTreeCall::IsPure(Compiler* compiler) const // helper call. // // Arguments: -// tree - The array allocation helper call. -// block - tree's basic block. +// tree - The array allocation helper call. // // Return Value: // Return the array length node. -GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree DEBUGARG(BasicBlock* block)) +GenTree* Compiler::getArrayLengthFromAllocation(GenTree* tree) { assert(tree != nullptr); @@ -2252,7 +2251,7 @@ bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool // Consider array allocators side-effect free for constant length (if it's not negative and fits into i32) if (helperProperties.IsAllocator(helper)) { - GenTree* arrLen = compiler->getArrayLengthFromAllocation((GenTree*)this DEBUGARG(nullptr)); + GenTree* arrLen = compiler->getArrayLengthFromAllocation((GenTree*)this); // if arrLen is nullptr it means it wasn't an array allocator if ((arrLen != nullptr) && arrLen->IsIntCnsFitsInI32()) { diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 2be00e7e1057c3..071263cb6fc88e 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -2045,6 +2045,44 @@ ValueNum ValueNumStore::VNForCastOper(var_types castToType, bool srcIsUnsigned) return result; } +//------------------------------------------------------------------------ +// VNIgnoreIntToLongCast: Looks through a sign-extending int-to-long cast +// or convert long-typed integral constants to int. +// +// Arguments: +// vn - The value number to inspect. +// +// Return Value: +// The value number of the original TYP_INT operand if 'vn' is a VNF_Cast +// that sign-extends a TYP_INT to TYP_LONG; or the value number of a TYP_INT +// constant if 'vn' is a TYP_LONG constant that fits in an int; otherwise, 'vn' itself. +// +ValueNum ValueNumStore::VNIgnoreIntToLongCast(ValueNum vn) +{ + if (TypeOfVN(vn) == TYP_LONG) + { + VNFuncApp funcApp; + if (GetVNFunc(vn, &funcApp) && funcApp.FuncIs(VNF_Cast)) + { + var_types castToType; + bool srcIsUnsigned; + GetCastOperFromVN(funcApp.m_args[1], &castToType, &srcIsUnsigned); + if ((castToType == TYP_LONG) && !srcIsUnsigned && TypeOfVN(funcApp.m_args[0]) == TYP_INT) + { + return funcApp.m_args[0]; + } + } + + // Also look through any long-typed integral constant that fits in an int. + int intCns; + if (IsVNIntegralConstant(vn, &intCns)) + { + return VNForIntCon(intCns); + } + } + return vn; +} + void ValueNumStore::GetCastOperFromVN(ValueNum vn, var_types* pCastToType, bool* pSrcIsUnsigned) { assert(pCastToType != nullptr); @@ -2625,28 +2663,8 @@ ValueNum ValueNumStore::VNForFunc(var_types typ, VNFunc func, ValueNum arg0VN) VNFuncApp newArrFuncApp; if (GetVNFunc(arg0VN, &newArrFuncApp) && (newArrFuncApp.m_func == VNF_JitNewArr)) { - ValueNum actualSizeVN = newArrFuncApp.m_args[1]; - var_types actualSizeVNType = TypeOfVN(actualSizeVN); - - // JitNewArr's size argument (args[1]) is typically upcasted to TYP_LONG via VNF_Cast. - if (actualSizeVNType == TYP_LONG) - { - VNFuncApp castFuncApp; - if (GetVNFunc(actualSizeVN, &castFuncApp) && (castFuncApp.m_func == VNF_Cast)) - { - var_types castToType; - bool srcIsUnsigned; - GetCastOperFromVN(castFuncApp.m_args[1], &castToType, &srcIsUnsigned); - - // Make sure we have exactly (TYP_LONG)myInt32 cast: - if (!srcIsUnsigned && (castToType == TYP_LONG) && TypeOfVN(castFuncApp.m_args[0]) == TYP_INT) - { - // If that is the case, return the original size argument - *resultVN = castFuncApp.m_args[0]; - } - } - } - else if (actualSizeVNType == TYP_INT) + ValueNum actualSizeVN = VNIgnoreIntToLongCast(newArrFuncApp.m_args[1]); + if (TypeOfVN(actualSizeVN) == TYP_INT) { *resultVN = actualSizeVN; } diff --git a/src/coreclr/jit/valuenum.h b/src/coreclr/jit/valuenum.h index 016eabdc2f84d5..02050154df46cf 100644 --- a/src/coreclr/jit/valuenum.h +++ b/src/coreclr/jit/valuenum.h @@ -508,6 +508,8 @@ class ValueNumStore // Unpacks the information stored by VNForCastOper in the constant represented by the value number. void GetCastOperFromVN(ValueNum vn, var_types* pCastToType, bool* pSrcIsUnsigned); + ValueNum VNIgnoreIntToLongCast(ValueNum vn); + // We keep handle values in a separate pool, so we don't confuse a handle with an int constant // that happens to be the same... ValueNum VNForHandle(ssize_t cnsVal, GenTreeFlags iconFlags);