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

Initial support for partially jitted methods #34522

Closed
wants to merge 11 commits into from
1 change: 1 addition & 0 deletions eng/pipelines/common/templates/runtimes/run-test-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ jobs:
scenarios:
- jitosr
- jitosr_stress
- jitosr_uncommon
- jitguardeddevirtualization
- jitehwritethru
- jitobjectstackallocation
Expand Down
11 changes: 6 additions & 5 deletions src/coreclr/src/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,11 @@ TODO: Talk about initializing strutures before use
#endif
#endif

SELECTANY const GUID JITEEVersionIdentifier = { /* 6ae798bf-44bd-4e8a-b8fc-dbe1d1f4029e */
0x6ae798bf,
0x44bd,
0x4e8a,
{0xb8, 0xfc, 0xdb, 0xe1, 0xd1, 0xf4, 0x02, 0x9e}
SELECTANY const GUID JITEEVersionIdentifier = { /* fd36d9ca-54c6-4949-ba01-ae257a9f2da3 */
0xfd36d9ca,
0x54c6,
0x4949,
{0xba, 0x01, 0xae, 0x25, 0x7a, 0x9f, 0x2d, 0xa3}
};

//////////////////////////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -634,6 +634,7 @@ enum CorInfoHelpFunc
CORINFO_HELP_STACK_PROBE, // Probes each page of the allocated stack frame

CORINFO_HELP_PATCHPOINT, // Notify runtime that code has reached a patchpoint
CORINFO_HELP_UNCOMMON_PATCHPOINT, // Notify runtime that code has reached a part of the method that wasn't originally jitted.

CORINFO_HELP_COUNT,
};
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/src/inc/jithelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@
#endif

JITHELPER(CORINFO_HELP_PATCHPOINT, JIT_Patchpoint, CORINFO_HELP_SIG_REG_ONLY)
JITHELPER(CORINFO_HELP_UNCOMMON_PATCHPOINT, JIT_UncommonPatchpoint, CORINFO_HELP_SIG_REG_ONLY)

#undef JITHELPER
#undef DYNAMICJITHELPER
Expand Down
47 changes: 46 additions & 1 deletion src/coreclr/src/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4545,6 +4545,7 @@ class AssertionPropFlowCallback
ASSERT_TP* mJumpDestGen;

BitVecTraits* apTraits;
Compiler* m_compiler;

public:
AssertionPropFlowCallback(Compiler* pCompiler, ASSERT_TP* jumpDestOut, ASSERT_TP* jumpDestGen)
Expand All @@ -4553,6 +4554,7 @@ class AssertionPropFlowCallback
, mJumpDestOut(jumpDestOut)
, mJumpDestGen(jumpDestGen)
, apTraits(pCompiler->apTraits)
, m_compiler(pCompiler)
{
}

Expand Down Expand Up @@ -4590,7 +4592,50 @@ class AssertionPropFlowCallback
// It means we can propagate only assertions that are valid for the whole try region.
void MergeHandler(BasicBlock* block, BasicBlock* firstTryBlock, BasicBlock* lastTryBlock)
{
BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, firstTryBlock->bbAssertionIn);
JITDUMP("AssertionPropCallback::MergeHandler : " FMT_BB " in -> %s, try-beg " FMT_BB " try-end " FMT_BB "\n\n",
block->bbNum, BitVecOps::ToString(apTraits, block->bbAssertionIn), firstTryBlock->bbNum,
lastTryBlock->bbNum);

// For OSR the first try block may not be the try entry block for
// control flow purposes. Find the actual entry blocks.
//
if (m_compiler->opts.IsOSR())
{
JITDUMP("OSR case -- scanning try range for possible non-first try entry BB\n");
assert(firstTryBlock->hasTryIndex());

const unsigned tryIndex = firstTryBlock->getTryIndex();
bool foundEntry = false;

// Scan try extent looking for a block with a predecessor that is not in the try.
for (BasicBlock* tryBlock = firstTryBlock; tryBlock != lastTryBlock->bbNext; tryBlock = tryBlock->bbNext)
{
assert(m_compiler->bbInTryRegions(tryIndex, tryBlock));

for (flowList* pred = tryBlock->bbPreds; pred; pred = pred->flNext)
{
BasicBlock* predBlock = pred->flBlock;

if (!m_compiler->bbInTryRegions(tryIndex, predBlock))
{
JITDUMP("Pred " FMT_BB " of " FMT_BB " is outside try, using latter as entry\n",
predBlock->bbNum, tryBlock->bbNum);
BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, tryBlock->bbAssertionIn);
foundEntry = true;
}
}
}

// We should have found at least one entry. We might see more than one.
assert(foundEntry);
}
else
{
// For the normal case the first block of the try is the try entry block.
//
BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, firstTryBlock->bbAssertionIn);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment could be added here stating that for the normal case we use the assertion state from the first block of the try.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

}

BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, lastTryBlock->bbAssertionOut);
}

Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/src/jit/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ void BasicBlock::dspFlags()
{
printf("ppoint ");
}
if (bbFlags & BBF_UNCOMMON_PATCHPOINT)
{
printf("u-ppoint ");
}
if (bbFlags & BBF_RETLESS_CALL)
{
printf("retless ");
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/src/jit/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ struct BasicBlock : private LIR::Range
#define BBF_DOMINATED_BY_EXCEPTIONAL_ENTRY 0x800000000 // Block is dominated by exceptional entry.
#define BBF_BACKWARD_JUMP_TARGET 0x1000000000 // Block is a target of a backward jump
#define BBF_PATCHPOINT 0x2000000000 // Block is a patchpoint
#define BBF_UNCOMMON_PATCHPOINT 0x4000000000 // Block is an uncommon patchpoint

// clang-format on

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/src/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4906,7 +4906,7 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags
//
void Compiler::generatePatchpointInfo()
{
if (!doesMethodHavePatchpoints())
if (!doesMethodHavePatchpoints() && !doesMethodHaveUncommonPatchpoints())
{
// Nothing to report
return;
Expand Down
31 changes: 21 additions & 10 deletions src/coreclr/src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -6370,16 +6370,17 @@ 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_OBJSTACKALLOC 0x00000040 // Method contains an object allocated on the stack.
#define OMF_HAS_GUARDEDDEVIRT 0x00000080 // Method contains guarded devirtualization candidate
#define OMF_HAS_EXPRUNTIMELOOKUP 0x00000100 // Method contains a runtime lookup to an expandable dictionary.
#define OMF_HAS_PATCHPOINT 0x00000200 // Method contains patchpoints
#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.
#define OMF_HAS_GUARDEDDEVIRT 0x00000080 // Method contains guarded devirtualization candidate
#define OMF_HAS_EXPRUNTIMELOOKUP 0x00000100 // Method contains a runtime lookup to an expandable dictionary.
#define OMF_HAS_PATCHPOINT 0x00000200 // Method contains patchpoints
#define OMF_HAS_UNCOMMON_PATCHPOINT 0x00000400 // Method contains uncommon patchpoints

bool doesMethodHaveFatPointer()
{
Expand Down Expand Up @@ -6446,6 +6447,16 @@ class Compiler
optMethodFlags |= OMF_HAS_PATCHPOINT;
}

bool doesMethodHaveUncommonPatchpoints()
{
return (optMethodFlags & OMF_HAS_UNCOMMON_PATCHPOINT) != 0;
}

void setMethodHasUncommonPatchpoint()
{
optMethodFlags |= OMF_HAS_UNCOMMON_PATCHPOINT;
}

unsigned optMethodFlags;

bool doesMethodHaveNoReturnCalls()
Expand Down
10 changes: 10 additions & 0 deletions src/coreclr/src/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9974,6 +9974,16 @@ void Compiler::fgRemoveEmptyBlocks()
newTryEntry->clearHndIndex();
fgInsertBBafter(tryEntryPrev, newTryEntry);

// Generally this (unreacahble) new entry can appear to "fall through"
// to the next block, but in cases where there's a nested try with an
// out of order handler, the next block may be a handler.So even though
// this new try entry block is unreachable, we need to give it a
// plausible flow target. Simplest is to just mark it as a throw.
if (bbIsHandlerBeg(newTryEntry->bbNext))
{
newTryEntry->bbJumpKind = BBJ_THROW;
}

JITDUMP("OSR: changing start of try region #%u from " FMT_BB " to new " FMT_BB "\n",
XTnum + delCnt, oldTryEntry->bbNum, newTryEntry->bbNum);
}
Expand Down
18 changes: 18 additions & 0 deletions src/coreclr/src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10578,6 +10578,24 @@ void Compiler::impImportBlockCode(BasicBlock* block)
assert((block->bbFlags & BBF_BACKWARD_JUMP_TARGET) == 0);
}

// Mark stack-empty rare blocks to be considered for deferred compilation.
//
// Ideally these are conditionally executed blocks -- if the method is going
// to unconditinally throw there's not as much to be gained by deferring jitting.
// For now, we just screen out the entry bb.
//
// In general we might want track all the IL stack empty points so we can
// propagate rareness back through flow and place the uncommon patchpoints "earlier"
// so there are fewer overall.
//
if ((JitConfig.TC_UncommonPatchpoint() > 0) && opts.jitFlags->IsSet(JitFlags::JIT_FLAG_TIER0) &&
(block != fgFirstBB) && block->isRunRarely() && (verCurrentState.esStackDepth == 0) &&
((block->bbFlags & BBF_PATCHPOINT) == 0))
{
block->bbFlags |= BBF_UNCOMMON_PATCHPOINT;
setMethodHasUncommonPatchpoint();
}

#endif // FEATURE_ON_STACK_REPLACEMENT

/* Walk the opcodes that comprise the basic block */
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/src/jit/jitconfigvalues.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,8 @@ CONFIG_INTEGER(JitGuardedDevirtualizationGuessBestClass, W("JitGuardedDevirtuali
CONFIG_INTEGER(TC_OnStackReplacement, W("TC_OnStackReplacement"), 0)
// Initial patchpoint counter value used by jitted code
CONFIG_INTEGER(TC_OnStackReplacement_InitialCounter, W("TC_OnStackReplacement_InitialCounter"), 1000)
// Enable uncommon patchpoints in Tier0 methods (aka partial compilation -- disabled by default)
CONFIG_INTEGER(TC_UncommonPatchpoint, W("TC_UncommonPatchpoint"), 0)

#if defined(DEBUG)
// JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the
Expand Down
87 changes: 75 additions & 12 deletions src/coreclr/src/jit/patchpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
// Insert patchpoint checks into Tier0 methods, based on locations identified
// during importation (see impImportBlockCode).
//
// Policy decisions implemented here:
// There are now two diffrent types of patchpoints:
// * loop based: enable OSR transitions in loops
// * uncommon: allows partial compilation of original method
//
// Loop patchpoint policy decisions implemented here:
// * One counter per stack frame, regardless of the number of patchpoints.
// * Shared counter value initialized to zero in prolog.
// * Shared counter value initialized to a constant in the prolog.
// * Patchpoint trees fully expanded into jit IR. Deferring expansion could
// lead to more compact code and lessen size overhead for Tier0.
//
Expand All @@ -29,15 +32,13 @@
//
class PatchpointTransformer
{
unsigned ppCounterLclNum;
const int HIGH_PROBABILITY = 99;
unsigned ppCounterLclNum;
Compiler* compiler;

public:
PatchpointTransformer(Compiler* compiler) : compiler(compiler)
PatchpointTransformer(Compiler* compiler) : ppCounterLclNum(BAD_VAR_NUM), compiler(compiler)
{
ppCounterLclNum = compiler->lvaGrabTemp(true DEBUGARG("patchpoint counter"));
compiler->lvaTable[ppCounterLclNum].lvType = TYP_INT;
}

//------------------------------------------------------------------------
Expand All @@ -53,11 +54,9 @@ class PatchpointTransformer
compiler->fgEnsureFirstBBisScratch();
}

BasicBlock* block = compiler->fgFirstBB;
TransformEntry(block);

int count = 0;
for (block = block->bbNext; block != nullptr; block = block->bbNext)

for (BasicBlock* block = compiler->fgFirstBB->bbNext; block != nullptr; block = block->bbNext)
{
if (block->bbFlags & BBF_PATCHPOINT)
{
Expand All @@ -72,11 +71,24 @@ class PatchpointTransformer
continue;
}

JITDUMP("Patchpoint: instrumenting " FMT_BB "\n", block->bbNum);
JITDUMP("Patchpoint: loop patchpoint in " FMT_BB "\n", block->bbNum);
assert(block != compiler->fgFirstBB);
TransformBlock(block);
count++;
}
else if (block->bbFlags & BBF_UNCOMMON_PATCHPOINT)
{
if (compiler->ehGetBlockHndDsc(block) != nullptr)
{
JITDUMP("Patchpoint: skipping uncommon patchpoint for " FMT_BB " as it is in a handler\n",
block->bbNum);
continue;
}

JITDUMP("Patchpoint: uncommon patchpoint in " FMT_BB "\n", block->bbNum);
TransformUncommon(block);
count++;
}
}

return count;
Expand Down Expand Up @@ -119,6 +131,16 @@ class PatchpointTransformer
//
void TransformBlock(BasicBlock* block)
{
// If we haven't allocated the counter temp yet, set it up
if (ppCounterLclNum == BAD_VAR_NUM)
{
ppCounterLclNum = compiler->lvaGrabTemp(true DEBUGARG("patchpoint counter"));
compiler->lvaTable[ppCounterLclNum].lvType = TYP_INT;

// and initialize in the entry block
TransformEntry(compiler->fgFirstBB);
}

// Capture the IL offset
IL_OFFSET ilOffset = block->bbCodeOffs;
assert(ilOffset != BAD_IL_OFFSET);
Expand Down Expand Up @@ -185,6 +207,47 @@ class PatchpointTransformer

compiler->fgNewStmtNearEnd(block, ppCounterAsg);
}

//------------------------------------------------------------------------
// TransformUncommon: delete all the statements in the block and insert
// a call to the uncommon patchpoint helper
//
// S0; S1; S2; ... SN;
//
// ==>
//
// ~~{ S0; ... SN; }~~ (deleted)
// call JIT_UNCOMMON_PATCHPOINT(ilOffset)
//
// Note S0 -- SN are not forever lost -- they will appear in the OSR version
// of the method created when the patchpoint is hit. Also note the patchpoint
// helper call will not return control to this method.
//
void TransformUncommon(BasicBlock* block)
{
// Capture the IL offset
IL_OFFSET ilOffset = block->bbCodeOffs;
assert(ilOffset != BAD_IL_OFFSET);

// Remove all statements from the block.
for (Statement* stmt : block->Statements())
{
compiler->fgRemoveStmt(block, stmt);
}

// Update flow
block->bbJumpKind = BBJ_THROW;
block->bbJumpDest = nullptr;

// Add helper call
//
// call UncommonPPHelper(ilOffset)
GenTree* ilOffsetNode = compiler->gtNewIconNode(ilOffset, TYP_INT);
GenTreeCall::Use* helperArgs = compiler->gtNewCallArgs(ilOffsetNode);
GenTreeCall* helperCall = compiler->gtNewHelperCallNode(CORINFO_HELP_UNCOMMON_PATCHPOINT, TYP_VOID, helperArgs);

compiler->fgNewStmtAtEnd(block, helperCall);
}
};

//------------------------------------------------------------------------
Expand All @@ -200,7 +263,7 @@ class PatchpointTransformer
//
PhaseStatus Compiler::fgTransformPatchpoints()
{
if (!doesMethodHavePatchpoints())
if (!doesMethodHavePatchpoints() && !doesMethodHaveUncommonPatchpoints())
{
JITDUMP("\n -- no patchpoints to transform\n");
return PhaseStatus::MODIFIED_NOTHING;
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/src/tools/crossgen2/jitinterface/jitwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ class CORJIT_FLAGS
uint64_t corJitFlags;
};

static const GUID JITEEVersionIdentifier = { /* 6ae798bf-44bd-4e8a-b8fc-dbe1d1f4029e */
0x6ae798bf,
0x44bd,
0x4e8a,
{0xb8, 0xfc, 0xdb, 0xe1, 0xd1, 0xf4, 0x02, 0x9e}
static const GUID JITEEVersionIdentifier = { /* fd36d9ca-54c6-4949-ba01-ae257a9f2da3 */
0xfd36d9ca,
0x54c6,
0x4949,
{0xba, 0x01, 0xae, 0x25, 0x7a, 0x9f, 0x2d, 0xa3}
};

class Jit
Expand Down
Loading