summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/jit/compiler.cpp2
-rw-r--r--src/jit/flowgraph.cpp4
-rw-r--r--src/jit/importer.cpp2
-rw-r--r--src/jit/inline.cpp49
-rw-r--r--src/jit/inline.def4
-rw-r--r--src/jit/inline.h30
-rw-r--r--src/jit/inlinepolicy.cpp388
-rw-r--r--src/jit/inlinepolicy.h29
-rw-r--r--src/jit/jitconfigvalues.h2
9 files changed, 475 insertions, 35 deletions
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index 3b2c3d6b3d..83853e6b50 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -683,7 +683,7 @@ void Compiler::compShutdown()
emitter::emitDone();
#if defined(DEBUG) || defined(INLINE_DATA)
- // Finish off any in-progress inline xml
+ // Finish reading and/or writing inline xml
InlineStrategy::FinalizeXml();
#endif // defined(DEBUG) || defined(INLINE_DATA)
diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp
index 2cfed2e23c..27ed8a449b 100644
--- a/src/jit/flowgraph.cpp
+++ b/src/jit/flowgraph.cpp
@@ -21429,7 +21429,7 @@ void Compiler::fgInline()
if ((expr->gtOper == GT_CALL) && ((expr->gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0))
{
GenTreeCall* call = expr->AsCall();
- InlineResult inlineResult(this, call, "fgInline");
+ InlineResult inlineResult(this, call, stmt->gtInlineContext, "fgInline");
fgMorphStmt = stmt;
@@ -21555,7 +21555,7 @@ Compiler::fgWalkResult Compiler::fgFindNonInlineCandidate(GenTreePtr* pTree
void Compiler::fgNoteNonInlineCandidate(GenTreePtr tree,
GenTreeCall* call)
{
- InlineResult inlineResult(this, call, "fgNotInlineCandidate");
+ InlineResult inlineResult(this, call, nullptr, "fgNotInlineCandidate");
InlineObservation currentObservation = InlineObservation::CALLSITE_NOT_CANDIDATE;
// Try and recover the reason left behind when the jit decided
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index 3ba0cd01f3..a97e7c3418 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -16461,7 +16461,7 @@ void Compiler::impMarkInlineCandidate(GenTreePtr callNode,
}
GenTreeCall* call = callNode->AsCall();
- InlineResult inlineResult(this, call, "impMarkInlineCandidate");
+ InlineResult inlineResult(this, call, nullptr, "impMarkInlineCandidate");
// Don't inline if not optimizing root method
if (opts.compDbgCode)
diff --git a/src/jit/inline.cpp b/src/jit/inline.cpp
index 1f728dd59f..1acea8e1f2 100644
--- a/src/jit/inline.cpp
+++ b/src/jit/inline.cpp
@@ -7,6 +7,8 @@
#pragma hdrstop
#endif
+#include "inlinepolicy.h"
+
// Lookup table for inline description strings
static const char* InlineDescriptions[] =
@@ -441,7 +443,7 @@ void InlineContext::DumpData(unsigned indent)
if (m_Parent == nullptr)
{
// Root method... cons up a policy so we can display the name
- InlinePolicy* policy = InlinePolicy::GetPolicy(compiler, true);
+ InlinePolicy* policy = InlinePolicy::GetPolicy(compiler, nullptr, true);
printf("\nInlines [%u] into \"%s\" [%s]\n",
m_InlineStrategy->GetInlineCount(),
calleeName,
@@ -531,20 +533,23 @@ void InlineContext::DumpXml(FILE* file, unsigned indent)
// InlineResult: Construct an InlineResult to evaluate a particular call
// for inlining.
//
-// Arguments
-// compiler - the compiler instance examining a call for inlining
-// call - the call in question
-// context - descrptive string to describe the context of the decision
-
-InlineResult::InlineResult(Compiler* compiler,
- GenTreeCall* call,
- const char* context)
+// Arguments:
+// compiler - the compiler instance examining a call for inlining
+// call - the call in question
+// inlineContext - the inline context for the inline, if known
+// description - string describing the context of the decision
+
+InlineResult::InlineResult(Compiler* compiler,
+ GenTreeCall* call,
+ InlineContext* inlineContext,
+ const char* description)
: m_RootCompiler(nullptr)
, m_Policy(nullptr)
, m_Call(call)
+ , m_InlineContext(inlineContext)
, m_Caller(nullptr)
, m_Callee(nullptr)
- , m_Context(context)
+ , m_Description(description)
, m_Reported(false)
{
// Set the compiler instance
@@ -552,7 +557,7 @@ InlineResult::InlineResult(Compiler* compiler,
// Set the policy
const bool isPrejitRoot = false;
- m_Policy = InlinePolicy::GetPolicy(m_RootCompiler, isPrejitRoot);
+ m_Policy = InlinePolicy::GetPolicy(m_RootCompiler, m_InlineContext, isPrejitRoot);
// Get method handle for caller. Note we use the
// handle for the "immediate" caller here.
@@ -570,9 +575,9 @@ InlineResult::InlineResult(Compiler* compiler,
// method as a possible inline candidate, while prejtting.
//
// Arguments:
-// compiler - the compiler instance doing the prejitting
-// method - the method in question
-// context - descrptive string to describe the context of the decision
+// compiler - the compiler instance doing the prejitting
+// method - the method in question
+// description - string describing the context of the decision
//
// Notes:
// Used only during prejitting to try and pre-identify methods that
@@ -583,13 +588,14 @@ InlineResult::InlineResult(Compiler* compiler,
InlineResult::InlineResult(Compiler* compiler,
CORINFO_METHOD_HANDLE method,
- const char* context)
+ const char* description)
: m_RootCompiler(nullptr)
, m_Policy(nullptr)
, m_Call(nullptr)
+ , m_InlineContext(nullptr)
, m_Caller(nullptr)
, m_Callee(method)
- , m_Context(context)
+ , m_Description(description)
, m_Reported(false)
{
// Set the compiler instance
@@ -597,7 +603,7 @@ InlineResult::InlineResult(Compiler* compiler,
// Set the policy
const bool isPrejitRoot = true;
- m_Policy = InlinePolicy::GetPolicy(m_RootCompiler, isPrejitRoot);
+ m_Policy = InlinePolicy::GetPolicy(m_RootCompiler, nullptr, isPrejitRoot);
}
//------------------------------------------------------------------------
@@ -637,7 +643,7 @@ void InlineResult::Report()
callee = (m_Callee == nullptr) ? "n/a" : m_RootCompiler->eeGetMethodFullName(m_Callee);
- JITDUMP(format, m_Context, ResultString(), ReasonString(), caller, callee);
+ JITDUMP(format, m_Description, ResultString(), ReasonString(), caller, callee);
}
// If the inline failed, leave information on the call so we can
@@ -686,7 +692,7 @@ void InlineResult::Report()
if (IsDecided())
{
const char* format = "INLINER: during '%s' result '%s' reason '%s'\n";
- JITLOG_THIS(m_RootCompiler, (LL_INFO100000, format, m_Context, ResultString(), ReasonString()));
+ JITLOG_THIS(m_RootCompiler, (LL_INFO100000, format, m_Description, ResultString(), ReasonString()));
COMP_HANDLE comp = m_RootCompiler->info.compCompHnd;
comp->reportInliningDecision(m_Caller, m_Callee, Result(), ReasonString());
}
@@ -1214,7 +1220,7 @@ void InlineStrategy::DumpData()
{
assert(limit <= 0);
const bool isPrejitRoot = (opts.eeFlags & CORJIT_FLG_PREJIT) != 0;
- m_LastSuccessfulPolicy = InlinePolicy::GetPolicy(m_Compiler, isPrejitRoot);
+ m_LastSuccessfulPolicy = InlinePolicy::GetPolicy(m_Compiler, nullptr, isPrejitRoot);
// Add in a bit of data....
const bool isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0;
@@ -1371,6 +1377,9 @@ void InlineStrategy::FinalizeXml(FILE* file)
// Workaroud compShutdown getting called twice.
s_HasDumpedXmlHeader = false;
}
+
+ // Finalize reading inline xml
+ ReplayPolicy::FinalizeXml();
}
#endif // defined(DEBUG) || defined(INLINE_DATA)
diff --git a/src/jit/inline.def b/src/jit/inline.def
index cd4f98a213..4ee03bb3bc 100644
--- a/src/jit/inline.def
+++ b/src/jit/inline.def
@@ -49,6 +49,7 @@ INLINE_OBSERVATION(IS_SYNCHRONIZED, bool, "is synchronized",
INLINE_OBSERVATION(IS_VM_NOINLINE, bool, "noinline per VM", FATAL, CALLEE)
INLINE_OBSERVATION(LACKS_RETURN, bool, "no return opcode", FATAL, CALLEE)
INLINE_OBSERVATION(LDFLD_NEEDS_HELPER, bool, "ldfld needs helper", FATAL, CALLEE)
+INLINE_OBSERVATION(LOG_REPLAY_REJECT, bool, "rejected by log replay", FATAL, CALLEE)
INLINE_OBSERVATION(MARKED_AS_SKIPPED, bool, "skipped by complus request", FATAL, CALLEE)
INLINE_OBSERVATION(MAXSTACK_TOO_BIG, bool, "maxstack too big" , FATAL, CALLEE)
INLINE_OBSERVATION(NEEDS_SECURITY_CHECK, bool, "needs security check", FATAL, CALLEE)
@@ -84,6 +85,7 @@ INLINE_OBSERVATION(IS_FORCE_INLINE, bool, "aggressive inline attribu
INLINE_OBSERVATION(IS_INSTANCE_CTOR, bool, "instance constructor", INFORMATION, CALLEE)
INLINE_OBSERVATION(IS_PROFITABLE_INLINE, bool, "profitable inline", INFORMATION, CALLEE)
INLINE_OBSERVATION(IS_SIZE_DECREASING_INLINE, bool, "size decreasing inline", INFORMATION, CALLEE)
+INLINE_OBSERVATION(LOG_REPLAY_ACCEPT, bool, "accepted by log replay", INFORMATION, CALLEE)
INLINE_OBSERVATION(LOOKS_LIKE_WRAPPER, bool, "thin wrapper around a call", INFORMATION, CALLEE)
INLINE_OBSERVATION(MAXSTACK, int, "maxstack", INFORMATION, CALLEE)
INLINE_OBSERVATION(OPCODE, int, "next opcode in IL stream", INFORMATION, CALLEE)
@@ -136,6 +138,7 @@ INLINE_OBSERVATION(IS_WITHIN_FILTER, bool, "within filterregion",
INLINE_OBSERVATION(LDARGA_NOT_LOCAL_VAR, bool, "ldarga not on local var", FATAL, CALLSITE)
INLINE_OBSERVATION(LDFLD_NEEDS_HELPER, bool, "ldfld needs helper", FATAL, CALLSITE)
INLINE_OBSERVATION(LDVIRTFN_ON_NON_VIRTUAL, bool, "ldvirtfn on non-virtual", FATAL, CALLSITE)
+INLINE_OBSERVATION(LOG_REPLAY_REJECT, bool, "rejected by log replay", FATAL, CALLSITE)
INLINE_OBSERVATION(NOT_CANDIDATE, bool, "not inline candidate", FATAL, CALLSITE)
INLINE_OBSERVATION(NOT_PROFITABLE_INLINE, bool, "unprofitable inline", FATAL, CALLSITE)
INLINE_OBSERVATION(OVER_BUDGET, bool, "inline exceeds budget", FATAL, CALLSITE)
@@ -156,6 +159,7 @@ INLINE_OBSERVATION(DEPTH, int, "depth",
INLINE_OBSERVATION(FREQUENCY, int, "execution frequency", INFORMATION, CALLSITE)
INLINE_OBSERVATION(IS_PROFITABLE_INLINE, bool, "profitable inline", INFORMATION, CALLSITE)
INLINE_OBSERVATION(IS_SIZE_DECREASING_INLINE, bool, "size decreasing inline", INFORMATION, CALLSITE)
+INLINE_OBSERVATION(LOG_REPLAY_ACCEPT, bool, "accepted by log replay", INFORMATION, CALLSITE)
INLINE_OBSERVATION(RANDOM_ACCEPT, bool, "random accept", INFORMATION, CALLSITE)
// ------ Final Sentinel -------
diff --git a/src/jit/inline.h b/src/jit/inline.h
index 19c3e2d09a..cac0fc8145 100644
--- a/src/jit/inline.h
+++ b/src/jit/inline.h
@@ -218,7 +218,7 @@ class InlinePolicy
public:
// Factory method for getting policies
- static InlinePolicy* GetPolicy(Compiler* compiler, bool isPrejitRoot);
+ static InlinePolicy* GetPolicy(Compiler* compiler, InlineContext* context, bool isPrejitRoot);
// Obligatory virtual dtor
virtual ~InlinePolicy() {}
@@ -289,13 +289,14 @@ public:
// particular call for inlining.
InlineResult(Compiler* compiler,
GenTreeCall* call,
- const char* context);
+ InlineContext* inlineContext,
+ const char* description);
// Construct a new InlineResult to evaluate a particular
// method to see if it is inlineable.
InlineResult(Compiler* compiler,
CORINFO_METHOD_HANDLE method,
- const char* context);
+ const char* description);
// Has the policy determined this inline should fail?
bool IsFailure() const
@@ -442,6 +443,12 @@ public:
m_Reported = true;
}
+ // Get the InlineContext for this inline
+ InlineContext* GetInlineContext() const
+ {
+ return m_InlineContext;
+ }
+
private:
// No copying or assignment allowed.
@@ -454,9 +461,10 @@ private:
Compiler* m_RootCompiler;
InlinePolicy* m_Policy;
GenTreeCall* m_Call;
+ InlineContext* m_InlineContext;
CORINFO_METHOD_HANDLE m_Caller; // immediate caller's handle
CORINFO_METHOD_HANDLE m_Callee;
- const char* m_Context;
+ const char* m_Description;
bool m_Reported;
};
@@ -570,6 +578,12 @@ public:
// Dump full subtree in xml format
void DumpXml(FILE* file = stderr, unsigned indent = 0);
+ // Get callee handle
+ CORINFO_METHOD_HANDLE GetCallee() const
+ {
+ return m_Callee;
+ }
+
#endif // defined(DEBUG) || defined(INLINE_DATA)
// Get the parent context for this context.
@@ -591,7 +605,7 @@ public:
}
// Get the observation that supported or disqualified this inline.
- InlineObservation GetObservation()
+ InlineObservation GetObservation() const
{
return m_Observation;
}
@@ -608,6 +622,12 @@ public:
return m_CodeSizeEstimate;
}
+ // True if this is the root context
+ bool IsRoot() const
+ {
+ return m_Parent == nullptr;
+ }
+
private:
InlineContext(InlineStrategy* strategy);
diff --git a/src/jit/inlinepolicy.cpp b/src/jit/inlinepolicy.cpp
index 0e4b436955..deaa849369 100644
--- a/src/jit/inlinepolicy.cpp
+++ b/src/jit/inlinepolicy.cpp
@@ -14,19 +14,23 @@
// getPolicy: Factory method for getting an InlinePolicy
//
// Arguments:
-// compiler - the compiler instance that will evaluate inlines
-// isPrejitRoot - true if this policy is evaluating a prejit root
+// compiler - the compiler instance that will evaluate inlines
+// inlineContext - the context of the inline
+// isPrejitRoot - true if this policy is evaluating a prejit root
//
// Return Value:
-// InlinePolicy to use in evaluating the inlines
+// InlinePolicy to use in evaluating an inline.
//
// Notes:
// Determines which of the various policies should apply,
// and creates (or reuses) a policy instance to use.
-InlinePolicy* InlinePolicy::GetPolicy(Compiler* compiler, bool isPrejitRoot)
+InlinePolicy* InlinePolicy::GetPolicy(Compiler* compiler, InlineContext* inlineContext, bool isPrejitRoot)
{
+ // inlineContext only conditionally used below.
+ (void) inlineContext;
+
#ifdef DEBUG
// Optionally install the RandomPolicy.
@@ -43,6 +47,14 @@ InlinePolicy* InlinePolicy::GetPolicy(Compiler* compiler, bool isPrejitRoot)
#if defined(DEBUG) || defined(INLINE_DATA)
+ // Optionally install the ReplayPolicy.
+ bool useReplayPolicy = JitConfig.JitInlinePolicyReplay() != 0;
+
+ if (useReplayPolicy)
+ {
+ return new (compiler, CMK_Inlining) ReplayPolicy(compiler, inlineContext, isPrejitRoot);
+ }
+
// Optionally install the SizePolicy.
bool useSizePolicy = JitConfig.JitInlinePolicySize() != 0;
@@ -1943,14 +1955,14 @@ void FullPolicy::DetermineProfitability(CORINFO_METHOD_INFO* methodInfo)
unsigned depthLimit = m_RootCompiler->m_inlineStrategy->GetMaxInlineDepth();
- if (m_Depth > depthLimit)
+ if (m_Depth > depthLimit)
{
SetFailure(InlineObservation::CALLSITE_IS_TOO_DEEP);
return;
}
// Check size
-
+
unsigned sizeLimit = m_RootCompiler->m_inlineStrategy->GetMaxInlineILSize();
if (m_CodeSize > sizeLimit)
@@ -2042,4 +2054,368 @@ void SizePolicy::DetermineProfitability(CORINFO_METHOD_INFO* methodInfo)
return;
}
+bool ReplayPolicy::s_WroteReplayBanner = false;
+FILE* ReplayPolicy::s_ReplayFile = nullptr;
+
+//------------------------------------------------------------------------/
+// ReplayPolicy: construct a new ReplayPolicy
+//
+// Arguments:
+// compiler -- compiler instance doing the inlining (root compiler)
+// inlineContext -- inline context for the inline
+// isPrejitRoot -- true if this compiler is prejitting the root method
+
+ReplayPolicy::ReplayPolicy(Compiler* compiler, InlineContext* inlineContext, bool isPrejitRoot)
+ : DiscretionaryPolicy(compiler, isPrejitRoot)
+ , m_InlineContext(inlineContext)
+{
+ // Is there a log file open already? If so, we can use it.
+ if (s_ReplayFile == nullptr)
+ {
+ // Did we already try and open and fail?
+ if (!s_WroteReplayBanner)
+ {
+ // Nope, open it up.
+ const wchar_t* replayFileName = JitConfig.JitInlineReplayFile();
+ s_ReplayFile = _wfopen(replayFileName, W("r"));
+ fprintf(stderr, "*** %s inlines from %ws",
+ s_ReplayFile == nullptr ? "Unable to replay" : "Replaying",
+ replayFileName);
+ s_WroteReplayBanner = true;
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// ReplayPolicy: Finalize reading of inline Xml
+//
+// Notes:
+// Called during jitShutdown()
+
+void ReplayPolicy::FinalizeXml()
+{
+ if (s_ReplayFile != nullptr)
+ {
+ fclose(s_ReplayFile);
+ s_ReplayFile = nullptr;
+ }
+}
+
+//------------------------------------------------------------------------
+// FindMethod: find the root method in the inline Xml
+//
+// ReturnValue:
+// true if found. File position left pointing just after the
+// <Token> entry for the method.
+
+bool ReplayPolicy::FindMethod()
+{
+ const mdMethodDef methodToken =
+ m_RootCompiler->info.compCompHnd->getMethodDefFromMethod(
+ m_RootCompiler->info.compMethodHnd);
+ const unsigned methodHash =
+ m_RootCompiler->info.compMethodHash();
+
+ if (s_ReplayFile == nullptr)
+ {
+ return false;
+ }
+
+ bool foundMethod = false;
+ char buffer[256];
+ fseek(s_ReplayFile, 0, SEEK_SET);
+
+ while (!foundMethod)
+ {
+ // Get next line
+ if (fgets(buffer, sizeof(buffer), s_ReplayFile) == nullptr)
+ {
+ break;
+ }
+
+ // Look for next method entry
+ if (strstr(buffer, "<Method>") == nullptr)
+ {
+ continue;
+ }
+
+ // Get next line
+ if (fgets(buffer, sizeof(buffer), s_ReplayFile) == nullptr)
+ {
+ break;
+ }
+
+ // See if token matches
+ unsigned token = 0;
+ int count = sscanf(buffer, " <Token>%u</Token> ", &token);
+ if ((count != 1) || (token != methodToken))
+ {
+ continue;
+ }
+
+ // Get next line
+ if (fgets(buffer, sizeof(buffer), s_ReplayFile) == nullptr)
+ {
+ break;
+ }
+
+ // See if hash matches
+ unsigned hash = 0;
+ count = sscanf(buffer, " <Hash>%u</Hash> ", &hash);
+ if ((count != 1) || (hash != methodHash))
+ {
+ continue;
+ }
+
+ // Found a match...
+ foundMethod = true;
+ break;
+ }
+
+ return foundMethod;
+}
+
+//------------------------------------------------------------------------
+// FindContext: find an inline context in the inline Xml
+//
+// Notes:
+// Assumes file position within the relevant method has just been
+// set by a successful call to FindMethod().
+//
+// Arguments:
+// context -- context of interest
+//
+// ReturnValue:
+// true if found. File position left pointing just after the
+// <Token> entry for the context.
+
+bool ReplayPolicy::FindContext(InlineContext* context)
+{
+ // Make sure we've found the parent context.
+ if (context->IsRoot())
+ {
+ // We've already found the method context so we're good.
+ return true;
+ }
+
+ bool foundParent = FindContext(context->GetParent());
+
+ if (!foundParent)
+ {
+ return false;
+ }
+
+ // File pointer should be pointing at the parent context level.
+ // See if we see an inline entry for this context.
+ //
+ // Token we're looking for.
+ mdMethodDef contextToken =
+ m_RootCompiler->info.compCompHnd->getMethodDefFromMethod(
+ context->GetCallee());
+
+ return FindInline(contextToken);
+}
+
+//------------------------------------------------------------------------
+// FindInline: find entry for the current inline in inline Xml.
+//
+// Arguments:
+// token -- token describing the inline
+//
+// ReturnValue:
+// true if the inline entry was found
+//
+// Notes:
+// Assumes file position has just been set by a successful call to
+// FindMethod or FindContext.
+//
+// Token will not be sufficiently unique to identify a particular
+// inline, if there are multiple calls to the same method.
+
+bool ReplayPolicy::FindInline(unsigned token)
+{
+ char buffer[256];
+ bool foundInline = false;
+ int depth = 0;
+
+ while (!foundInline)
+ {
+ // Get next line
+ if (fgets(buffer, sizeof(buffer), s_ReplayFile) == nullptr)
+ {
+ break;
+ }
+
+ // If we hit </Method> we've gone too far,
+ // and the XML is messed up.
+ if (strstr(buffer, "</Method>") != nullptr)
+ {
+ break;
+ }
+
+ // Look for <Inlines />....
+ if (strstr(buffer, "<Inlines />") != nullptr)
+ {
+ if (depth == 0)
+ {
+ // Exited depth 1, failed to find the context
+ break;
+ }
+ else
+ {
+ // Exited nested, keep looking
+ continue;
+ }
+ }
+
+ // Look for <Inlines>....
+ if (strstr(buffer, "<Inlines>") != nullptr)
+ {
+ depth++;
+ continue;
+ }
+
+ // If we hit </Inlines> we've exited a nested entry
+ // or the current entry.
+ if (strstr(buffer, "</Inlines>") != nullptr)
+ {
+ depth--;
+
+ if (depth == 0)
+ {
+ // Exited depth 1, failed to find the context
+ break;
+ }
+ else
+ {
+ // Exited nested, keep looking
+ continue;
+ }
+ }
+
+ // Look for start of inline section at the right depth
+ if ((depth != 1) || (strstr(buffer, "<Inline>") == nullptr))
+ {
+ continue;
+ }
+
+ // Get next line
+ if (fgets(buffer, sizeof(buffer), s_ReplayFile) == nullptr)
+ {
+ break;
+ }
+
+ unsigned inlineToken = 0;
+ int count = sscanf(buffer, " <Token>%u</Token> ", &inlineToken);
+
+ // Need a secondary check here for callsite
+ // ID... offset or similar. Hash would be nice too.
+ if ((count != 1) || (inlineToken != token))
+ {
+ continue;
+ }
+
+ // We're good!
+ foundInline = true;
+ break;
+ }
+
+ return foundInline;
+}
+
+//------------------------------------------------------------------------
+// FindInline: find entry for a particular callee in inline Xml.
+//
+// Arguments:
+// callee -- handle for the callee method
+//
+// ReturnValue:
+// true if the inline should be performed.
+//
+// Notes:
+// Assumes file position has just been set by a successful call to
+// FindContext(...);
+//
+// callee handle will not be sufficiently unique to identify a
+// particular inline, if there are multiple calls to the same
+// method.
+
+bool ReplayPolicy::FindInline(CORINFO_METHOD_HANDLE callee)
+{
+ // Token we're looking for
+ mdMethodDef calleeToken =
+ m_RootCompiler->info.compCompHnd->getMethodDefFromMethod(callee);
+
+ bool foundInline = FindInline(calleeToken);
+
+ return foundInline;
+}
+
+//------------------------------------------------------------------------
+// DetermineProfitability: determine if this inline is profitable
+//
+// Arguments:
+// methodInfo -- method info for the callee
+
+void ReplayPolicy::DetermineProfitability(CORINFO_METHOD_INFO* methodInfo)
+{
+ // TODO: handle prejit root case....need to record this in the
+ // root method XML.
+ if (m_IsPrejitRoot)
+ {
+ // Fall back to discretionary policy for now.
+ return DiscretionaryPolicy::DetermineProfitability(methodInfo);
+ }
+
+ // Otherwise try and find this candiate in the Xml. If we fail
+ // the don't inline.
+ bool accept = false;
+
+ // First, locate the entries for the root method.
+ bool foundMethod = FindMethod();
+
+ if (foundMethod && (m_InlineContext != nullptr))
+ {
+ // Next, navigate the context tree to find the entries
+ // for the context that contains this candidate.
+ bool foundContext = FindContext(m_InlineContext);
+
+ if (foundContext)
+ {
+ // Finally, find this candidate within its context
+ CORINFO_METHOD_HANDLE calleeHandle = methodInfo->ftn;
+ accept = FindInline(calleeHandle);
+ }
+ }
+
+ if (accept)
+ {
+ JITLOG_THIS(m_RootCompiler, (LL_INFO100000, "Inline accepted via log replay"))
+
+ if (m_IsPrejitRoot)
+ {
+ SetCandidate(InlineObservation::CALLEE_LOG_REPLAY_ACCEPT);
+ }
+ else
+ {
+ SetCandidate(InlineObservation::CALLSITE_LOG_REPLAY_ACCEPT);
+ }
+ }
+ else
+ {
+ JITLOG_THIS(m_RootCompiler, (LL_INFO100000, "Inline rejected via log replay"))
+
+ if (m_IsPrejitRoot)
+ {
+ SetNever(InlineObservation::CALLEE_LOG_REPLAY_REJECT);
+ }
+ else
+ {
+ SetFailure(InlineObservation::CALLSITE_LOG_REPLAY_REJECT);
+ }
+ }
+
+ return;
+}
+
#endif // defined(DEBUG) || defined(INLINE_DATA)
diff --git a/src/jit/inlinepolicy.h b/src/jit/inlinepolicy.h
index 31a686e52f..29e6af40c0 100644
--- a/src/jit/inlinepolicy.h
+++ b/src/jit/inlinepolicy.h
@@ -338,6 +338,35 @@ public:
const char* GetName() const override { return "SizePolicy"; }
};
+// The ReplayPolicy performs only inlines specified by an external
+// inline replay log.
+
+class ReplayPolicy : public DiscretionaryPolicy
+{
+public:
+
+ // Construct a ReplayPolicy
+ ReplayPolicy(Compiler* compiler, InlineContext* inlineContext, bool isPrejitRoot);
+
+ // Policy determinations
+ void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) override;
+
+ // Miscellaneous
+ const char* GetName() const override { return "ReplayPolicy"; }
+
+ static void FinalizeXml();
+
+private:
+
+ bool FindMethod();
+ bool FindContext(InlineContext* context);
+ bool FindInline(CORINFO_METHOD_HANDLE callee);
+ bool FindInline(unsigned token);
+
+ static bool s_WroteReplayBanner;
+ static FILE* s_ReplayFile;
+ InlineContext* m_InlineContext;
+};
#endif // defined(DEBUG) || defined(INLINE_DATA)
diff --git a/src/jit/jitconfigvalues.h b/src/jit/jitconfigvalues.h
index cb4724dee0..2fe24dfc2f 100644
--- a/src/jit/jitconfigvalues.h
+++ b/src/jit/jitconfigvalues.h
@@ -198,7 +198,9 @@ CONFIG_INTEGER(JitInlinePolicyDiscretionary, W("JitInlinePolicyDiscretionary"),
CONFIG_INTEGER(JitInlinePolicyModel, W("JitInlinePolicyModel"), 0)
CONFIG_INTEGER(JitInlinePolicyFull, W("JitInlinePolicyFull"), 0)
CONFIG_INTEGER(JitInlinePolicySize, W("JitInlinePolicySize"), 0)
+CONFIG_INTEGER(JitInlinePolicyReplay, W("JitInlinePolicyReplay"), 0)
CONFIG_STRING(JitNoInlineRange, W("JitNoInlineRange"))
+CONFIG_STRING(JitInlineReplayFile, W("JitInlineReplayFile"))
#endif // defined(DEBUG) || defined(INLINE_DATA)
#undef CONFIG_INTEGER