diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gc/gcinterface.h | 2 | ||||
-rw-r--r-- | src/jit/compiler.cpp | 1 | ||||
-rw-r--r-- | src/jit/compiler.h | 3 | ||||
-rw-r--r-- | src/jit/flowgraph.cpp | 34 | ||||
-rw-r--r-- | src/jit/importer.cpp | 89 | ||||
-rw-r--r-- | src/jit/inline.def | 7 | ||||
-rw-r--r-- | src/jit/inlinepolicy.cpp | 16 | ||||
-rw-r--r-- | src/jit/inlinepolicy.h | 6 | ||||
-rw-r--r-- | src/jit/jitconfigvalues.h | 1 | ||||
-rw-r--r-- | src/jit/morph.cpp | 2 |
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"; } |