diff options
author | Andy Ayers <andya@microsoft.com> | 2019-01-08 11:27:32 -0800 |
---|---|---|
committer | Andy Ayers <andya@microsoft.com> | 2019-01-08 14:16:43 -0800 |
commit | 3f182cfcf80af6500217673d920c52ccf6919661 (patch) | |
tree | 0619314f777c79213d4c49f45fd4c0179cb6ab93 /src/jit | |
parent | 7d54c590315ad682ed74f9491654720d83532ae7 (diff) | |
download | coreclr-3f182cfcf80af6500217673d920c52ccf6919661.tar.gz coreclr-3f182cfcf80af6500217673d920c52ccf6919661.tar.bz2 coreclr-3f182cfcf80af6500217673d920c52ccf6919661.zip |
JIT: modify inline budget update to use estimated imported IL size
The inliner keeps a time budget to try and avoid pathological runaway inline
behavior (see #4375). The jit estimates the time impact of an inline using a
simple projection based on IL size. If an prospective inline would put the jit
over the time budget, the inline is blocked -- and note even aggressive inlines
can be blocked this way.
We now have a fair number of aggressive inline methods like
`Vector256<T>.IsSupported` where the IL is optimized early on by the jit and the
actual impact on the calling method is much less than the initial IL size would
suggest. For instance `IsSupported` is 286 bytes of IL, but the net contribution
of this method at jit time is either a constant 0 or 1, and so the effective size
is more like 2 bytes of IL.
This set of changes updates the jit to estimate the imported IL size of a method
when updating the budget.
Closes #21794.
Diffstat (limited to 'src/jit')
-rw-r--r-- | src/jit/compiler.cpp | 5 | ||||
-rw-r--r-- | src/jit/compiler.h | 1 | ||||
-rw-r--r-- | src/jit/flowgraph.cpp | 36 | ||||
-rw-r--r-- | src/jit/inline.cpp | 36 | ||||
-rw-r--r-- | src/jit/inline.h | 19 |
5 files changed, 79 insertions, 18 deletions
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp index 40c31c6870..d0cb9ac6b3 100644 --- a/src/jit/compiler.cpp +++ b/src/jit/compiler.cpp @@ -5800,8 +5800,9 @@ int Compiler::compCompileHelper(CORINFO_MODULE_HANDLE classPtr, { CORINFO_METHOD_HANDLE methodHnd = info.compMethodHnd; - info.compCode = methodInfo->ILCode; - info.compILCodeSize = methodInfo->ILCodeSize; + info.compCode = methodInfo->ILCode; + info.compILCodeSize = methodInfo->ILCodeSize; + info.compILImportSize = 0; if (info.compILCodeSize == 0) { diff --git a/src/jit/compiler.h b/src/jit/compiler.h index 601c27b9e3..8e50c1606c 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -8695,6 +8695,7 @@ public: const BYTE* compCode; IL_OFFSET compILCodeSize; // The IL code size + IL_OFFSET compILImportSize; // Estimated amount of IL actually imported UNATIVE_OFFSET compNativeCodeSize; // The native code size, after instructions are issued. This // is less than (compTotalHotCodeSize + compTotalColdCodeSize) only if: // (1) the code is not hot/cold split, and we issued less code than we expected, or diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp index 4e2fc40bba..2df3cce3ba 100644 --- a/src/jit/flowgraph.cpp +++ b/src/jit/flowgraph.cpp @@ -6822,6 +6822,42 @@ void Compiler::fgImport() verFlag = tiIsVerifiableCode ? CORINFO_FLG_VERIFIABLE : CORINFO_FLG_UNVERIFIABLE; info.compCompHnd->setMethodAttribs(info.compMethodHnd, verFlag); } + + // Estimate how much of method IL was actually imported. + // + // Note this includes (to some extent) the impact of importer folded + // branches, provided the folded tree covered the entire block's IL. + unsigned importedILSize = 0; + for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->bbNext) + { + if ((block->bbFlags & BBF_IMPORTED) != 0) + { + // Assume if we generate any IR for the block we generate IR for the entire block. + if (!block->isEmpty()) + { + unsigned blockILSize = blockILSize = block->bbCodeOffsEnd - block->bbCodeOffs; + importedILSize += blockILSize; + } + } + } + + // Could be tripped up if we ever duplicate blocks + assert(importedILSize <= info.compILCodeSize); + + // Leave a note if we only did a partial import. + if (importedILSize != info.compILCodeSize) + { + JITDUMP("\n** Note: %s IL was partially imported -- imported %u of %u bytes of method IL\n", + compIsForInlining() ? "inlinee" : "root method", importedILSize, info.compILCodeSize); + } + + // Record this for diagnostics and for the inliner's budget computations + info.compILImportSize = importedILSize; + + if (compIsForInlining()) + { + compInlineResult->SetImportedILSize(info.compILImportSize); + } } /***************************************************************************** diff --git a/src/jit/inline.cpp b/src/jit/inline.cpp index 6bc8482065..8435f9a6eb 100644 --- a/src/jit/inline.cpp +++ b/src/jit/inline.cpp @@ -332,6 +332,7 @@ InlineContext::InlineContext(InlineStrategy* strategy) , m_Sibling(nullptr) , m_Code(nullptr) , m_ILSize(0) + , m_ImportedILSize(0) , m_Offset(BAD_IL_OFFSET) , m_Observation(InlineObservation::CALLEE_UNUSED_INITIAL) , m_CodeSizeEstimate(0) @@ -574,6 +575,7 @@ InlineResult::InlineResult(Compiler* compiler, GenTreeCall* call, GenTreeStmt* s , m_InlineContext(nullptr) , m_Caller(nullptr) , m_Callee(nullptr) + , m_ImportedILSize(0) , m_Description(description) , m_Reported(false) { @@ -958,16 +960,16 @@ int InlineStrategy::EstimateTime(InlineContext* context) { // Simple linear models based on observations // show time is fairly well predicted by IL size. - unsigned ilSize = context->GetILSize(); - + // // Prediction varies for root and inlines. if (context == m_RootContext) { - return EstimateRootTime(ilSize); + return EstimateRootTime(context->GetILSize()); } else { - return EstimateInlineTime(ilSize); + // Use amount of IL actually imported + return EstimateInlineTime(context->GetImportedILSize()); } } @@ -1137,14 +1139,17 @@ void InlineStrategy::NoteOutcome(InlineContext* context) } //------------------------------------------------------------------------ -// BudgetCheck: return true if as inline of this size would exceed the -// jit time budget for this method +// BudgetCheck: return true if an inline of this size would likely +// exceed the jit time budget for this method // // Arguments: // ilSize - size of the method's IL // // Return Value: // true if the inline would go over budget +// +// Notes: +// Presumes all IL in the method will be imported. bool InlineStrategy::BudgetCheck(unsigned ilSize) { @@ -1205,15 +1210,16 @@ InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo) calleeContext->m_Parent = parentContext; // Push on front here will put siblings in reverse lexical // order which we undo in the dumper - calleeContext->m_Sibling = parentContext->m_Child; - parentContext->m_Child = calleeContext; - calleeContext->m_Child = nullptr; - calleeContext->m_Offset = stmt->gtStmtILoffsx; - calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation(); - calleeContext->m_Success = true; - calleeContext->m_Devirtualized = originalCall->IsDevirtualized(); - calleeContext->m_Guarded = originalCall->IsGuarded(); - calleeContext->m_Unboxed = originalCall->IsUnboxed(); + calleeContext->m_Sibling = parentContext->m_Child; + parentContext->m_Child = calleeContext; + calleeContext->m_Child = nullptr; + calleeContext->m_Offset = stmt->gtStmtILoffsx; + calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation(); + calleeContext->m_Success = true; + calleeContext->m_Devirtualized = originalCall->IsDevirtualized(); + calleeContext->m_Guarded = originalCall->IsGuarded(); + calleeContext->m_Unboxed = originalCall->IsUnboxed(); + calleeContext->m_ImportedILSize = inlineInfo->inlineResult->GetImportedILSize(); #if defined(DEBUG) || defined(INLINE_DATA) diff --git a/src/jit/inline.h b/src/jit/inline.h index e94765a74c..17e3b995c2 100644 --- a/src/jit/inline.h +++ b/src/jit/inline.h @@ -470,12 +470,22 @@ public: m_Reported = true; } - // Get the InlineContext for this inline + // Get the InlineContext for this inline. InlineContext* GetInlineContext() const { return m_InlineContext; } + unsigned GetImportedILSize() const + { + return m_ImportedILSize; + } + + void SetImportedILSize(unsigned x) + { + m_ImportedILSize = x; + } + private: // No copying or assignment allowed. InlineResult(const InlineResult&) = delete; @@ -490,6 +500,7 @@ private: InlineContext* m_InlineContext; CORINFO_METHOD_HANDLE m_Caller; // immediate caller's handle CORINFO_METHOD_HANDLE m_Callee; + unsigned m_ImportedILSize; // estimated size of imported IL const char* m_Description; bool m_Reported; }; @@ -707,6 +718,11 @@ public: return m_Unboxed; } + unsigned GetImportedILSize() const + { + return m_ImportedILSize; + } + private: InlineContext(InlineStrategy* strategy); @@ -717,6 +733,7 @@ private: InlineContext* m_Sibling; // next child of the parent BYTE* m_Code; // address of IL buffer for the method unsigned m_ILSize; // size of IL buffer for the method + unsigned m_ImportedILSize; // estimated size of imported IL IL_OFFSETX m_Offset; // call site location within parent InlineObservation m_Observation; // what lead to this inline int m_CodeSizeEstimate; // in bytes * 10 |