diff options
-rw-r--r-- | src/jit/compiler.cpp | 2 | ||||
-rw-r--r-- | src/jit/flowgraph.cpp | 4 | ||||
-rw-r--r-- | src/jit/importer.cpp | 2 | ||||
-rw-r--r-- | src/jit/inline.cpp | 49 | ||||
-rw-r--r-- | src/jit/inline.def | 4 | ||||
-rw-r--r-- | src/jit/inline.h | 30 | ||||
-rw-r--r-- | src/jit/inlinepolicy.cpp | 388 | ||||
-rw-r--r-- | src/jit/inlinepolicy.h | 29 | ||||
-rw-r--r-- | src/jit/jitconfigvalues.h | 2 |
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 |