Skip to content

Commit

Permalink
Enable object stack allocation in R2R mode.
Browse files Browse the repository at this point in the history
This change modified the importer to create GenTreeAllocObj node for
box and newobj instead of a helper call in R2R mode. ObjectAllocator phase
decides whether the object can be allocated on the stack or has to be created
on the heap via a helper call.

To trigger object stack allocation COMPlus_JitObjectStackAllocation has
to be set (it's not set by default).


Commit migrated from dotnet/coreclr@3e06d9f
  • Loading branch information
erozenfeld committed Dec 16, 2018
1 parent a16ec19 commit a4c765f
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 65 deletions.
4 changes: 3 additions & 1 deletion src/coreclr/src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2465,7 +2465,9 @@ class Compiler

GenTreeCast* gtNewCastNodeL(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType);

GenTree* gtNewAllocObjNode(unsigned int helper, CORINFO_CLASS_HANDLE clsHnd, var_types type, GenTree* op1);
GenTreeAllocObj* gtNewAllocObjNode(unsigned int helper, CORINFO_CLASS_HANDLE clsHnd, var_types type, GenTree* op1);

GenTreeAllocObj* gtNewAllocObjNode(CORINFO_RESOLVED_TOKEN* pResolvedToken, BOOL useParent);

GenTree* gtNewRuntimeLookup(CORINFO_GENERIC_HANDLE hnd, CorInfoGenericHandleType hndTyp, GenTree* lookupTree);

Expand Down
12 changes: 7 additions & 5 deletions src/coreclr/src/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1165,12 +1165,13 @@ inline GenTreeCall* Compiler::gtNewHelperCallNode(unsigned helper, var_types typ
// Return Value:
// Returns GT_ALLOCOBJ node that will be later morphed into an
// allocation helper call or local variable allocation on the stack.
inline GenTree* Compiler::gtNewAllocObjNode(unsigned int helper,
CORINFO_CLASS_HANDLE clsHnd,
var_types type,
GenTree* op1)

inline GenTreeAllocObj* Compiler::gtNewAllocObjNode(unsigned int helper,
CORINFO_CLASS_HANDLE clsHnd,
var_types type,
GenTree* op1)
{
GenTree* node = new (this, GT_ALLOCOBJ) GenTreeAllocObj(type, helper, clsHnd, op1);
GenTreeAllocObj* node = new (this, GT_ALLOCOBJ) GenTreeAllocObj(type, helper, clsHnd, op1);
return node;
}

Expand All @@ -1184,6 +1185,7 @@ inline GenTree* Compiler::gtNewAllocObjNode(unsigned int helper,
//
// Return Value:
// New GenTreeRuntimeLookup node.

inline GenTree* Compiler::gtNewRuntimeLookup(CORINFO_GENERIC_HANDLE hnd, CorInfoGenericHandleType hndTyp, GenTree* tree)
{
assert(tree != nullptr);
Expand Down
62 changes: 62 additions & 0 deletions src/coreclr/src/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6685,6 +6685,68 @@ GenTree* Compiler::gtNewBitCastNode(var_types type, GenTree* arg)
return node;
}

//------------------------------------------------------------------------
// gtNewAllocObjNode: Helper to create an object allocation node.
//
// Arguments:
// pResolvedToken - Resolved token for the object being allocated
// useParent - true iff the token represents a child of the object's class
//
// Return Value:
// Returns GT_ALLOCOBJ node that will be later morphed into an
// allocation helper call or local variable allocation on the stack.

GenTreeAllocObj* Compiler::gtNewAllocObjNode(CORINFO_RESOLVED_TOKEN* pResolvedToken, BOOL useParent)
{
const BOOL mustRestoreHandle = TRUE;
BOOL* const pRuntimeLookup = nullptr;
bool usingReadyToRunHelper = false;
CorInfoHelpFunc helper;
GenTree* opHandle = impTokenToHandle(pResolvedToken, pRuntimeLookup, mustRestoreHandle, useParent);

#ifdef FEATURE_READYTORUN_COMPILER
CORINFO_CONST_LOOKUP lookup;

if (opts.IsReadyToRun())
{
helper = CORINFO_HELP_READYTORUN_NEW;
CORINFO_LOOKUP_KIND* const pGenericLookupKind = nullptr;
usingReadyToRunHelper =
info.compCompHnd->getReadyToRunHelper(pResolvedToken, pGenericLookupKind, helper, &lookup);
}
#endif

if (!usingReadyToRunHelper)
{
if (opHandle == nullptr)
{
// We must be backing out of an inline.
assert(compDonotInline());
return nullptr;
}

helper = info.compCompHnd->getNewHelper(pResolvedToken, info.compMethodHnd);
}

// TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
// and the newfast call with a single call to a dynamic R2R cell that will:
// 1) Load the context
// 2) Perform the generic dictionary lookup and caching, and generate the appropriate stub
// 3) Allocate and return the new object for boxing
// Reason: performance (today, we'll always use the slow helper for the R2R generics case)

GenTreeAllocObj* allocObj = gtNewAllocObjNode(helper, pResolvedToken->hClass, TYP_REF, opHandle);

#ifdef FEATURE_READYTORUN_COMPILER
if (usingReadyToRunHelper)
{
allocObj->gtEntryPoint = lookup;
}
#endif

return allocObj;
}

/*****************************************************************************
*
* Clones the given tree value and returns a copy of the given tree.
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/src/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -5561,13 +5561,19 @@ struct GenTreeAllocObj final : public GenTreeUnOp
{
unsigned int gtNewHelper; // Value returned by ICorJitInfo::getNewHelper
CORINFO_CLASS_HANDLE gtAllocObjClsHnd;
#ifdef FEATURE_READYTORUN_COMPILER
CORINFO_CONST_LOOKUP gtEntryPoint;
#endif

GenTreeAllocObj(var_types type, unsigned int helper, CORINFO_CLASS_HANDLE clsHnd, GenTree* op)
: GenTreeUnOp(GT_ALLOCOBJ, type, op DEBUGARG(/*largeNode*/ TRUE))
, // This node in most cases will be changed to a call node
gtNewHelper(helper)
, gtAllocObjClsHnd(clsHnd)
{
#ifdef FEATURE_READYTORUN_COMPILER
gtEntryPoint.addr = nullptr;
#endif
}
#if DEBUGGABLE_GENTREE
GenTreeAllocObj() : GenTreeUnOp()
Expand Down
62 changes: 8 additions & 54 deletions src/coreclr/src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6252,36 +6252,11 @@ void Compiler::impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken)
// the opcode stack becomes empty
impBoxTempInUse = true;

#ifdef FEATURE_READYTORUN_COMPILER
bool usingReadyToRunHelper = false;

if (opts.IsReadyToRun())
{
op1 = impReadyToRunHelperToTree(pResolvedToken, CORINFO_HELP_READYTORUN_NEW, TYP_REF);
usingReadyToRunHelper = (op1 != nullptr);
}

if (!usingReadyToRunHelper)
#endif
const BOOL useParent = FALSE;
op1 = gtNewAllocObjNode(pResolvedToken, useParent);
if (op1 == nullptr)
{
// TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
// and the newfast call with a single call to a dynamic R2R cell that will:
// 1) Load the context
// 2) Perform the generic dictionary lookup and caching, and generate the appropriate stub
// 3) Allocate and return the new object for boxing
// Reason: performance (today, we'll always use the slow helper for the R2R generics case)

// Ensure that the value class is restored
op2 = impTokenToHandle(pResolvedToken, nullptr, TRUE /* mustRestoreHandle */);
if (op2 == nullptr)
{
// We must be backing out of an inline.
assert(compDonotInline());
return;
}

op1 = gtNewAllocObjNode(info.compCompHnd->getNewHelper(pResolvedToken, info.compMethodHnd),
pResolvedToken->hClass, TYP_REF, op2);
return;
}

/* Remember that this basic block contains 'new' of an object, and so does this method */
Expand Down Expand Up @@ -13972,32 +13947,11 @@ void Compiler::impImportBlockCode(BasicBlock* block)
}
else
{
#ifdef FEATURE_READYTORUN_COMPILER
if (opts.IsReadyToRun())
{
op1 = impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_NEW, TYP_REF);
usingReadyToRunHelper = (op1 != nullptr);
}

if (!usingReadyToRunHelper)
#endif
const BOOL useParent = TRUE;
op1 = gtNewAllocObjNode(&resolvedToken, useParent);
if (op1 == nullptr)
{
op1 = impParentClassTokenToHandle(&resolvedToken, nullptr, TRUE);
if (op1 == nullptr)
{ // compDonotInline()
return;
}

// TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
// and the newfast call with a single call to a dynamic R2R cell that will:
// 1) Load the context
// 2) Perform the generic dictionary lookup and caching, and generate the appropriate
// stub
// 3) Allocate and return the new object
// Reason: performance (today, we'll always use the slow helper for the R2R generics case)

op1 = gtNewAllocObjNode(info.compCompHnd->getNewHelper(&resolvedToken, info.compMethodHnd),
resolvedToken.hClass, TYP_REF, op1);
return;
}

// Remember that this basic block contains 'new' of an object
Expand Down
29 changes: 25 additions & 4 deletions src/coreclr/src/jit/objectalloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -371,11 +371,32 @@ GenTree* ObjectAllocator::MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* alloc
{
assert(allocObj != nullptr);

GenTree* op1 = allocObj->gtGetOp1();
GenTree* op1 = allocObj->gtGetOp1();
unsigned int helper = allocObj->gtNewHelper;

const bool morphArgs = false;
GenTree* helperCall =
comp->fgMorphIntoHelperCall(allocObj, allocObj->gtNewHelper, comp->gtNewArgList(op1), morphArgs);
GenTreeArgList* args;
#ifdef FEATURE_READYTORUN_COMPILER
CORINFO_CONST_LOOKUP entryPoint = allocObj->gtEntryPoint;
if (helper == CORINFO_HELP_READYTORUN_NEW)
{
args = nullptr;
}
else
#endif
{
args = comp->gtNewArgList(op1);
}

const bool morphArgs = false;
GenTree* helperCall = comp->fgMorphIntoHelperCall(allocObj, allocObj->gtNewHelper, args, morphArgs);

#ifdef FEATURE_READYTORUN_COMPILER
if (entryPoint.addr != nullptr)
{
assert(comp->opts.IsReadyToRun());
helperCall->gtCall.setEntryPoint(entryPoint);
}
#endif

return helperCall;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
<PropertyGroup>
<CrossGenTest>false</CrossGenTest>
<CLRTestBatchPreCommands>
<![CDATA[
$(CLRTestBatchPreCommands)
Expand Down

0 comments on commit a4c765f

Please sign in to comment.