summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gc/gcinterface.h2
-rw-r--r--src/jit/compiler.cpp1
-rw-r--r--src/jit/compiler.h3
-rw-r--r--src/jit/flowgraph.cpp34
-rw-r--r--src/jit/importer.cpp89
-rw-r--r--src/jit/inline.def7
-rw-r--r--src/jit/inlinepolicy.cpp16
-rw-r--r--src/jit/inlinepolicy.h6
-rw-r--r--src/jit/jitconfigvalues.h1
-rw-r--r--src/jit/morph.cpp2
10 files changed, 131 insertions, 30 deletions
diff --git a/src/gc/gcinterface.h b/src/gc/gcinterface.h
index 4a35d30f88..b1d6b8090c 100644
--- a/src/gc/gcinterface.h
+++ b/src/gc/gcinterface.h
@@ -841,13 +841,11 @@ struct ScanContext
void* _unused1;
#endif //CHECK_APP_DOMAIN_LEAKS || FEATURE_APPDOMAIN_RESOURCE_MONITORING || DACCESS_COMPILE
-#ifndef FEATURE_REDHAWK
#if defined(GC_PROFILING) || defined (DACCESS_COMPILE)
MethodDesc *pMD;
#else
void* _unused2;
#endif //GC_PROFILING || DACCESS_COMPILE
-#endif // FEATURE_REDHAWK
#if defined(GC_PROFILING) || defined(FEATURE_EVENT_TRACE)
EtwGCRootKind dwEtwRootKind;
#else
diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp
index 515912c673..efad4cfd0f 100644
--- a/src/jit/compiler.cpp
+++ b/src/jit/compiler.cpp
@@ -1997,6 +1997,7 @@ void Compiler::compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo)
compLongUsed = false;
compTailCallUsed = false;
compLocallocUsed = false;
+ compLocallocOptimized = false;
compQmarkRationalized = false;
compQmarkUsed = false;
compFloatingPointUsed = false;
diff --git a/src/jit/compiler.h b/src/jit/compiler.h
index 87d3f3965f..ffaa8cec9d 100644
--- a/src/jit/compiler.h
+++ b/src/jit/compiler.h
@@ -7917,6 +7917,7 @@ public:
bool compFloatingPointUsed; // Does the method use TYP_FLOAT or TYP_DOUBLE
bool compTailCallUsed; // Does the method do a tailcall
bool compLocallocUsed; // Does the method use localloc.
+ bool compLocallocOptimized; // Does the method have an optimized localloc
bool compQmarkUsed; // Does the method use GT_QMARK/GT_COLON
bool compQmarkRationalized; // Is it allowed to use a GT_QMARK/GT_COLON node.
bool compUnsafeCastUsed; // Does the method use LDIND/STIND to cast between scalar/refernce types
@@ -9295,6 +9296,8 @@ public:
#define DEFAULT_MAX_INLINE_DEPTH 20 // Methods at more than this level deep will not be inlined
+#define DEFAULT_MAX_LOCALLOC_TO_LOCAL_SIZE 32 // fixed locallocs of this size or smaller will convert to local buffers
+
private:
#ifdef FEATURE_JIT_METHOD_PERF
JitTimer* pCompJitTimer; // Timer data structure (by phases) for current compilation.
diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp
index b7d50d105f..946f77d7ae 100644
--- a/src/jit/flowgraph.cpp
+++ b/src/jit/flowgraph.cpp
@@ -4333,6 +4333,18 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, BYTE*
compInlineResult->NoteBool(InlineObservation::CALLEE_IS_FORCE_INLINE, isForceInline);
compInlineResult->NoteInt(InlineObservation::CALLEE_IL_CODE_SIZE, codeSize);
+ // Determine if call site is within a try.
+ if (isInlining && impInlineInfo->iciBlock->hasTryIndex())
+ {
+ compInlineResult->Note(InlineObservation::CALLSITE_IN_TRY_REGION);
+ }
+
+ // Determine if the call site is in a loop.
+ if (isInlining && ((impInlineInfo->iciBlock->bbFlags & BBF_BACKWARD_JUMP) != 0))
+ {
+ compInlineResult->Note(InlineObservation::CALLSITE_IN_LOOP);
+ }
+
#ifdef DEBUG
// If inlining, this method should still be a candidate.
@@ -4807,8 +4819,6 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, BYTE*
// the list of other opcodes (for all platforms).
__fallthrough;
-
- case CEE_LOCALLOC:
case CEE_MKREFANY:
case CEE_RETHROW:
if (makeInlineObservations)
@@ -4826,6 +4836,19 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, BYTE*
}
break;
+ case CEE_LOCALLOC:
+
+ // We now allow localloc callees to become candidates in some cases.
+ if (makeInlineObservations)
+ {
+ compInlineResult->Note(InlineObservation::CALLEE_HAS_LOCALLOC);
+ if (isInlining && compInlineResult->IsFailure())
+ {
+ return;
+ }
+ }
+ break;
+
case CEE_LDARG_0:
case CEE_LDARG_1:
case CEE_LDARG_2:
@@ -4915,12 +4938,6 @@ void Compiler::fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, BYTE*
}
}
- // Determine if call site is within a try.
- if (isInlining && impInlineInfo->iciBlock->hasTryIndex())
- {
- compInlineResult->Note(InlineObservation::CALLSITE_IN_TRY_REGION);
- }
-
// If the inline is viable and discretionary, do the
// profitability screening.
if (compInlineResult->IsDiscretionaryCandidate())
@@ -22959,6 +22976,7 @@ _Done:
compLongUsed |= InlineeCompiler->compLongUsed;
compFloatingPointUsed |= InlineeCompiler->compFloatingPointUsed;
compLocallocUsed |= InlineeCompiler->compLocallocUsed;
+ compLocallocOptimized |= InlineeCompiler->compLocallocOptimized;
compQmarkUsed |= InlineeCompiler->compQmarkUsed;
compUnsafeCastUsed |= InlineeCompiler->compUnsafeCastUsed;
compNeedsGSSecurityCookie |= InlineeCompiler->compNeedsGSSecurityCookie;
diff --git a/src/jit/importer.cpp b/src/jit/importer.cpp
index bb02ca809f..b5df87b0ec 100644
--- a/src/jit/importer.cpp
+++ b/src/jit/importer.cpp
@@ -14279,8 +14279,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
break;
case CEE_LOCALLOC:
- assert(!compIsForInlining());
-
if (tiVerificationNeeded)
{
Verify(false, "bad opcode");
@@ -14292,11 +14290,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
BADCODE("Localloc can't be inside handler");
}
- /* The FP register may not be back to the original value at the end
- of the method, even if the frame size is 0, as localloc may
- have modified it. So we will HAVE to reset it */
-
- compLocallocUsed = true;
setNeedsGSSecurityCookie();
// Get the size to allocate
@@ -14309,11 +14302,81 @@ void Compiler::impImportBlockCode(BasicBlock* block)
BADCODE("Localloc can only be used when the stack is empty");
}
- op1 = gtNewOperNode(GT_LCLHEAP, TYP_I_IMPL, op2);
+ // If the localloc is not in a loop and its size is a small constant,
+ // create a new local var of TYP_BLK and return its address.
+ {
+ bool convertedToLocal = false;
+
+ // Need to aggressively fold here, as even fixed-size locallocs
+ // will have casts in the way.
+ op2 = gtFoldExpr(op2);
+
+ if (op2->IsIntegralConst())
+ {
+ const ssize_t allocSize = op2->AsIntCon()->IconValue();
+
+ if (allocSize == 0)
+ {
+ // Result is nullptr
+ JITDUMP("Converting stackalloc of 0 bytes to push null unmanaged pointer\n");
+ op1 = gtNewIconNode(0, TYP_I_IMPL);
+ convertedToLocal = true;
+ }
+ else if ((allocSize > 0) && ((compCurBB->bbFlags & BBF_BACKWARD_JUMP) == 0))
+ {
+ // Get the size threshold for local conversion
+ ssize_t maxSize = DEFAULT_MAX_LOCALLOC_TO_LOCAL_SIZE;
+
+#ifdef DEBUG
+ // Optionally allow this to be modified
+ maxSize = JitConfig.JitStackAllocToLocalSize();
+#endif // DEBUG
+
+ if (allocSize <= maxSize)
+ {
+ const unsigned stackallocAsLocal = lvaGrabTemp(false DEBUGARG("stackallocLocal"));
+ JITDUMP("Converting stackalloc of %lld bytes to new local V%02u\n", allocSize,
+ stackallocAsLocal);
+ lvaTable[stackallocAsLocal].lvType = TYP_BLK;
+ lvaTable[stackallocAsLocal].lvExactSize = (unsigned)allocSize;
+ lvaTable[stackallocAsLocal].lvIsUnsafeBuffer = true;
+ op1 = gtNewLclvNode(stackallocAsLocal, TYP_BLK);
+ op1 = gtNewOperNode(GT_ADDR, TYP_I_IMPL, op1);
+ convertedToLocal = true;
+ compGSReorderStackLayout = true;
+ }
+ }
+ }
+
+ if (!convertedToLocal)
+ {
+ // Bail out if inlining and the localloc was not converted.
+ //
+ // Note we might consider allowing the inline, if the call
+ // site is not in a loop.
+ if (compIsForInlining())
+ {
+ InlineObservation obs = op2->IsIntegralConst()
+ ? InlineObservation::CALLEE_LOCALLOC_TOO_LARGE
+ : InlineObservation::CALLSITE_LOCALLOC_SIZE_UNKNOWN;
+ compInlineResult->NoteFatal(obs);
+ return;
+ }
- // May throw a stack overflow exception. Obviously, we don't want locallocs to be CSE'd.
+ op1 = gtNewOperNode(GT_LCLHEAP, TYP_I_IMPL, op2);
+ // May throw a stack overflow exception. Obviously, we don't want locallocs to be CSE'd.
+ op1->gtFlags |= (GTF_EXCEPT | GTF_DONT_CSE);
- op1->gtFlags |= (GTF_EXCEPT | GTF_DONT_CSE);
+ /* The FP register may not be back to the original value at the end
+ of the method, even if the frame size is 0, as localloc may
+ have modified it. So we will HAVE to reset it */
+ compLocallocUsed = true;
+ }
+ else
+ {
+ compLocallocOptimized = true;
+ }
+ }
impPushOnStack(op1, tiRetVal);
break;
@@ -14811,7 +14874,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
impResolveToken(codeAddr + (sz + 1), &unboxResolvedToken, CORINFO_TOKENKIND_Class);
- // See if token types a equal.
+ // See if the resolved tokens describe types that are equal.
const TypeCompareState compare =
info.compCompHnd->compareTypesForEquality(unboxResolvedToken.hClass, resolvedToken.hClass);
@@ -14823,10 +14886,6 @@ void Compiler::impImportBlockCode(BasicBlock* block)
sz += sizeof(mdToken) + 1;
break;
}
- else
- {
- assert(unboxResolvedToken.hClass != resolvedToken.hClass);
- }
}
impImportAndPushBox(&resolvedToken);
diff --git a/src/jit/inline.def b/src/jit/inline.def
index ff0b21100e..0a13950b48 100644
--- a/src/jit/inline.def
+++ b/src/jit/inline.def
@@ -48,6 +48,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(LOCALLOC_TOO_LARGE, bool, "localloc size too large", 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)
@@ -78,6 +79,7 @@ INLINE_OBSERVATION(CLASS_PROMOTABLE, bool, "promotable value class",
INLINE_OBSERVATION(DOES_NOT_RETURN, bool, "does not return", INFORMATION, CALLEE)
INLINE_OBSERVATION(END_OPCODE_SCAN, bool, "done looking at opcodes", INFORMATION, CALLEE)
INLINE_OBSERVATION(HAS_GC_STRUCT, bool, "has gc field in struct local", INFORMATION, CALLEE)
+INLINE_OBSERVATION(HAS_LOCALLOC, bool, "has localloc", INFORMATION, CALLEE)
INLINE_OBSERVATION(HAS_PINNED_LOCALS, bool, "has pinned locals", INFORMATION, CALLEE)
INLINE_OBSERVATION(HAS_SIMD, bool, "has SIMD arg, local, or ret", INFORMATION, CALLEE)
INLINE_OBSERVATION(HAS_SWITCH, bool, "has switch", INFORMATION, CALLEE)
@@ -143,6 +145,8 @@ INLINE_OBSERVATION(IS_WITHIN_FILTER, bool, "within filter region",
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(LOCALLOC_IN_LOOP, bool, "within loop, has localloc", FATAL, CALLSITE)
+INLINE_OBSERVATION(LOCALLOC_SIZE_UNKNOWN, bool, "localloc size unknown", 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)
@@ -164,7 +168,8 @@ INLINE_OBSERVATION(RARE_GC_STRUCT, bool, "rarely called, has gc str
INLINE_OBSERVATION(CONSTANT_ARG_FEEDS_TEST, bool, "constant argument feeds test", INFORMATION, CALLSITE)
INLINE_OBSERVATION(DEPTH, int, "depth", INFORMATION, CALLSITE)
INLINE_OBSERVATION(FREQUENCY, int, "rough call site frequency", INFORMATION, CALLSITE)
-INLINE_OBSERVATION(IN_TRY_REGION, bool, "call site in try region", INFORMATION, CALLSITE)
+INLINE_OBSERVATION(IN_LOOP, bool, "call site is in a loop", INFORMATION, CALLSITE)
+INLINE_OBSERVATION(IN_TRY_REGION, bool, "call site is in a try region", INFORMATION, CALLSITE)
INLINE_OBSERVATION(IS_PROFITABLE_INLINE, bool, "profitable inline", INFORMATION, CALLSITE)
INLINE_OBSERVATION(IS_SAME_THIS, bool, "same this as root caller", INFORMATION, CALLSITE)
INLINE_OBSERVATION(IS_SIZE_DECREASING_INLINE, bool, "size decreasing inline", INFORMATION, CALLSITE)
diff --git a/src/jit/inlinepolicy.cpp b/src/jit/inlinepolicy.cpp
index 61e70c3ed4..a4f51c9ee9 100644
--- a/src/jit/inlinepolicy.cpp
+++ b/src/jit/inlinepolicy.cpp
@@ -384,11 +384,20 @@ void LegacyPolicy::NoteBool(InlineObservation obs, bool value)
}
case InlineObservation::CALLEE_HAS_PINNED_LOCALS:
+ case InlineObservation::CALLEE_HAS_LOCALLOC:
// The legacy policy is to never inline methods with
- // pinned locals.
+ // pinned locals or localloc.
SetNever(obs);
break;
+ case InlineObservation::CALLSITE_IN_TRY_REGION:
+ m_CallsiteIsInTryRegion = true;
+ break;
+
+ case InlineObservation::CALLSITE_IN_LOOP:
+ m_CallsiteIsInLoop = true;
+ break;
+
default:
// Ignore the remainder for now
break;
@@ -900,6 +909,11 @@ void EnhancedLegacyPolicy::NoteBool(InlineObservation obs, bool value)
}
break;
+ case InlineObservation::CALLEE_HAS_LOCALLOC:
+ // We see this during the IL prescan. Ignore for now, we will
+ // bail out, if necessary, during importation
+ break;
+
default:
// Pass all other information to the legacy policy
LegacyPolicy::NoteBool(obs, value);
diff --git a/src/jit/inlinepolicy.h b/src/jit/inlinepolicy.h
index 3239dcbe89..2af99b580b 100644
--- a/src/jit/inlinepolicy.h
+++ b/src/jit/inlinepolicy.h
@@ -99,6 +99,7 @@ public:
, m_LooksLikeWrapperMethod(false)
, m_MethodIsMostlyLoadStore(false)
, m_CallsiteIsInTryRegion(false)
+ , m_CallsiteIsInLoop(false)
{
// empty
}
@@ -167,10 +168,11 @@ protected:
bool m_LooksLikeWrapperMethod : 1;
bool m_MethodIsMostlyLoadStore : 1;
bool m_CallsiteIsInTryRegion : 1;
+ bool m_CallsiteIsInLoop : 1;
};
-// EnhancedLegacyPolicy extends the legacy policy by rejecting
-// inlining of methods that never return because they throw.
+// EnhancedLegacyPolicy extends the legacy policy by
+// relaxing various restrictions.
class EnhancedLegacyPolicy : public LegacyPolicy
{
diff --git a/src/jit/jitconfigvalues.h b/src/jit/jitconfigvalues.h
index b978e4a7df..65d670f0cf 100644
--- a/src/jit/jitconfigvalues.h
+++ b/src/jit/jitconfigvalues.h
@@ -101,6 +101,7 @@ CONFIG_INTEGER(JitPrintInlinedMethods, W("JitPrintInlinedMethods"), 0)
CONFIG_INTEGER(JitPrintDevirtualizedMethods, W("JitPrintDevirtualizedMethods"), 0)
CONFIG_INTEGER(JitRequired, W("JITRequired"), -1)
CONFIG_INTEGER(JitRoundFloat, W("JITRoundFloat"), DEFAULT_ROUND_LEVEL)
+CONFIG_INTEGER(JitStackAllocToLocalSize, W("JitStackAllocToLocalSize"), DEFAULT_MAX_LOCALLOC_TO_LOCAL_SIZE)
CONFIG_INTEGER(JitSkipArrayBoundCheck, W("JitSkipArrayBoundCheck"), 0)
CONFIG_INTEGER(JitSlowDebugChecksEnabled, W("JitSlowDebugChecksEnabled"), 1) // Turn on slow debug checks
CONFIG_INTEGER(JitSplitFunctionSize, W("JitSplitFunctionSize"), 0) // On ARM, use this as the maximum function/funclet
diff --git a/src/jit/morph.cpp b/src/jit/morph.cpp
index 8519f42fdd..9d6d0d3ce5 100644
--- a/src/jit/morph.cpp
+++ b/src/jit/morph.cpp
@@ -8272,7 +8272,7 @@ GenTreePtr Compiler::fgMorphCall(GenTreeCall* call)
{
szFailReason = "Needs security check";
}
- else if (compLocallocUsed)
+ else if (compLocallocUsed || compLocallocOptimized)
{
szFailReason = "Localloc used";
}