summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEgor Chesakov <t-egche@microsoft.com>2016-07-21 15:37:41 -0700
committerEgor Chesakov <t-egche@microsoft.com>2016-08-08 17:58:25 -0700
commit3c30aa1642ac52844aa3078fd6bc79a06f5c586a (patch)
tree5a13beb22fce080b9172ee097670e4122eaab755 /src
parent17ced7fea35cb89ce6fe6c0a614c9fe15171bdea (diff)
downloadcoreclr-3c30aa1642ac52844aa3078fd6bc79a06f5c586a.tar.gz
coreclr-3c30aa1642ac52844aa3078fd6bc79a06f5c586a.tar.bz2
coreclr-3c30aa1642ac52844aa3078fd6bc79a06f5c586a.zip
Work towards objects stack allocation: moved allocation part of newobj-lowering into separate phase
1. Introduced `GT_ALLOCOBJ` node to mark places where object allocation happens 2. In `importer.cpp` changed lowering of allocation part of newobj instruction from an allocation helper call to a `GT_ALLOCOBJ` node creation 3. Created new phase `ObjectAllocator` (`PHASE_ALLOCATE_OBJECTS`) and put it right after dominator computing (we will need the information for escape analysis) 4. Current implementation of ObjectAllocator walks over all basic blocks having flag `BBF_HAS_NEWOBJ` set and replaces `GT_ALLOCOBJ` with an allocation helper call
Diffstat (limited to 'src')
-rw-r--r--src/jit/CMakeLists.txt1
-rw-r--r--src/jit/compiler.cpp8
-rw-r--r--src/jit/compiler.h6
-rw-r--r--src/jit/compiler.hpp22
-rw-r--r--src/jit/compphases.h1
-rw-r--r--src/jit/gentree.cpp19
-rw-r--r--src/jit/gentree.h19
-rw-r--r--src/jit/gtlist.h2
-rw-r--r--src/jit/gtstructs.h1
-rw-r--r--src/jit/importer.cpp18
-rw-r--r--src/jit/jit.settings.targets1
-rw-r--r--src/jit/jitpch.h2
-rwxr-xr-xsrc/jit/morph.cpp8
-rw-r--r--src/jit/objectalloc.cpp204
-rw-r--r--src/jit/objectalloc.h86
15 files changed, 389 insertions, 9 deletions
diff --git a/src/jit/CMakeLists.txt b/src/jit/CMakeLists.txt
index b9f0c840a5..4992074443 100644
--- a/src/jit/CMakeLists.txt
+++ b/src/jit/CMakeLists.txt
@@ -51,6 +51,7 @@ set( JIT_SOURCES
lower.cpp
lsra.cpp
morph.cpp
+ objectalloc.cpp
optcse.cpp
optimizer.cpp
rangecheck.cpp
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index 25b2cdc027..a79ddb3480 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -4151,7 +4151,15 @@ void Compiler::compCompile(void * * methodCodePtr,
// Compute reachability sets and dominators.
fgComputeReachability();
+ }
+
+ // Transform each GT_ALLOCOBJ node into either an allocation helper call or
+ // local variable allocation on the stack.
+ ObjectAllocator objectAllocator(this);
+ objectAllocator.Run();
+ if (!opts.MinOpts() && !opts.compDbgCode)
+ {
/* Perform loop inversion (i.e. transform "while" loops into
"repeat" loops) and discover and classify natural loops
(e.g. mark iterative loops as such). Also marks loop blocks
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index d7799d894b..cef9ceec76 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -1364,6 +1364,7 @@ class Compiler
friend class CodeGen;
friend class LclVarDsc;
friend class TempDsc;
+ friend class ObjectAllocator;
#ifndef _TARGET_64BIT_
friend class DecomposeLongs;
@@ -1930,6 +1931,11 @@ public:
GenTreePtr op1,
var_types castType);
+ GenTreePtr gtNewAllocObjNode(unsigned int helper,
+ CORINFO_CLASS_HANDLE clsHnd,
+ var_types type,
+ GenTreePtr op1);
+
//------------------------------------------------------------------------
// Other GenTree functions
diff --git a/src/jit/compiler.hpp b/src/jit/compiler.hpp
index a74bf5e122..acf8896c72 100644
--- a/src/jit/compiler.hpp
+++ b/src/jit/compiler.hpp
@@ -1116,6 +1116,28 @@ GenTreeCall* Compiler::gtNewHelperCallNode(unsigned helper,
return result;
}
+//------------------------------------------------------------------------
+// gtNewAllocObjNode: A little helper to create an object allocation node.
+//
+// Arguments:
+// helper - Value returned by ICorJitInfo::getNewHelper
+// clsHnd - Corresponding class handle
+// type - Tree return type (e.g. TYP_REF)
+// op1 - Node containing an address of VtablePtr
+//
+// Return Value:
+// Returns GT_ALLOCOBJ node that will be later morphed into an
+// allocation helper call or local variable allocation on the stack.
+inline
+GenTreePtr Compiler::gtNewAllocObjNode(unsigned int helper,
+ CORINFO_CLASS_HANDLE clsHnd,
+ var_types type,
+ GenTreePtr op1)
+{
+ GenTreePtr node = new(this, GT_ALLOCOBJ) GenTreeAllocObj(type, helper, clsHnd, op1);
+ return node;
+}
+
/*****************************************************************************/
inline
diff --git a/src/jit/compphases.h b/src/jit/compphases.h
index 999f6cf348..f193d04647 100644
--- a/src/jit/compphases.h
+++ b/src/jit/compphases.h
@@ -31,6 +31,7 @@ CompPhaseNameMacro(PHASE_COMPUTE_EDGE_WEIGHTS, "Compute edge weights (1)",
CompPhaseNameMacro(PHASE_CREATE_FUNCLETS, "Create EH funclets", "EH-FUNC", false, -1)
#endif // FEATURE_EH_FUNCLETS
CompPhaseNameMacro(PHASE_OPTIMIZE_LAYOUT, "Optimize layout", "LAYOUT", false, -1)
+CompPhaseNameMacro(PHASE_ALLOCATE_OBJECTS, "Allocate Objects", "ALLOC-OBJ",false, -1)
CompPhaseNameMacro(PHASE_OPTIMIZE_LOOPS, "Optimize loops", "LOOP-OPT", false, -1)
CompPhaseNameMacro(PHASE_CLONE_LOOPS, "Clone loops", "LP-CLONE", false, -1)
CompPhaseNameMacro(PHASE_UNROLL_LOOPS, "Unroll loops", "UNROLL", false, -1)
diff --git a/src/jit/gentree.cpp b/src/jit/gentree.cpp
index ee8c4c8879..78c9028eaa 100644
--- a/src/jit/gentree.cpp
+++ b/src/jit/gentree.cpp
@@ -272,6 +272,7 @@ void GenTree::InitNodeSize()
GenTree::s_gtNodeSizes[GT_LEA ] = TREE_NODE_SZ_LARGE;
GenTree::s_gtNodeSizes[GT_COPYOBJ ] = TREE_NODE_SZ_LARGE;
GenTree::s_gtNodeSizes[GT_INTRINSIC ] = TREE_NODE_SZ_LARGE;
+ GenTree::s_gtNodeSizes[GT_ALLOCOBJ ] = TREE_NODE_SZ_LARGE;
#if USE_HELPERS_FOR_INT_DIV
GenTree::s_gtNodeSizes[GT_DIV ] = TREE_NODE_SZ_LARGE;
GenTree::s_gtNodeSizes[GT_UDIV ] = TREE_NODE_SZ_LARGE;
@@ -341,6 +342,7 @@ void GenTree::InitNodeSize()
static_assert_no_msg(sizeof(GenTreeArgPlace) <= TREE_NODE_SZ_SMALL);
static_assert_no_msg(sizeof(GenTreeLabel) <= TREE_NODE_SZ_SMALL);
static_assert_no_msg(sizeof(GenTreePhiArg) <= TREE_NODE_SZ_SMALL);
+ static_assert_no_msg(sizeof(GenTreeAllocObj) <= TREE_NODE_SZ_LARGE); // *** large node
#ifndef FEATURE_UNIX_AMD64_STRUCT_PASSING
static_assert_no_msg(sizeof(GenTreePutArgStk) <= TREE_NODE_SZ_SMALL);
#else // FEATURE_UNIX_AMD64_STRUCT_PASSING
@@ -2145,7 +2147,10 @@ AGAIN:
case GT_INDEX:
hash += tree->gtIndex.gtIndElemSize;
break;
-
+ case GT_ALLOCOBJ:
+ hash = genTreeHashAdd(hash, static_cast<unsigned>(reinterpret_cast<uintptr_t>(tree->gtAllocObj.gtAllocObjClsHnd)));
+ hash = genTreeHashAdd(hash, tree->gtAllocObj.gtNewHelper);
+ break;
// For the ones below no extra argument matters for comparison.
case GT_BOX:
@@ -6754,6 +6759,13 @@ GenTreePtr Compiler::gtCloneExpr(GenTree * tree,
}
break;
+ case GT_ALLOCOBJ:
+ {
+ GenTreeAllocObj* asAllocObj = tree->AsAllocObj();
+ copy = new (this, GT_ALLOCOBJ) GenTreeAllocObj(tree->TypeGet(), asAllocObj->gtNewHelper, asAllocObj->gtAllocObjClsHnd, asAllocObj->gtOp1);
+ }
+ break;
+
case GT_ARR_LENGTH:
copy = new (this, GT_ARR_LENGTH) GenTreeArrLen(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtArrLen.ArrLenOffset());
break;
@@ -11062,6 +11074,11 @@ GenTreePtr Compiler::gtFoldExprConst(GenTreePtr tree)
}
#endif // FEATURE_SIMD
+ if (tree->gtOper == GT_ALLOCOBJ)
+ {
+ return tree;
+ }
+
if (kind & GTK_UNOP)
{
assert(op1->OperKind() & GTK_CONST);
diff --git a/src/jit/gentree.h b/src/jit/gentree.h
index 5db3d726aa..9058997f6a 100644
--- a/src/jit/gentree.h
+++ b/src/jit/gentree.h
@@ -4221,6 +4221,25 @@ struct GenTreeCopyOrReload : public GenTreeUnOp
#endif
};
+// Represents GT_ALLOCOBJ node
+
+struct GenTreeAllocObj final : public GenTreeUnOp
+{
+ unsigned int gtNewHelper; // Value returned by ICorJitInfo::getNewHelper
+ CORINFO_CLASS_HANDLE gtAllocObjClsHnd;
+
+ GenTreeAllocObj(var_types type, unsigned int helper, CORINFO_CLASS_HANDLE clsHnd, GenTreePtr 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)
+ {}
+#if DEBUGGABLE_GENTREE
+ GenTreeAllocObj() : GenTreeUnOp() {}
+#endif
+};
+
+
//------------------------------------------------------------------------
// Deferred inline functions of GenTree -- these need the subtypes above to
// be defined already.
diff --git a/src/jit/gtlist.h b/src/jit/gtlist.h
index b56952a4b2..97db5b3c0e 100644
--- a/src/jit/gtlist.h
+++ b/src/jit/gtlist.h
@@ -85,6 +85,8 @@ GTNODE(SIMD_CHK , "simdChk" ,0,GTK_SPECIAL) // Compare wheth
// does the compare, so that it can be more easily optimized. But that involves generating qmarks at import time...
#endif // FEATURE_SIMD
+GTNODE(ALLOCOBJ , "allocObj" ,0,GTK_UNOP|GTK_EXOP) // object allocator
+
//-----------------------------------------------------------------------------
// Binary operators (2 operands):
//-----------------------------------------------------------------------------
diff --git a/src/jit/gtstructs.h b/src/jit/gtstructs.h
index c76e69f417..ae7311ace5 100644
--- a/src/jit/gtstructs.h
+++ b/src/jit/gtstructs.h
@@ -101,6 +101,7 @@ GTSTRUCT_1(CpBlk , GT_COPYBLK)
#ifdef FEATURE_SIMD
GTSTRUCT_1(SIMD , GT_SIMD)
#endif // FEATURE_SIMD
+GTSTRUCT_1(AllocObj , GT_ALLOCOBJ)
/*****************************************************************************/
#undef GTSTRUCT_0
#undef GTSTRUCT_1
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index 445844eabe..038fb98ab2 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -11852,18 +11852,22 @@ DO_LDFTN:
// 3) Allocate and return the new object
// Reason: performance (today, we'll always use the slow helper for the R2R generics case)
- op1 = gtNewHelperCallNode( info.compCompHnd->getNewHelper(&resolvedToken, info.compMethodHnd),
- TYP_REF, 0,
- gtNewArgList(op1));
+ op1 = gtNewAllocObjNode( info.compCompHnd->getNewHelper(&resolvedToken, info.compMethodHnd),
+ resolvedToken.hClass, TYP_REF, op1 );
}
- /* Remember that this basic block contains 'new' of an object */
+ // Remember that this basic block contains 'new' of an object
block->bbFlags |= BBF_HAS_NEWOBJ;
optMethodFlags |= OMF_HAS_NEWOBJ;
- /* Append the assignment to the temp/local. Dont need to spill
- at all as we are just calling an EE-Jit helper which can only
- cause an (async) OutOfMemoryException */
+ // Append the assignment to the temp/local. Dont need to spill
+ // at all as we are just calling an EE-Jit helper which can only
+ // cause an (async) OutOfMemoryException.
+
+ // We assign the newly allocated object (by a GT_ALLOCOBJ node)
+ // to a temp. Note that the pattern "temp = allocObj" is required
+ // by ObjectAllocator phase to be able to determine GT_ALLOCOBJ nodes
+ // without exhaustive walk over all expressions.
impAssignTempGen(lclNum, op1, (unsigned)CHECK_SPILL_NONE);
diff --git a/src/jit/jit.settings.targets b/src/jit/jit.settings.targets
index 2ffcfcb69c..b8e109e761 100644
--- a/src/jit/jit.settings.targets
+++ b/src/jit/jit.settings.targets
@@ -84,6 +84,7 @@
<CppCompile Include="..\inlinepolicy.cpp" />
<CppCompile Include="..\jitconfig.cpp" />
<CppCompile Include="..\hostallocator.cpp" />
+ <CppCompile Include="..\objectalloc.cpp" />
<CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='True'" Include="..\CodeGenLegacy.cpp" />
<CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\Lower.cpp" />
<CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\LSRA.cpp" />
diff --git a/src/jit/jitpch.h b/src/jit/jitpch.h
index 58c9c6bd95..e7966de92e 100644
--- a/src/jit/jitpch.h
+++ b/src/jit/jitpch.h
@@ -34,4 +34,4 @@
#include "blockset.h"
#include "bitvec.h"
#include "inline.h"
-
+#include "objectalloc.h"
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index 9570d48518..33dfb6dcfc 100755
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -64,6 +64,8 @@ GenTreePtr Compiler::fgMorphIntoHelperCall(GenTreePtr tree,
tree->ChangeOper(GT_CALL);
tree->gtFlags |= GTF_CALL;
+ if (args)
+ tree->gtFlags |= (args->gtFlags & GTF_ALL_EFFECT);
tree->gtCall.gtCallType = CT_HELPER;
tree->gtCall.gtCallMethHnd = eeFindHelper(helper);
tree->gtCall.gtCallArgs = args;
@@ -79,6 +81,12 @@ GenTreePtr Compiler::fgMorphIntoHelperCall(GenTreePtr tree,
tree->gtCall.gtCallRegUsedMask = RBM_NONE;
#endif // LEGACY_BACKEND
+#if DEBUG
+ // Helper calls are never candidates.
+
+ tree->gtCall.gtInlineObservation = InlineObservation::CALLSITE_IS_CALL_TO_HELPER;
+#endif // DEBUG
+
#ifdef FEATURE_READYTORUN_COMPILER
tree->gtCall.gtEntryPoint.addr = nullptr;
#endif
diff --git a/src/jit/objectalloc.cpp b/src/jit/objectalloc.cpp
new file mode 100644
index 0000000000..38a441c6f0
--- /dev/null
+++ b/src/jit/objectalloc.cpp
@@ -0,0 +1,204 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XX XX
+XX ObjectAllocator XX
+XX XX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+*/
+
+#include "jitpch.h"
+#ifdef _MSC_VER
+#pragma hdrstop
+#endif
+
+//===============================================================================
+
+//------------------------------------------------------------------------
+// DoPhase: Run analysis (if object stack allocation is enabled) and then
+// morph each GT_ALLOCOBJ node either into an allocation helper
+// call or stack allocation.
+// Notes:
+// Runs only if Compiler::optMethodFlags has flag OMF_HAS_NEWOBJ set.
+void ObjectAllocator::DoPhase()
+{
+ if ((comp->optMethodFlags & OMF_HAS_NEWOBJ) == 0)
+ {
+ return;
+ }
+
+ if (IsObjectStackAllocationEnabled())
+ {
+ DoAnalysis();
+ }
+
+ MorphAllocObjNodes();
+}
+
+//------------------------------------------------------------------------
+// DoAnalysis: Walk over basic blocks of the method and detect all local
+// variables that can be allocated on the stack.
+//
+// Assumptions:
+// Must be run after the dominators have been computed (we need this
+// information to detect loops).
+void ObjectAllocator::DoAnalysis()
+{
+ assert(m_IsObjectStackAllocationEnabled);
+ assert(comp->fgDomsComputed);
+ // TODO-ObjectStackAllocation
+ NYI("DoAnalysis");
+}
+
+//------------------------------------------------------------------------
+// MorphAllocObjNodes: Morph each GT_ALLOCOBJ node either into an
+// allocation helper call or stack allocation.
+//
+// Notes:
+// Runs only over the blocks having bbFlags BBF_HAS_NEWOBJ set.
+void ObjectAllocator::MorphAllocObjNodes()
+{
+ BasicBlock* block;
+
+ foreach_block(comp, block)
+ {
+ if ((block->bbFlags & BBF_HAS_NEWOBJ) == 0)
+ {
+ continue;
+ }
+
+ for (GenTreeStmt* stmt = block->firstStmt();
+ stmt;
+ stmt = stmt->gtNextStmt)
+ {
+ GenTreePtr stmtExpr = stmt->gtStmtExpr;
+ GenTreePtr op2 = nullptr;
+
+ bool canonicalAllocObjFound = false;
+
+ if (stmtExpr->OperGet() == GT_ASG && stmtExpr->TypeGet() == TYP_REF)
+ {
+ op2 = stmtExpr->gtGetOp2();
+
+ if (op2->OperGet() == GT_ALLOCOBJ)
+ {
+ canonicalAllocObjFound = true;
+ }
+ }
+
+ if (canonicalAllocObjFound)
+ {
+ //------------------------------------------------------------------------
+ // We expect the following expression tree at this point
+ // * GT_STMT void (top level)
+ // | /--* GT_ALLOCOBJ ref
+ // \--* GT_ASG ref
+ // \--* GT_LCL_VAR ref
+ //------------------------------------------------------------------------
+
+ GenTreePtr op1 = stmtExpr->gtGetOp1();
+
+ assert(op1->OperGet() == GT_LCL_VAR);
+ assert(op1->TypeGet() == TYP_REF);
+ assert(op2 != nullptr);
+ assert(op2->OperGet() == GT_ALLOCOBJ);
+
+ GenTreeAllocObj* asAllocObj = op2->AsAllocObj();
+ unsigned int lclNum = op1->AsLclVar()->GetLclNum();
+
+ if (IsObjectStackAllocationEnabled() && CanAllocateLclVarOnStack(lclNum))
+ {
+ op2 = MorphAllocObjNodeIntoStackAlloc(asAllocObj, block, stmt);
+ }
+ else
+ {
+ op2 = MorphAllocObjNodeIntoHelperCall(asAllocObj);
+ }
+
+ // Propagate flags of op2 to its parent.
+ stmtExpr->gtOp.gtOp2 = op2;
+ stmtExpr->gtFlags |= op2->gtFlags & GTF_ALL_EFFECT;
+ }
+#ifdef DEBUG
+ else
+ {
+ // We assume that GT_ALLOCOBJ nodes are always present in the
+ // canonical form.
+ comp->fgWalkTreePre(&stmt->gtStmtExpr, AssertWhenAllocObjFoundVisitor);
+ }
+#endif // DEBUG
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// MorphAllocObjNodeIntoHelperCall: Morph a GT_ALLOCOBJ node into an
+// allocation helper call.
+//
+// Arguments:
+// allocObj - GT_ALLOCOBJ that will be replaced by helper call.
+//
+// Return Value:
+// Address of helper call node (can be the same as allocObj).
+//
+// Notes:
+// Must update parents flags after this.
+GenTreePtr ObjectAllocator::MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* allocObj)
+{
+ assert(allocObj != nullptr);
+
+ GenTreePtr op1 = allocObj->gtGetOp1();
+
+ GenTreePtr helperCall = comp->fgMorphIntoHelperCall(
+ allocObj, allocObj->gtNewHelper, comp->gtNewArgList(op1));
+
+ return helperCall;
+}
+
+//------------------------------------------------------------------------
+// MorphAllocObjNodeIntoStackAlloc: Morph a GT_ALLOCOBJ node into stack
+// allocation.
+// Arguments:
+// allocObj - GT_ALLOCOBJ that will be replaced by helper call.
+// block - a basic block where allocObj is
+// stmt - a statement where allocObj is
+//
+// Return Value:
+// Address of tree doing stack allocation (can be the same as allocObj).
+//
+// Notes:
+// Must update parents flags after this.
+// This function can insert additional statements before stmt.
+GenTreePtr ObjectAllocator::MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* allocObj, BasicBlock* block, GenTreeStmt* stmt)
+{
+ assert(allocObj != nullptr);
+ assert(m_AnalysisDone);
+
+ // TODO-StackAllocation
+ NYI("MorphAllocObjIntoStackAlloc");
+
+ return allocObj;
+}
+
+#ifdef DEBUG
+
+//------------------------------------------------------------------------
+// AssertWhenAllocObjFoundVisitor: Look for a GT_ALLOCOBJ node and assert
+// when found one.
+Compiler::fgWalkResult ObjectAllocator::AssertWhenAllocObjFoundVisitor(GenTreePtr* pTree, Compiler::fgWalkData* data)
+{
+ GenTreePtr tree = *pTree;
+
+ assert(tree != nullptr);
+ assert(tree->OperGet() != GT_ALLOCOBJ);
+
+ return Compiler::fgWalkResult::WALK_CONTINUE;
+}
+
+#endif // DEBUG
+
+//===============================================================================
diff --git a/src/jit/objectalloc.h b/src/jit/objectalloc.h
new file mode 100644
index 0000000000..a9707f326d
--- /dev/null
+++ b/src/jit/objectalloc.h
@@ -0,0 +1,86 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XX XX
+XX ObjectAllocator XX
+XX XX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+*/
+
+/*****************************************************************************/
+#ifndef OBJECTALLOC_H
+#define OBJECTALLOC_H
+/*****************************************************************************/
+
+//===============================================================================
+#include "phase.h"
+
+class ObjectAllocator final : public Phase
+{
+ //===============================================================================
+ // Data members
+ bool m_IsObjectStackAllocationEnabled;
+ bool m_AnalysisDone;
+ //===============================================================================
+ // Methods
+public:
+ ObjectAllocator(Compiler* comp);
+ bool IsObjectStackAllocationEnabled() const;
+ void EnableObjectStackAllocation();
+
+protected:
+ virtual void DoPhase() override;
+
+private:
+ bool CanAllocateLclVarOnStack(unsigned int lclNum) const;
+ void DoAnalysis();
+ void MorphAllocObjNodes();
+ GenTreePtr MorphAllocObjNodeIntoHelperCall(GenTreeAllocObj* allocObj);
+ GenTreePtr MorphAllocObjNodeIntoStackAlloc(GenTreeAllocObj* allocObj, BasicBlock* block, GenTreeStmt* stmt);
+#ifdef DEBUG
+ static Compiler::fgWalkResult AssertWhenAllocObjFoundVisitor(GenTreePtr* pTree, Compiler::fgWalkData* data);
+#endif // DEBUG
+};
+
+//===============================================================================
+
+inline
+ObjectAllocator::ObjectAllocator(Compiler* comp) :
+ Phase(comp, "Allocate Objects", PHASE_ALLOCATE_OBJECTS),
+ m_IsObjectStackAllocationEnabled(false),
+ m_AnalysisDone(false)
+{
+}
+
+inline
+bool ObjectAllocator::IsObjectStackAllocationEnabled() const
+{
+ return m_IsObjectStackAllocationEnabled;
+}
+
+inline
+void ObjectAllocator::EnableObjectStackAllocation()
+{
+ m_IsObjectStackAllocationEnabled = true;
+}
+
+//------------------------------------------------------------------------
+// CanAllocateLclVarOnStack: Returns true iff local variable can not
+// potentially escape from the method and
+// can be allocated on the stack.
+inline
+bool ObjectAllocator::CanAllocateLclVarOnStack(unsigned int lclNum) const
+{
+ assert(m_AnalysisDone);
+ // TODO-ObjectStackAllocation
+ NYI("CanAllocateLclVarOnStack");
+ return false;
+}
+
+//===============================================================================
+
+#endif // OBJECTALLOC_H