Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: Stop reordering arguments throwing distinct exceptions #70893

Merged
merged 14 commits into from
Jun 22, 2022
Merged
9 changes: 2 additions & 7 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5376,13 +5376,6 @@ class Compiler
void* pCallBackData = nullptr,
bool computeStack = false);

// An fgWalkPreFn that looks for expressions that have inline throws in
// minopts mode. Basically it looks for tress with gtOverflowEx() or
// GTF_IND_RNGCHK. It returns WALK_ABORT if one is found. It
// returns WALK_SKIP_SUBTREES if GTF_EXCEPT is not set (assumes flags
// properly propagated to parent trees). It returns WALK_CONTINUE
// otherwise.
static fgWalkResult fgChkThrowCB(GenTree** pTree, Compiler::fgWalkData* data);
static fgWalkResult fgChkLocAllocCB(GenTree** pTree, Compiler::fgWalkData* data);
static fgWalkResult fgChkQmarkCB(GenTree** pTree, Compiler::fgWalkData* data);

Expand Down Expand Up @@ -5899,6 +5892,8 @@ class Compiler
bool gtIsTypeHandleToRuntimeTypeHandleHelper(GenTreeCall* call, CorInfoHelpFunc* pHelper = nullptr);
bool gtIsActiveCSE_Candidate(GenTree* tree);

ExceptionSetFlags gtCollectExceptions(GenTree* tree);

bool fgIsBigOffset(size_t offset);

bool fgNeedReturnSpillTemp();
Expand Down
41 changes: 0 additions & 41 deletions src/coreclr/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4130,47 +4130,6 @@ void Compiler::fgSetBlockOrder(BasicBlock* block)
return firstNode;
}

/*static*/ Compiler::fgWalkResult Compiler::fgChkThrowCB(GenTree** pTree, fgWalkData* data)
{
GenTree* tree = *pTree;

// If this tree doesn't have the EXCEPT flag set, then there is no
// way any of the child nodes could throw, so we can stop recursing.
if (!(tree->gtFlags & GTF_EXCEPT))
{
return Compiler::WALK_SKIP_SUBTREES;
}

switch (tree->gtOper)
{
case GT_MUL:
case GT_ADD:
case GT_SUB:
case GT_CAST:
if (tree->gtOverflow())
{
return Compiler::WALK_ABORT;
}
break;

case GT_INDEX_ADDR:
// This calls CORINFO_HELP_RNGCHKFAIL for Debug code.
if (tree->AsIndexAddr()->IsBoundsChecked())
{
return Compiler::WALK_ABORT;
}
break;

case GT_BOUNDS_CHECK:
return Compiler::WALK_ABORT;

default:
break;
}

return Compiler::WALK_CONTINUE;
}

/*static*/ Compiler::fgWalkResult Compiler::fgChkLocAllocCB(GenTree** pTree, fgWalkData* data)
{
GenTree* tree = *pTree;
Expand Down
175 changes: 145 additions & 30 deletions src/coreclr/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6354,16 +6354,20 @@ bool GenTree::OperIsImplicitIndir() const
}

//------------------------------------------------------------------------------
// OperMayThrow : Check whether the operation may throw.
// OperExceptions : Get exception set this tree may throw.
//
//
// Arguments:
// comp - Compiler instance
//
// Return Value:
// True if the given operator may cause an exception

bool GenTree::OperMayThrow(Compiler* comp)
// A bit set of exceptions this tree may throw.
//
// Remarks:
// Should not be used on calls given that we can say nothing precise about
// those.
//
ExceptionSetFlags GenTree::OperExceptions(Compiler* comp)
{
GenTree* op;

Expand All @@ -6380,61 +6384,98 @@ bool GenTree::OperMayThrow(Compiler* comp)

if (varTypeIsFloating(op->TypeGet()))
{
return false; // Floating point division does not throw.
return ExceptionSetFlags::None;
}

// For integers only division by 0 or by -1 can throw
if (op->IsIntegralConst() && !op->IsIntegralConst(0) && !op->IsIntegralConst(-1))
if (op->IsIntegralConst())
{
return false;
if (op->IsIntegralConst(0))
{
return ExceptionSetFlags::DivideByZeroException;
}
if (op->IsIntegralConst(-1))
{
return ExceptionSetFlags::ArithmeticException;
}

return ExceptionSetFlags::None;
}
return true;

return ExceptionSetFlags::DivideByZeroException | ExceptionSetFlags::ArithmeticException;

case GT_INTRINSIC:
// If this is an intrinsic that represents the object.GetType(), it can throw an NullReferenceException.
// Currently, this is the only intrinsic that can throw an exception.
return AsIntrinsic()->gtIntrinsicName == NI_System_Object_GetType;
if (AsIntrinsic()->gtIntrinsicName == NI_System_Object_GetType)
{
return ExceptionSetFlags::NullReferenceException;
}

return ExceptionSetFlags::None;

case GT_CALL:
assert(!"Unexpected GT_CALL in OperExceptions");

CorInfoHelpFunc helper;
helper = comp->eeGetHelperNum(this->AsCall()->gtCallMethHnd);
return ((helper == CORINFO_HELP_UNDEF) || !comp->s_helperCallProperties.NoThrow(helper));
return ExceptionSetFlags::All;

case GT_IND:
case GT_BLK:
case GT_OBJ:
case GT_NULLCHECK:
case GT_STORE_BLK:
case GT_STORE_DYN_BLK:
return (((this->gtFlags & GTF_IND_NONFAULTING) == 0) && comp->fgAddrCouldBeNull(this->AsIndir()->Addr()));
if (((this->gtFlags & GTF_IND_NONFAULTING) == 0) && comp->fgAddrCouldBeNull(this->AsIndir()->Addr()))
{
return ExceptionSetFlags::NullReferenceException;
}

return ExceptionSetFlags::None;

case GT_ARR_LENGTH:
return (((this->gtFlags & GTF_IND_NONFAULTING) == 0) &&
comp->fgAddrCouldBeNull(this->AsArrLen()->ArrRef()));
if (((this->gtFlags & GTF_IND_NONFAULTING) == 0) && comp->fgAddrCouldBeNull(this->AsArrLen()->ArrRef()))
{
return ExceptionSetFlags::NullReferenceException;
}

return ExceptionSetFlags::None;

case GT_ARR_ELEM:
return comp->fgAddrCouldBeNull(this->AsArrElem()->gtArrObj);
if (comp->fgAddrCouldBeNull(this->AsArrElem()->gtArrObj))
{
return ExceptionSetFlags::NullReferenceException;
}

return ExceptionSetFlags::None;

case GT_FIELD:
{
GenTree* fldObj = this->AsField()->GetFldObj();

if (fldObj != nullptr)
{
return comp->fgAddrCouldBeNull(fldObj);
if (comp->fgAddrCouldBeNull(fldObj))
{
return ExceptionSetFlags::NullReferenceException;
}
}

return false;
return ExceptionSetFlags::None;
}

case GT_BOUNDS_CHECK:
case GT_INDEX_ADDR:
return ExceptionSetFlags::IndexOutOfRangeException;

case GT_ARR_INDEX:
case GT_ARR_OFFSET:
case GT_LCLHEAP:
return ExceptionSetFlags::NullReferenceException;

case GT_CKFINITE:
case GT_INDEX_ADDR:
return true;
return ExceptionSetFlags::ArithmeticException;

case GT_LCLHEAP:
return ExceptionSetFlags::StackOverflowException;

#ifdef FEATURE_HW_INTRINSICS
case GT_HWINTRINSIC:
Expand All @@ -6446,23 +6487,42 @@ bool GenTree::OperMayThrow(Compiler* comp)
// This operation contains an implicit indirection
// it could throw a null reference exception.
//
return true;
return ExceptionSetFlags::NullReferenceException;
}
break;

return ExceptionSetFlags::None;
}
#endif // FEATURE_HW_INTRINSICS
default:
break;
}
if (gtOverflowEx())
{
return ExceptionSetFlags::OverflowException;
}

/* Overflow arithmetic operations also throw exceptions */
return ExceptionSetFlags::None;
}
}

if (gtOverflowEx())
//------------------------------------------------------------------------------
// OperMayThrow : Check whether the operation may throw.
//
//
// Arguments:
// comp - Compiler instance
//
// Return Value:
// True if the given operator may cause an exception
//
bool GenTree::OperMayThrow(Compiler* comp)
{
if (OperIs(GT_CALL))
{
return true;
CorInfoHelpFunc helper;
helper = comp->eeGetHelperNum(this->AsCall()->gtCallMethHnd);
return ((helper == CORINFO_HELP_UNDEF) || !comp->s_helperCallProperties.NoThrow(helper));
}

return false;
return OperExceptions(comp) != ExceptionSetFlags::None;
}

//-----------------------------------------------------------------------------------
Expand Down Expand Up @@ -16116,7 +16176,7 @@ bool Compiler::gtIsTypeHandleToRuntimeTypeHelper(GenTreeCall* call)
//
// Return Value:
// True if so

//
bool Compiler::gtIsTypeHandleToRuntimeTypeHandleHelper(GenTreeCall* call, CorInfoHelpFunc* pHelper)
{
CorInfoHelpFunc helper = CORINFO_HELP_UNDEF;
Expand All @@ -16143,6 +16203,61 @@ bool Compiler::gtIsActiveCSE_Candidate(GenTree* tree)
return (optValnumCSE_phase && IS_CSE_INDEX(tree->gtCSEnum));
}

//------------------------------------------------------------------------
// gtCollectExceptions: walk a tree collecting a bit set of exceptions the tree
// may throw.
//
// Arguments:
// tree - tree to examine
//
// Return Value:
// Bit set of exceptions the tree may throw.
//
ExceptionSetFlags Compiler::gtCollectExceptions(GenTree* tree)
{
class ExceptionsWalker final : public GenTreeVisitor<ExceptionsWalker>
{
ExceptionSetFlags m_preciseExceptions = ExceptionSetFlags::None;

public:
ExceptionsWalker(Compiler* comp) : GenTreeVisitor<ExceptionsWalker>(comp)
{
}

enum
{
DoPreOrder = true,
};

ExceptionSetFlags GetFlags()
{
return m_preciseExceptions;
}

fgWalkResult PreOrderVisit(GenTree** use, GenTree* user)
{
GenTree* tree = *use;
if ((tree->gtFlags & GTF_EXCEPT) == 0)
{
return WALK_SKIP_SUBTREES;
}

m_preciseExceptions |= tree->OperExceptions(m_compiler);
return WALK_CONTINUE;
}
};

// We only expect the caller to ask for precise exceptions for cases where
// it may help with disambiguating between exceptions. If the tree contains
// a call it can always throw arbitrary exceptions.
assert((tree->gtFlags & GTF_CALL) == 0);

ExceptionsWalker walker(this);
walker.WalkTree(&tree, nullptr);
assert(((tree->gtFlags & GTF_EXCEPT) == 0) || (walker.GetFlags() != ExceptionSetFlags::None));
return walker.GetFlags();
}

/*****************************************************************************/

struct ComplexityStruct
Expand Down
Loading