Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
Implement escape analysis and stack allocation of non-box objects wit…
Browse files Browse the repository at this point in the history
…hout gc fields.

This change implements a conservative flow-insensitive escape analysis and stack allocation
of non-box objects without gc fields.

Handling of objects with gc fields, box objects, and fixed-size arrays is future work.

Escape analysis is based on the one described here: https://www.cc.gatech.edu/~harrold/6340/cs6340_fall2009/Readings/choi99escape.pdf

Main limitations of this version of the escape analysis:
1. The analysis is flow-insensitive.
2. The analysis is intra-procedural and only sees the current method and the inlined methods.
3. The analysis assumes that references passed to non-pure-helper calls escape.
4. The analysis assumes that any references assigned to fields of objects escape.

Some of these limitations will be removed in future work.

I started with prior prototypes from @echesakovMSFT and @AndyAyersMS and extended and refactored
parts of them.

I also added tests for cases that are currently handled or will be handled soon.
  • Loading branch information
erozenfeld committed Nov 7, 2018
1 parent 323d3b3 commit 4e57598
Show file tree
Hide file tree
Showing 12 changed files with 936 additions and 52 deletions.
13 changes: 7 additions & 6 deletions src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -6107,12 +6107,13 @@ class Compiler
}
};

#define OMF_HAS_NEWARRAY 0x00000001 // Method contains 'new' of an array
#define OMF_HAS_NEWOBJ 0x00000002 // Method contains 'new' of an object type.
#define OMF_HAS_ARRAYREF 0x00000004 // Method contains array element loads or stores.
#define OMF_HAS_VTABLEREF 0x00000008 // Method contains method table reference.
#define OMF_HAS_NULLCHECK 0x00000010 // Method contains null check.
#define OMF_HAS_FATPOINTER 0x00000020 // Method contains call, that needs fat pointer transformation.
#define OMF_HAS_NEWARRAY 0x00000001 // Method contains 'new' of an array
#define OMF_HAS_NEWOBJ 0x00000002 // Method contains 'new' of an object type.
#define OMF_HAS_ARRAYREF 0x00000004 // Method contains array element loads or stores.
#define OMF_HAS_VTABLEREF 0x00000008 // Method contains method table reference.
#define OMF_HAS_NULLCHECK 0x00000010 // Method contains null check.
#define OMF_HAS_FATPOINTER 0x00000020 // Method contains call, that needs fat pointer transformation.
#define OMF_HAS_OBJSTACKALLOC 0x00000040 // Method contains an object allocated on the stack.

bool doesMethodHaveFatPointer()
{
Expand Down
1 change: 1 addition & 0 deletions src/jit/compmemkind.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ CompMemKindMacro(Unknown)
CompMemKindMacro(RangeCheck)
CompMemKindMacro(CopyProp)
CompMemKindMacro(SideEffects)
CompMemKindMacro(ObjectAllocator)
//clang-format on

#undef CompMemKindMacro
29 changes: 29 additions & 0 deletions src/jit/gcencode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4245,6 +4245,7 @@ void GCInfo::gcMakeRegPtrTable(
// Or in byref_OFFSET_FLAG for 'byref' pointer tracking
flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
}
gcUpdateFlagForStackAllocatedObjects(flags);

if (varDsc->lvPinned)
{
Expand Down Expand Up @@ -4344,6 +4345,7 @@ void GCInfo::gcMakeRegPtrTable(
{
flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
}
gcUpdateFlagForStackAllocatedObjects(flags);

GcStackSlotBase stackSlotBase = GC_SP_REL;
if (compiler->isFramePointerUsed())
Expand Down Expand Up @@ -4728,6 +4730,7 @@ void GCInfo::gcInfoRecordGCRegStateChange(GcInfoEncoder* gcInfoEncoder,
{
regFlags = (GcSlotFlags)(regFlags | GC_SLOT_INTERIOR);
}
gcUpdateFlagForStackAllocatedObjects(regFlags);

RegSlotIdKey rskey(regNum, regFlags);
GcSlotId regSlotId;
Expand Down Expand Up @@ -4818,6 +4821,8 @@ void GCInfo::gcMakeVarPtrTable(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode
{
flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
}
gcUpdateFlagForStackAllocatedObjects(flags);

if ((lowBits & pinned_OFFSET_FLAG) != 0)
{
flags = (GcSlotFlags)(flags | GC_SLOT_PINNED);
Expand Down Expand Up @@ -4920,6 +4925,30 @@ void GCInfo::gcInfoRecordGCStackArgsDead(GcInfoEncoder* gcInfoEncoder,
}
}

//------------------------------------------------------------------------
// gcUpdateFlagForStackAllocatedObjects: Update the flags to handle a possibly stack-allocated object.
// allocation.
// Arguments:
// flags - flags to update
//
//
// Notes:
// TODO-ObjectStackAllocation: This is a temporary conservative implementation.
// Currently variables pointing to heap and/or stack allocated objects have type TYP_REF so we
// conservatively report them as INTERIOR.
// Ideally we should have the following types for object pointers:
// 1. TYP_I_IMPL for variables always pointing to stack-allocated objects (not reporting to GC)
// 2. TYP_REF for variables always pointing to heap-allocated objects (reporting as normal objects to GC)
// 3. TYP_BYREF for variables that may point to the stack or to the heap (reporting as interior objects to GC)

void GCInfo::gcUpdateFlagForStackAllocatedObjects(GcSlotFlags& flags)
{
if ((compiler->optMethodFlags & OMF_HAS_OBJSTACKALLOC) != 0)
{
flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
}
}

#undef GCENCODER_WITH_LOGGING

#endif // !JIT32_GCENCODER
Expand Down
9 changes: 8 additions & 1 deletion src/jit/gentree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10769,7 +10769,14 @@ void Compiler::gtDispTree(GenTree* tree,
switch (tree->gtOper)
{
case GT_FIELD:
printf(" %s", eeGetFieldName(tree->gtField.gtFldHnd), 0);
if (FieldSeqStore::IsPseudoField(tree->gtField.gtFldHnd))
{
printf(" #PseudoField:0x%x", tree->gtField.gtFldOffset);
}
else
{
printf(" %s", eeGetFieldName(tree->gtField.gtFldHnd), 0);
}

if (tree->gtField.gtFldObj && !topOnly)
{
Expand Down
3 changes: 3 additions & 0 deletions src/jit/jitgcinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ class GCInfo
regPtrDsc* genStackPtrFirst,
regPtrDsc* genStackPtrLast);

// Update the flags for a stack allocated object
void gcUpdateFlagForStackAllocatedObjects(GcSlotFlags& flags);

#endif

#if MEASURE_PTRTAB_SIZE
Expand Down
2 changes: 1 addition & 1 deletion src/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1590,7 +1590,7 @@ bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE
{
if (!compiler->eeIsValueClass(typeHnd))
{
// TODO: Enable promotion of fields of stack-allocated objects.
// TODO-ObjectStackAllocation: Enable promotion of fields of stack-allocated objects.
return false;
}

Expand Down
4 changes: 4 additions & 0 deletions src/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16734,10 +16734,14 @@ void Compiler::fgMorph()
// local variable allocation on the stack.
ObjectAllocator objectAllocator(this); // PHASE_ALLOCATE_OBJECTS

// TODO-ObjectStackAllocation: Enable the optimization for architectures using
// JIT32_GCENCODER (i.e., x86).
#ifndef JIT32_GCENCODER
if (JitConfig.JitObjectStackAllocation() && !opts.MinOpts() && !opts.compDbgCode)
{
objectAllocator.EnableObjectStackAllocation();
}
#endif // JIT32_GCENCODER

objectAllocator.Run();

Expand Down
Loading

0 comments on commit 4e57598

Please sign in to comment.