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

Enable escape analysis and use in vn #103148

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4717,9 +4717,17 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
// local variable allocation on the stack.
ObjectAllocator objectAllocator(this); // PHASE_ALLOCATE_OBJECTS

if (compObjectStackAllocation() && opts.OptimizationEnabled())
if (opts.OptimizationEnabled())
{
objectAllocator.EnableObjectStackAllocation();
if (JitConfig.JitObjectStackAllocationAnalysis() > 0)
{
objectAllocator.EnableObjectStackAllocationAnalysis();
}

if (compObjectStackAllocation())
{
objectAllocator.EnableObjectStackAllocation();
}
}

objectAllocator.Run();
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -6497,7 +6497,7 @@ class Compiler

GenTree* fgMorphCastIntoHelper(GenTree* tree, int helper, GenTree* oper);

GenTree* fgMorphIntoHelperCall(
GenTreeCall* fgMorphIntoHelperCall(
GenTree* tree, int helper, bool morphArgs, GenTree* arg1 = nullptr, GenTree* arg2 = nullptr);

// A "MorphAddrContext" carries information from the surrounding context. If we are evaluating a byref address,
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -4171,6 +4171,7 @@ enum GenTreeCallFlags : unsigned int
GTF_CALL_M_CAST_CAN_BE_EXPANDED = 0x04000000, // this cast (helper call) can be expanded if it's profitable. To be removed.
GTF_CALL_M_CAST_OBJ_NONNULL = 0x08000000, // if we expand this specific cast we don't need to check the input object for null
// NOTE: if needed, this flag can be removed, and we can introduce new _NONNUL cast helpers
GTF_CALL_M_NO_ESCAPE = 0x10000000, // the object allocated by this call does not escape allocating thread
};

inline constexpr GenTreeCallFlags operator ~(GenTreeCallFlags a)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/jitconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ RELEASE_CONFIG_INTEGER(JitExtDefaultPolicyProfScale, W("JitExtDefaultPolicyProfS
RELEASE_CONFIG_INTEGER(JitInlinePolicyModel, W("JitInlinePolicyModel"), 0)
RELEASE_CONFIG_INTEGER(JitInlinePolicyProfile, W("JitInlinePolicyProfile"), 0)
RELEASE_CONFIG_INTEGER(JitInlinePolicyProfileThreshold, W("JitInlinePolicyProfileThreshold"), 40)
RELEASE_CONFIG_INTEGER(JitObjectStackAllocationAnalysis, W("JitObjectStackAllocationAnalysis"), 1)
RELEASE_CONFIG_INTEGER(JitObjectStackAllocation, W("JitObjectStackAllocation"), 0)

RELEASE_CONFIG_INTEGER(JitEECallTimingInfo, W("JitEECallTimingInfo"), 0)
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/jitmetadatalist.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ JITMETADATAMETRIC(ProfileInconsistentInlineeScale, int, 0)
JITMETADATAMETRIC(ProfileInconsistentInlinee, int, 0)
JITMETADATAMETRIC(ProfileInconsistentNoReturnInlinee, int, 0)
JITMETADATAMETRIC(ProfileInconsistentMayThrowInlinee, int, 0)
JITMETADATAMETRIC(NewRefClassHelperCalls, int, 0)
JITMETADATAMETRIC(NonEscapingNewRefClassHelperCalls, int, 0)
JITMETADATAMETRIC(NewBoxedValueClassHelperCalls, int, 0)
JITMETADATAMETRIC(NonEscapingNewBoxedValueClassHelperCalls, int, 0)

#undef JITMETADATA
#undef JITMETADATAINFO
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ class SharedTempsScope
// Return value:
// The call (which is the same as `tree`).
//
GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, bool morphArgs, GenTree* arg1, GenTree* arg2)
GenTreeCall* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, bool morphArgs, GenTree* arg1, GenTree* arg2)
{
// The helper call ought to be semantically equivalent to the original node, so preserve its VN.
tree->ChangeOper(GT_CALL, GenTree::PRESERVE_VN);
Expand Down Expand Up @@ -250,10 +250,10 @@ GenTree* Compiler::fgMorphIntoHelperCall(GenTree* tree, int helper, bool morphAr
if (morphArgs)
{
SharedTempsScope scope(this);
tree = fgMorphArgs(call);
call = fgMorphArgs(call);
}

return tree;
return call;
}

//------------------------------------------------------------------------
Expand Down
72 changes: 61 additions & 11 deletions src/coreclr/jit/objectalloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ PhaseStatus ObjectAllocator::DoPhase()
return PhaseStatus::MODIFIED_NOTHING;
}

if (IsObjectStackAllocationEnabled())
const bool analyze = IsObjectStackAllocationAnalysisEnabled();

if (analyze)
{
JITDUMP("enabled, analyzing...\n");
DoAnalysis();
Expand Down Expand Up @@ -117,7 +119,7 @@ void ObjectAllocator::AddConnGraphEdge(unsigned int sourceLclNum, unsigned int t

void ObjectAllocator::DoAnalysis()
{
assert(m_IsObjectStackAllocationEnabled);
assert(m_IsObjectStackAllocationAnalysisEnabled);
assert(!m_AnalysisDone);

if (comp->lvaCount > 0)
Expand Down Expand Up @@ -378,6 +380,7 @@ bool ObjectAllocator::MorphAllocObjNodes()
if (canonicalAllocObjFound)
{
assert(basicBlockHasNewObj);

//------------------------------------------------------------------------
// We expect the following expression tree at this point
// STMTx (IL 0x... ???)
Expand All @@ -390,6 +393,19 @@ bool ObjectAllocator::MorphAllocObjNodes()
unsigned int lclNum = stmtExpr->AsLclVar()->GetLclNum();
CORINFO_CLASS_HANDLE clsHnd = data->AsAllocObj()->gtAllocObjClsHnd;

DWORD classAttribs = 0;
// comp->info.compCompHnd->getClassAttribs(clsHnd);
const bool isValueClass = (classAttribs & CORINFO_FLG_VALUECLASS) != 0;

if (isValueClass)
{
comp->Metrics.NewBoxedValueClassHelperCalls++;
}
else
{
comp->Metrics.NewRefClassHelperCalls++;
}

// Don't attempt to do stack allocations inside basic blocks that may be in a loop.
if (IsObjectStackAllocationEnabled() && !basicBlockHasBackwardJump &&
CanAllocateLclVarOnStack(lclNum, clsHnd))
Expand All @@ -413,9 +429,35 @@ bool ObjectAllocator::MorphAllocObjNodes()
JITDUMP("Allocating local variable V%02u on the heap\n", lclNum);
}

data = MorphAllocObjNodeIntoHelperCall(asAllocObj);
// Some new helpers directly cause escape (eg add to finalizer queue)
//
GenTreeCall* const helper = MorphAllocObjNodeIntoHelperCall(asAllocObj);
data = helper;
stmtExpr->AsLclVar()->Data() = data;
stmtExpr->AddAllEffectsFlags(data);

if (IsObjectStackAllocationAnalysisEnabled())
{
const bool doesNotEscape = !CanLclVarEscape(lclNum) && !asAllocObj->gtHelperHasSideEffects;
if (doesNotEscape)
{
JITDUMP("ALLOCOBJ at [%06u] does not escape\n", comp->dspTreeID(asAllocObj));
helper->gtCallMoreFlags |= GTF_CALL_M_NO_ESCAPE;

if (isValueClass)
{
comp->Metrics.NonEscapingNewBoxedValueClassHelperCalls++;
}
else
{
comp->Metrics.NonEscapingNewRefClassHelperCalls++;
}
}
else
{
JITDUMP("ALLOCOBJ at [%06u] escapes\n", comp->dspTreeID(asAllocObj));
}
}
}
}
#ifdef DEBUG
Expand Down Expand Up @@ -444,7 +486,7 @@ bool ObjectAllocator::MorphAllocObjNodes()
// Notes:
// Must update parents flags after this.

GenTree* ObjectAllocator::MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* allocObj)
GenTreeCall* ObjectAllocator::MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* allocObj)
{
assert(allocObj != nullptr);

Expand All @@ -460,11 +502,11 @@ GenTree* ObjectAllocator::MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* alloc
}
#endif

const bool morphArgs = false;
GenTree* helperCall = comp->fgMorphIntoHelperCall(allocObj, allocObj->gtNewHelper, morphArgs, arg);
const bool morphArgs = false;
GenTreeCall* const helperCall = comp->fgMorphIntoHelperCall(allocObj, allocObj->gtNewHelper, morphArgs, arg);
if (helperHasSideEffects)
{
helperCall->AsCall()->gtCallMoreFlags |= GTF_CALL_M_ALLOC_SIDE_EFFECTS;
helperCall->gtCallMoreFlags |= GTF_CALL_M_ALLOC_SIDE_EFFECTS;
}

#ifdef FEATURE_READYTORUN
Expand Down Expand Up @@ -601,13 +643,16 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack<GenTree*>* parent

case GT_EQ:
case GT_NE:
case GT_NULLCHECK:
canLclVarEscapeViaParentStack = false;
break;

case GT_COMMA:
case GT_STORE_BLK:
if (parent->AsOp()->gtGetOp1() == parentStack->Top(parentIndex - 1))
{
// Left child of GT_COMMA, it will be discarded
// Left child of GT_COMMA will be discarded
// Left childof GT_STORE_BLK is an address to write to
canLclVarEscapeViaParentStack = false;
break;
}
Expand All @@ -616,6 +661,8 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack<GenTree*>* parent
case GT_QMARK:
case GT_ADD:
case GT_FIELD_ADDR:
case GT_LCL_ADDR:
case GT_BOX:
// Check whether the local escapes via its grandparent.
++parentIndex;
keepChecking = true;
Expand All @@ -642,9 +689,10 @@ bool ObjectAllocator::CanLclVarEscapeViaParentStack(ArrayStack<GenTree*>* parent
// TODO-ObjectStackAllocation: Special-case helpers here that
// 1. Don't make objects escape.
// 2. Protect objects as interior (GCPROTECT_BEGININTERIOR() instead of GCPROTECT_BEGIN()).
// 3. Don't check that the object is in the heap in ValidateInner.

canLclVarEscapeViaParentStack = true;
// 3. Don't check that the object is in the heap in ValidateInner
//
canLclVarEscapeViaParentStack =
!Compiler::s_helperCallProperties.IsNoEscape(comp->eeGetHelperNum(asCall->gtCallMethHnd));
}
break;
}
Expand Down Expand Up @@ -709,6 +757,7 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack<GenTree*>* p
case GT_QMARK:
case GT_ADD:
case GT_FIELD_ADDR:
case GT_LCL_ADDR:
if (parent->TypeGet() == TYP_REF)
{
parent->ChangeType(newType);
Expand All @@ -733,6 +782,7 @@ void ObjectAllocator::UpdateAncestorTypes(GenTree* tree, ArrayStack<GenTree*>* p
break;

case GT_IND:
case GT_CALL:
break;

default:
Expand Down
28 changes: 26 additions & 2 deletions src/coreclr/jit/objectalloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class ObjectAllocator final : public Phase

//===============================================================================
// Data members
bool m_IsObjectStackAllocationAnalysisEnabled;
bool m_IsObjectStackAllocationEnabled;
bool m_AnalysisDone;
BitVecTraits m_bitVecTraits;
Expand All @@ -41,7 +42,9 @@ class ObjectAllocator final : public Phase
public:
ObjectAllocator(Compiler* comp);
bool IsObjectStackAllocationEnabled() const;
bool IsObjectStackAllocationAnalysisEnabled() const;
void EnableObjectStackAllocation();
void EnableObjectStackAllocationAnalysis();

protected:
virtual PhaseStatus DoPhase() override;
Expand All @@ -61,7 +64,7 @@ class ObjectAllocator final : public Phase
void ComputeStackObjectPointers(BitVecTraits* bitVecTraits);
bool MorphAllocObjNodes();
void RewriteUses();
GenTree* MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* allocObj);
GenTreeCall* MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* allocObj);
unsigned int MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* allocObj, BasicBlock* block, Statement* stmt);
struct BuildConnGraphVisitorCallbackData;
bool CanLclVarEscapeViaParentStack(ArrayStack<GenTree*>* parentStack, unsigned int lclNum);
Expand All @@ -74,6 +77,7 @@ class ObjectAllocator final : public Phase

inline ObjectAllocator::ObjectAllocator(Compiler* comp)
: Phase(comp, PHASE_ALLOCATE_OBJECTS)
, m_IsObjectStackAllocationAnalysisEnabled(false)
, m_IsObjectStackAllocationEnabled(false)
, m_AnalysisDone(false)
, m_bitVecTraits(comp->lvaCount, comp)
Expand All @@ -96,12 +100,32 @@ inline bool ObjectAllocator::IsObjectStackAllocationEnabled() const
return m_IsObjectStackAllocationEnabled;
}

//------------------------------------------------------------------------
// IsObjectStackAllocationAnalysisEnabled: Returns true iff object stack allocation analysis is enabled
//
// Return Value:
// Returns true iff object stack allocation analysis is enabled

inline bool ObjectAllocator::IsObjectStackAllocationAnalysisEnabled() const
{
return m_IsObjectStackAllocationAnalysisEnabled;
}

//------------------------------------------------------------------------
// EnableObjectStackAllocationAnalysis: Enable object stack allocation analysis.

inline void ObjectAllocator::EnableObjectStackAllocationAnalysis()
{
m_IsObjectStackAllocationAnalysisEnabled = true;
}

//------------------------------------------------------------------------
// EnableObjectStackAllocation: Enable object stack allocation.

inline void ObjectAllocator::EnableObjectStackAllocation()
{
m_IsObjectStackAllocationEnabled = true;
m_IsObjectStackAllocationEnabled = true;
m_IsObjectStackAllocationAnalysisEnabled = true;
}

//------------------------------------------------------------------------
Expand Down
13 changes: 8 additions & 5 deletions src/coreclr/jit/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1520,6 +1520,7 @@ void HelperCallProperties::init()
bool isAllocator = false; // true if the result is usually a newly created heap item, or may throw OutOfMemory
bool mutatesHeap = false; // true if any previous heap objects [are|can be] modified
bool mayRunCctor = false; // true if the helper call may cause a static constructor to be run.
bool isNoEscape = false; // true if none of the GC ref arguments can escape

switch (helper)
{
Expand Down Expand Up @@ -1644,8 +1645,9 @@ void HelperCallProperties::init()
case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE:
case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE:

isPure = true;
noThrow = true; // These return null for a failing cast
isNoEscape = true;
isPure = true;
noThrow = true; // These return null for a failing cast
break;

case CORINFO_HELP_GETCURRENTMANAGEDTHREADID:
Expand All @@ -1663,14 +1665,15 @@ void HelperCallProperties::init()

// These throw for a failing cast
// But if given a null input arg will return null
// if they throw they cause the object to escape
isPure = true;
break;

// helpers returning addresses, these can also throw
case CORINFO_HELP_UNBOX:
case CORINFO_HELP_LDELEMA_REF:

isPure = true;
isNoEscape = true;
isPure = true;
break;

// GETREFANY is pure up to the value of the struct argument. We
Expand Down Expand Up @@ -1742,7 +1745,6 @@ void HelperCallProperties::init()
case CORINFO_HELP_ASSIGN_REF_ENSURE_NONHEAP:
case CORINFO_HELP_ASSIGN_BYREF:
case CORINFO_HELP_BULK_WRITEBARRIER:

mutatesHeap = true;
break;

Expand Down Expand Up @@ -1821,6 +1823,7 @@ void HelperCallProperties::init()
m_isAllocator[helper] = isAllocator;
m_mutatesHeap[helper] = mutatesHeap;
m_mayRunCctor[helper] = mayRunCctor;
m_isNoEscape[helper] = isNoEscape;
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/jit/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ class HelperCallProperties
bool m_isAllocator[CORINFO_HELP_COUNT];
bool m_mutatesHeap[CORINFO_HELP_COUNT];
bool m_mayRunCctor[CORINFO_HELP_COUNT];
bool m_isNoEscape[CORINFO_HELP_COUNT];

void init();

Expand Down Expand Up @@ -647,6 +648,13 @@ class HelperCallProperties
assert(helperId < CORINFO_HELP_COUNT);
return m_mayRunCctor[helperId];
}

bool IsNoEscape(CorInfoHelpFunc helperId)
{
assert(helperId > CORINFO_HELP_UNDEF);
assert(helperId < CORINFO_HELP_COUNT);
return m_isNoEscape[helperId];
}
};

//*****************************************************************************
Expand Down
Loading
Loading