summaryrefslogtreecommitdiff
path: root/src/jit
diff options
context:
space:
mode:
authorAndy Ayers <andya@microsoft.com>2019-01-08 11:27:32 -0800
committerAndy Ayers <andya@microsoft.com>2019-01-08 14:16:43 -0800
commit3f182cfcf80af6500217673d920c52ccf6919661 (patch)
tree0619314f777c79213d4c49f45fd4c0179cb6ab93 /src/jit
parent7d54c590315ad682ed74f9491654720d83532ae7 (diff)
downloadcoreclr-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.cpp5
-rw-r--r--src/jit/compiler.h1
-rw-r--r--src/jit/flowgraph.cpp36
-rw-r--r--src/jit/inline.cpp36
-rw-r--r--src/jit/inline.h19
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