1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
9 #include "inlinepolicy.h"
11 // Lookup table for inline description strings
13 static const char* InlineDescriptions[] = {
14 #define INLINE_OBSERVATION(name, type, description, impact, target) description,
16 #undef INLINE_OBSERVATION
19 // Lookup table for inline targets
21 static const InlineTarget InlineTargets[] = {
22 #define INLINE_OBSERVATION(name, type, description, impact, target) InlineTarget::target,
24 #undef INLINE_OBSERVATION
27 // Lookup table for inline impacts
29 static const InlineImpact InlineImpacts[] = {
30 #define INLINE_OBSERVATION(name, type, description, impact, target) InlineImpact::impact,
32 #undef INLINE_OBSERVATION
37 //------------------------------------------------------------------------
38 // InlIsValidObservation: run a validity check on an inline observation
41 // obs - the observation in question
44 // true if the observation is valid
46 bool InlIsValidObservation(InlineObservation obs)
48 return ((obs > InlineObservation::CALLEE_UNUSED_INITIAL) && (obs < InlineObservation::CALLEE_UNUSED_FINAL));
53 //------------------------------------------------------------------------
54 // InlGetObservationString: get a string describing this inline observation
57 // obs - the observation in question
60 // string describing the observation
62 const char* InlGetObservationString(InlineObservation obs)
64 assert(InlIsValidObservation(obs));
65 return InlineDescriptions[static_cast<int>(obs)];
68 //------------------------------------------------------------------------
69 // InlGetTarget: get the target of an inline observation
72 // obs - the observation in question
75 // enum describing the target
77 InlineTarget InlGetTarget(InlineObservation obs)
79 assert(InlIsValidObservation(obs));
80 return InlineTargets[static_cast<int>(obs)];
83 //------------------------------------------------------------------------
84 // InlGetTargetString: get a string describing the target of an inline observation
87 // obs - the observation in question
90 // string describing the target
92 const char* InlGetTargetString(InlineObservation obs)
94 InlineTarget t = InlGetTarget(obs);
97 case InlineTarget::CALLER:
99 case InlineTarget::CALLEE:
101 case InlineTarget::CALLSITE:
104 return "unexpected target";
108 //------------------------------------------------------------------------
109 // InlGetImpact: get the impact of an inline observation
112 // obs - the observation in question
115 // enum value describing the impact
117 InlineImpact InlGetImpact(InlineObservation obs)
119 assert(InlIsValidObservation(obs));
120 return InlineImpacts[static_cast<int>(obs)];
123 //------------------------------------------------------------------------
124 // InlGetImpactString: get a string describing the impact of an inline observation
127 // obs - the observation in question
130 // string describing the impact
132 const char* InlGetImpactString(InlineObservation obs)
134 InlineImpact i = InlGetImpact(obs);
137 case InlineImpact::FATAL:
138 return "correctness -- fatal";
139 case InlineImpact::FUNDAMENTAL:
140 return "correctness -- fundamental limitation";
141 case InlineImpact::LIMITATION:
142 return "correctness -- jit limitation";
143 case InlineImpact::PERFORMANCE:
144 return "performance";
145 case InlineImpact::INFORMATION:
146 return "information";
148 return "unexpected impact";
152 //------------------------------------------------------------------------
153 // InlGetCorInfoInlineDecision: translate decision into a CorInfoInline
156 // d - the decision in question
159 // CorInfoInline value representing the decision
161 CorInfoInline InlGetCorInfoInlineDecision(InlineDecision d)
165 case InlineDecision::SUCCESS:
167 case InlineDecision::FAILURE:
169 case InlineDecision::NEVER:
172 assert(!"Unexpected InlineDecision");
177 //------------------------------------------------------------------------
178 // InlGetDecisionString: get a string representing this decision
181 // d - the decision in question
184 // string representing the decision
186 const char* InlGetDecisionString(InlineDecision d)
190 case InlineDecision::SUCCESS:
192 case InlineDecision::FAILURE:
193 return "failed this call site";
194 case InlineDecision::NEVER:
195 return "failed this callee";
196 case InlineDecision::CANDIDATE:
198 case InlineDecision::UNDECIDED:
201 assert(!"Unexpected InlineDecision");
206 //------------------------------------------------------------------------
207 // InlDecisionIsFailure: check if this decision describes a failing inline
210 // d - the decision in question
213 // true if the inline is definitely a failure
215 bool InlDecisionIsFailure(InlineDecision d)
219 case InlineDecision::SUCCESS:
220 case InlineDecision::UNDECIDED:
221 case InlineDecision::CANDIDATE:
223 case InlineDecision::FAILURE:
224 case InlineDecision::NEVER:
227 assert(!"Unexpected InlineDecision");
232 //------------------------------------------------------------------------
233 // InlDecisionIsSuccess: check if this decision describes a sucessful inline
236 // d - the decision in question
239 // true if the inline is definitely a success
241 bool InlDecisionIsSuccess(InlineDecision d)
245 case InlineDecision::SUCCESS:
247 case InlineDecision::FAILURE:
248 case InlineDecision::NEVER:
249 case InlineDecision::UNDECIDED:
250 case InlineDecision::CANDIDATE:
253 assert(!"Unexpected InlineDecision");
258 //------------------------------------------------------------------------
259 // InlDecisionIsNever: check if this decision describes a never inline
262 // d - the decision in question
265 // true if the inline is a never inline case
267 bool InlDecisionIsNever(InlineDecision d)
271 case InlineDecision::NEVER:
273 case InlineDecision::FAILURE:
274 case InlineDecision::SUCCESS:
275 case InlineDecision::UNDECIDED:
276 case InlineDecision::CANDIDATE:
279 assert(!"Unexpected InlineDecision");
284 //------------------------------------------------------------------------
285 // InlDecisionIsCandidate: check if this decision describes a viable candidate
288 // d - the decision in question
291 // true if this inline still might happen
293 bool InlDecisionIsCandidate(InlineDecision d)
295 return !InlDecisionIsFailure(d);
298 //------------------------------------------------------------------------
299 // InlDecisionIsDecided: check if this decision has been made
302 // d - the decision in question
305 // true if this inline has been decided one way or another
307 bool InlDecisionIsDecided(InlineDecision d)
311 case InlineDecision::NEVER:
312 case InlineDecision::FAILURE:
313 case InlineDecision::SUCCESS:
315 case InlineDecision::UNDECIDED:
316 case InlineDecision::CANDIDATE:
319 assert(!"Unexpected InlineDecision");
324 //------------------------------------------------------------------------
325 // InlineContext: default constructor
327 InlineContext::InlineContext(InlineStrategy* strategy)
328 : m_InlineStrategy(strategy)
334 , m_ImportedILSize(0)
335 , m_Offset(BAD_IL_OFFSET)
336 , m_Observation(InlineObservation::CALLEE_UNUSED_INITIAL)
337 , m_CodeSizeEstimate(0)
339 , m_Devirtualized(false)
342 #if defined(DEBUG) || defined(INLINE_DATA)
347 #endif // defined(DEBUG) || defined(INLINE_DATA)
352 #if defined(DEBUG) || defined(INLINE_DATA)
354 //------------------------------------------------------------------------
355 // Dump: Dump an InlineContext entry and all descendants to jitstdout
358 // indent - indentation level for this node
360 void InlineContext::Dump(unsigned indent)
362 // Handle fact that siblings are in reverse order.
363 if (m_Sibling != nullptr)
365 m_Sibling->Dump(indent);
368 // We may not know callee name in some of the failing cases
369 Compiler* compiler = m_InlineStrategy->GetCompiler();
370 const char* calleeName = nullptr;
372 if (m_Callee == nullptr)
375 calleeName = "<unknown>";
381 calleeName = compiler->eeGetMethodFullName(m_Callee);
383 calleeName = "callee";
384 #endif // defined(DEBUG)
387 mdMethodDef calleeToken = compiler->info.compCompHnd->getMethodDefFromMethod(m_Callee);
390 if (m_Parent == nullptr)
393 InlinePolicy* policy = InlinePolicy::GetPolicy(compiler, true);
394 printf("Inlines into %08X [via %s] %s\n", calleeToken, policy->GetName(), calleeName);
399 const char* inlineReason = InlGetObservationString(m_Observation);
400 const char* inlineResult = m_Success ? "" : "FAILED: ";
401 const char* devirtualized = m_Devirtualized ? " devirt" : "";
402 const char* guarded = m_Guarded ? " guarded" : "";
403 const char* unboxed = m_Unboxed ? " unboxed" : "";
405 if (m_Offset == BAD_IL_OFFSET)
407 printf("%*s[%u IL=???? TR=%06u %08X] [%s%s%s%s%s] %s\n", indent, "", m_Ordinal, m_TreeID, calleeToken,
408 inlineResult, inlineReason, guarded, devirtualized, unboxed, calleeName);
412 IL_OFFSET offset = jitGetILoffs(m_Offset);
413 printf("%*s[%u IL=%04d TR=%06u %08X] [%s%s%s%s%s] %s\n", indent, "", m_Ordinal, offset, m_TreeID,
414 calleeToken, inlineResult, inlineReason, guarded, devirtualized, unboxed, calleeName);
418 // Recurse to first child
419 if (m_Child != nullptr)
421 m_Child->Dump(indent + 2);
425 //------------------------------------------------------------------------
426 // DumpData: Dump a successful InlineContext entry, detailed data, and
427 // any successful descendant inlines
430 // indent - indentation level for this node
432 void InlineContext::DumpData(unsigned indent)
434 // Handle fact that siblings are in reverse order.
435 if (m_Sibling != nullptr)
437 m_Sibling->DumpData(indent);
440 Compiler* compiler = m_InlineStrategy->GetCompiler();
443 const char* calleeName = compiler->eeGetMethodFullName(m_Callee);
445 const char* calleeName = "callee";
446 #endif // defined(DEBUG)
448 if (m_Parent == nullptr)
450 // Root method... cons up a policy so we can display the name
451 InlinePolicy* policy = InlinePolicy::GetPolicy(compiler, true);
452 printf("\nInlines [%u] into \"%s\" [%s]\n", m_InlineStrategy->GetInlineCount(), calleeName, policy->GetName());
456 const char* inlineReason = InlGetObservationString(m_Observation);
457 printf("%*s%u,\"%s\",\"%s\",", indent, "", m_Ordinal, inlineReason, calleeName);
458 m_Policy->DumpData(jitstdout);
462 // Recurse to first child
463 if (m_Child != nullptr)
465 m_Child->DumpData(indent + 2);
469 //------------------------------------------------------------------------
470 // DumpXml: Dump an InlineContext entry and all descendants in xml format
473 // file - file for output
474 // indent - indentation level for this node
476 void InlineContext::DumpXml(FILE* file, unsigned indent)
478 // Handle fact that siblings are in reverse order.
479 if (m_Sibling != nullptr)
481 m_Sibling->DumpXml(file, indent);
484 // Optionally suppress failing inline records
485 if ((JitConfig.JitInlineDumpXml() == 3) && !m_Success)
490 const bool isRoot = m_Parent == nullptr;
491 const bool hasChild = m_Child != nullptr;
492 const char* inlineType = m_Success ? "Inline" : "FailedInline";
493 unsigned newIndent = indent;
497 Compiler* compiler = m_InlineStrategy->GetCompiler();
499 mdMethodDef calleeToken = compiler->info.compCompHnd->getMethodDefFromMethod(m_Callee);
500 unsigned calleeHash = compiler->compMethodHash(m_Callee);
501 const char* inlineReason = InlGetObservationString(m_Observation);
504 if (m_Offset != BAD_IL_OFFSET)
506 offset = (int)jitGetILoffs(m_Offset);
509 fprintf(file, "%*s<%s>\n", indent, "", inlineType);
510 fprintf(file, "%*s<Token>%08x</Token>\n", indent + 2, "", calleeToken);
511 fprintf(file, "%*s<Hash>%08x</Hash>\n", indent + 2, "", calleeHash);
512 fprintf(file, "%*s<Offset>%u</Offset>\n", indent + 2, "", offset);
513 fprintf(file, "%*s<Reason>%s</Reason>\n", indent + 2, "", inlineReason);
515 // Optionally, dump data about the inline
516 const int dumpDataSetting = JitConfig.JitInlineDumpData();
518 // JitInlineDumpData=1 -- dump data plus deltas for last inline only
519 if ((dumpDataSetting == 1) && (this == m_InlineStrategy->GetLastContext()))
521 fprintf(file, "%*s<Data>", indent + 2, "");
522 m_InlineStrategy->DumpDataContents(file);
523 fprintf(file, "</Data>\n");
526 // JitInlineDumpData=2 -- dump data for all inlines, no deltas
527 if ((dumpDataSetting == 2) && (m_Policy != nullptr))
529 fprintf(file, "%*s<Data>", indent + 2, "");
530 m_Policy->DumpData(file);
531 fprintf(file, "</Data>\n");
534 newIndent = indent + 2;
541 fprintf(file, "%*s<Inlines>\n", newIndent, "");
542 m_Child->DumpXml(file, newIndent + 2);
543 fprintf(file, "%*s</Inlines>\n", newIndent, "");
547 fprintf(file, "%*s<Inlines />\n", newIndent, "");
554 fprintf(file, "%*s</%s>\n", indent, "", inlineType);
558 #endif // defined(DEBUG) || defined(INLINE_DATA)
560 //------------------------------------------------------------------------
561 // InlineResult: Construct an InlineResult to evaluate a particular call
565 // compiler - the compiler instance examining a call for inlining
566 // call - the call in question
567 // stmt - statement containing the call (if known)
568 // description - string describing the context of the decision
570 InlineResult::InlineResult(Compiler* compiler, GenTreeCall* call, Statement* stmt, const char* description)
571 : m_RootCompiler(nullptr)
574 , m_InlineContext(nullptr)
577 , m_ImportedILSize(0)
578 , m_Description(description)
581 // Set the compiler instance
582 m_RootCompiler = compiler->impInlineRoot();
585 const bool isPrejitRoot = false;
586 m_Policy = InlinePolicy::GetPolicy(m_RootCompiler, isPrejitRoot);
588 // Pass along some optional information to the policy.
591 m_InlineContext = stmt->GetInlineContext();
592 m_Policy->NoteContext(m_InlineContext);
594 #if defined(DEBUG) || defined(INLINE_DATA)
595 m_Policy->NoteOffset(call->gtRawILOffset);
597 m_Policy->NoteOffset(stmt->GetILOffsetX());
598 #endif // defined(DEBUG) || defined(INLINE_DATA)
601 // Get method handle for caller. Note we use the
602 // handle for the "immediate" caller here.
603 m_Caller = compiler->info.compMethodHnd;
605 // Get method handle for callee, if known
606 if (m_Call->AsCall()->gtCallType == CT_USER_FUNC)
608 m_Callee = m_Call->AsCall()->gtCallMethHnd;
612 //------------------------------------------------------------------------
613 // InlineResult: Construct an InlineResult to evaluate a particular
614 // method as a possible inline candidate, while prejtting.
617 // compiler - the compiler instance doing the prejitting
618 // method - the method in question
619 // description - string describing the context of the decision
622 // Used only during prejitting to try and pre-identify methods that
623 // cannot be inlined, to help subsequent jit throughput.
625 // We use the inlCallee member to track the method since logically
626 // it is the callee here.
628 InlineResult::InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, const char* description)
629 : m_RootCompiler(nullptr)
632 , m_InlineContext(nullptr)
635 , m_Description(description)
638 // Set the compiler instance
639 m_RootCompiler = compiler->impInlineRoot();
642 const bool isPrejitRoot = true;
643 m_Policy = InlinePolicy::GetPolicy(m_RootCompiler, isPrejitRoot);
646 //------------------------------------------------------------------------
647 // Report: Dump, log, and report information about an inline decision.
650 // Called (automatically via the InlineResult dtor) when the
651 // inliner is done evaluating a candidate.
653 // Dumps state of the inline candidate, and if a decision was
654 // reached, sends it to the log and reports the decision back to the
655 // EE. Optionally update the method attribute to NOINLINE if
656 // observation and policy warrant.
658 // All this can be suppressed if desired by calling setReported()
659 // before the InlineResult goes out of scope.
661 void InlineResult::Report()
665 // If this is a failure of a specific inline candidate and we haven't captured
666 // a failing observation yet, do so now.
667 if (IsFailure() && (m_Call != nullptr))
669 // compiler should have revoked candidacy on the call by now
670 assert((m_Call->gtFlags & GTF_CALL_INLINE_CANDIDATE) == 0);
672 if (m_Call->gtInlineObservation == InlineObservation::CALLEE_UNUSED_INITIAL)
674 m_Call->gtInlineObservation = m_Policy->GetObservation();
679 // If we weren't actually inlining, user may have suppressed
680 // reporting via setReported(). If so, do nothing.
689 const char* callee = nullptr;
691 // Optionally dump the result
692 if (VERBOSE || m_RootCompiler->fgPrintInlinedMethods)
694 const char* format = "INLINER: during '%s' result '%s' reason '%s' for '%s' calling '%s'\n";
695 const char* caller = (m_Caller == nullptr) ? "n/a" : m_RootCompiler->eeGetMethodFullName(m_Caller);
697 callee = (m_Callee == nullptr) ? "n/a" : m_RootCompiler->eeGetMethodFullName(m_Callee);
699 JITDUMP(format, m_Description, ResultString(), ReasonString(), caller, callee);
703 // Was the result NEVER? If so we might want to propagate this to
706 if (IsNever() && m_Policy->PropagateNeverToRuntime())
708 // If we know the callee, and if the observation that got us
709 // to this Never inline state is something *other* than
710 // IS_NOINLINE, then we've uncovered a reason why this method
711 // can't ever be inlined. Update the callee method attributes
712 // so that future inline attempts for this callee fail faster.
714 InlineObservation obs = m_Policy->GetObservation();
716 if ((m_Callee != nullptr) && (obs != InlineObservation::CALLEE_IS_NOINLINE))
721 const char* obsString = InlGetObservationString(obs);
725 JITDUMP("\nINLINER: Marking %s as NOINLINE because of %s\n", callee, obsString);
727 else if (m_RootCompiler->fgPrintInlinedMethods)
729 printf("Marking %s as NOINLINE because of %s\n", callee, obsString);
734 COMP_HANDLE comp = m_RootCompiler->info.compCompHnd;
735 comp->setMethodAttribs(m_Callee, CORINFO_FLG_BAD_INLINEE);
741 const char* format = "INLINER: during '%s' result '%s' reason '%s'\n";
742 JITLOG_THIS(m_RootCompiler, (LL_INFO100000, format, m_Description, ResultString(), ReasonString()));
743 COMP_HANDLE comp = m_RootCompiler->info.compCompHnd;
744 comp->reportInliningDecision(m_Caller, m_Callee, Result(), ReasonString());
748 //------------------------------------------------------------------------
749 // InlineStrategy construtor
752 // compiler - root compiler instance
754 InlineStrategy::InlineStrategy(Compiler* compiler)
755 : m_Compiler(compiler)
756 , m_RootContext(nullptr)
757 , m_LastSuccessfulPolicy(nullptr)
758 , m_LastContext(nullptr)
759 , m_PrejitRootDecision(InlineDecision::UNDECIDED)
761 , m_CandidateCount(0)
762 , m_AlwaysCandidateCount(0)
763 , m_ForceCandidateCount(0)
764 , m_DiscretionaryCandidateCount(0)
765 , m_UnprofitableCandidateCount(0)
768 , m_MaxInlineSize(DEFAULT_MAX_INLINE_SIZE)
769 , m_MaxInlineDepth(DEFAULT_MAX_INLINE_DEPTH)
770 , m_InitialTimeBudget(0)
771 , m_InitialTimeEstimate(0)
772 , m_CurrentTimeBudget(0)
773 , m_CurrentTimeEstimate(0)
774 , m_InitialSizeEstimate(0)
775 , m_CurrentSizeEstimate(0)
776 , m_HasForceViaDiscretionary(false)
777 #if defined(DEBUG) || defined(INLINE_DATA)
778 , m_MethodXmlFilePosition(0)
780 #endif // defined(DEBUG) || defined(INLINE_DATA)
783 // Verify compiler is a root compiler instance
784 assert(m_Compiler->impInlineRoot() == m_Compiler);
788 // Possibly modify the max inline size.
790 // Default value of JitInlineSize is the same as our default.
791 // So normally this next line does not change the size.
792 m_MaxInlineSize = JitConfig.JitInlineSize();
794 // Up the max size under stress
795 if (m_Compiler->compInlineStress())
797 m_MaxInlineSize *= 10;
800 // But don't overdo it
801 if (m_MaxInlineSize > IMPLEMENTATION_MAX_INLINE_SIZE)
803 m_MaxInlineSize = IMPLEMENTATION_MAX_INLINE_SIZE;
806 // Verify: not too small, not too big.
807 assert(m_MaxInlineSize >= ALWAYS_INLINE_SIZE);
808 assert(m_MaxInlineSize <= IMPLEMENTATION_MAX_INLINE_SIZE);
810 // Possibly modify the max inline depth
812 // Default value of JitInlineDepth is the same as our default.
813 // So normally this next line does not change the size.
814 m_MaxInlineDepth = JitConfig.JitInlineDepth();
816 // But don't overdo it
817 if (m_MaxInlineDepth > IMPLEMENTATION_MAX_INLINE_DEPTH)
819 m_MaxInlineDepth = IMPLEMENTATION_MAX_INLINE_DEPTH;
825 //------------------------------------------------------------------------
826 // GetRootContext: get the InlineContext for the root method
829 // Root context; describes the method being jitted.
832 // Also initializes the jit time estimate and budget.
834 InlineContext* InlineStrategy::GetRootContext()
836 if (m_RootContext == nullptr)
838 // Allocate on first demand.
839 m_RootContext = NewRoot();
841 // Estimate how long the jit will take if there's no inlining
842 // done to this method.
843 m_InitialTimeEstimate = EstimateTime(m_RootContext);
844 m_CurrentTimeEstimate = m_InitialTimeEstimate;
846 // Set the initial budget for inlining. Note this is
847 // deliberately set very high and is intended to catch
848 // only pathological runaway inline cases.
849 m_InitialTimeBudget = BUDGET * m_InitialTimeEstimate;
850 m_CurrentTimeBudget = m_InitialTimeBudget;
852 // Estimate the code size if there's no inlining
853 m_InitialSizeEstimate = EstimateSize(m_RootContext);
854 m_CurrentSizeEstimate = m_InitialSizeEstimate;
857 assert(m_CurrentTimeEstimate > 0);
858 assert(m_CurrentSizeEstimate > 0);
860 // Cache as the "last" context created
861 m_LastContext = m_RootContext;
864 return m_RootContext;
867 //------------------------------------------------------------------------
868 // NoteAttempt: do bookkeeping for an inline attempt
871 // result -- InlineResult for successful inline candidate
873 void InlineStrategy::NoteAttempt(InlineResult* result)
875 assert(result->IsCandidate());
876 InlineObservation obs = result->GetObservation();
878 if (obs == InlineObservation::CALLEE_BELOW_ALWAYS_INLINE_SIZE)
880 m_AlwaysCandidateCount++;
882 else if (obs == InlineObservation::CALLEE_IS_FORCE_INLINE)
884 m_ForceCandidateCount++;
888 m_DiscretionaryCandidateCount++;
892 //------------------------------------------------------------------------
893 // DumpCsvHeader: dump header for csv inline stats
896 // fp -- file for dump output
898 void InlineStrategy::DumpCsvHeader(FILE* fp)
900 fprintf(fp, "\"InlineCalls\",");
901 fprintf(fp, "\"InlineCandidates\",");
902 fprintf(fp, "\"InlineAlways\",");
903 fprintf(fp, "\"InlineForce\",");
904 fprintf(fp, "\"InlineDiscretionary\",");
905 fprintf(fp, "\"InlineUnprofitable\",");
906 fprintf(fp, "\"InlineEarlyFail\",");
907 fprintf(fp, "\"InlineImport\",");
908 fprintf(fp, "\"InlineLateFail\",");
909 fprintf(fp, "\"InlineSuccess\",");
912 //------------------------------------------------------------------------
913 // DumpCsvData: dump data for csv inline stats
916 // fp -- file for dump output
918 void InlineStrategy::DumpCsvData(FILE* fp)
920 fprintf(fp, "%u,", m_CallCount);
921 fprintf(fp, "%u,", m_CandidateCount);
922 fprintf(fp, "%u,", m_AlwaysCandidateCount);
923 fprintf(fp, "%u,", m_ForceCandidateCount);
924 fprintf(fp, "%u,", m_DiscretionaryCandidateCount);
925 fprintf(fp, "%u,", m_UnprofitableCandidateCount);
927 // Early failures are cases where candates are rejected between
928 // the time the jit invokes the inlinee compiler and the time it
929 // starts to import the inlinee IL.
931 // So they are "cheaper" that late failures.
933 unsigned profitableCandidateCount = m_DiscretionaryCandidateCount - m_UnprofitableCandidateCount;
935 unsigned earlyFailCount =
936 m_CandidateCount - m_AlwaysCandidateCount - m_ForceCandidateCount - profitableCandidateCount;
938 fprintf(fp, "%u,", earlyFailCount);
940 unsigned lateFailCount = m_ImportCount - m_InlineCount;
942 fprintf(fp, "%u,", m_ImportCount);
943 fprintf(fp, "%u,", lateFailCount);
944 fprintf(fp, "%u,", m_InlineCount);
947 //------------------------------------------------------------------------
948 // EstimateTime: estimate impact of this inline on the method jit time
951 // context - context describing this inline
954 // Nominal estimate of jit time.
956 int InlineStrategy::EstimateTime(InlineContext* context)
958 // Simple linear models based on observations
959 // show time is fairly well predicted by IL size.
961 // Prediction varies for root and inlines.
962 if (context == m_RootContext)
964 return EstimateRootTime(context->GetILSize());
968 // Use amount of IL actually imported
969 return EstimateInlineTime(context->GetImportedILSize());
973 //------------------------------------------------------------------------
974 // EstimteRootTime: estimate jit time for method of this size with
978 // ilSize - size of the method's IL
981 // Nominal estimate of jit time.
984 // Based on observational data. Time is nominally microseconds.
986 int InlineStrategy::EstimateRootTime(unsigned ilSize)
988 return 60 + 3 * ilSize;
991 //------------------------------------------------------------------------
992 // EstimteInlineTime: estimate time impact on jitting for an inline
996 // ilSize - size of the method's IL
999 // Nominal increase in jit time.
1002 // Based on observational data. Time is nominally microseconds.
1003 // Small inlines will make the jit a bit faster.
1005 int InlineStrategy::EstimateInlineTime(unsigned ilSize)
1007 return -14 + 2 * ilSize;
1010 //------------------------------------------------------------------------
1011 // EstimateSize: estimate impact of this inline on the method size
1014 // context - context describing this inline
1017 // Nominal estimate of method size (bytes * 10)
1019 int InlineStrategy::EstimateSize(InlineContext* context)
1021 // Prediction varies for root and inlines.
1022 if (context == m_RootContext)
1024 // Simple linear models based on observations show root method
1025 // native code size is fairly well predicted by IL size.
1027 // Model below is for x64 on windows.
1028 unsigned ilSize = context->GetILSize();
1029 int estimate = (1312 + 228 * ilSize) / 10;
1035 // Use context's code size estimate.
1036 return context->GetCodeSizeEstimate();
1040 //------------------------------------------------------------------------
1041 // NoteOutcome: do bookkeeping for an inline
1044 // context - context for the inlie
1046 void InlineStrategy::NoteOutcome(InlineContext* context)
1048 // Note we can't generally count up failures here -- we only
1049 // create contexts for failures in debug modes, and even then
1050 // we may not get them all.
1051 if (context->IsSuccess())
1055 #if defined(DEBUG) || defined(INLINE_DATA)
1057 // Keep track of the inline targeted for data collection or,
1058 // if we don't have one (yet), the last successful inline.
1059 bool updateLast = (m_LastSuccessfulPolicy == nullptr) || !m_LastSuccessfulPolicy->IsDataCollectionTarget();
1063 m_LastContext = context;
1064 m_LastSuccessfulPolicy = context->m_Policy;
1068 // We only expect one inline to be a data collection
1070 assert(!context->m_Policy->IsDataCollectionTarget());
1073 #endif // defined(DEBUG) || defined(INLINE_DATA)
1077 // If callee is a force inline, increase budget, provided all
1078 // parent contexts are likewise force inlines.
1080 // If callee is discretionary or has a discretionary ancestor,
1081 // increase expense.
1083 InlineContext* currentContext = context;
1084 bool isForceInline = false;
1086 while (currentContext != m_RootContext)
1088 InlineObservation observation = currentContext->GetObservation();
1090 if (observation != InlineObservation::CALLEE_IS_FORCE_INLINE)
1094 // Interesting case where discretionary inlines pull
1095 // in a force inline...
1096 m_HasForceViaDiscretionary = true;
1099 isForceInline = false;
1103 isForceInline = true;
1104 currentContext = currentContext->GetParent();
1107 int timeDelta = EstimateTime(context);
1111 // Update budget since this inline was forced. Only allow
1112 // budget to increase.
1115 m_CurrentTimeBudget += timeDelta;
1119 // Update time estimate.
1120 m_CurrentTimeEstimate += timeDelta;
1122 // Update size estimate.
1124 // Sometimes estimates don't make sense. Don't let the method
1125 // size go negative.
1126 int sizeDelta = EstimateSize(context);
1128 if (m_CurrentSizeEstimate + sizeDelta <= 0)
1133 // Update the code size estimate.
1134 m_CurrentSizeEstimate += sizeDelta;
1138 //------------------------------------------------------------------------
1139 // BudgetCheck: return true if an inline of this size would likely
1140 // exceed the jit time budget for this method
1143 // ilSize - size of the method's IL
1146 // true if the inline would go over budget
1149 // Presumes all IL in the method will be imported.
1151 bool InlineStrategy::BudgetCheck(unsigned ilSize)
1153 const int timeDelta = EstimateInlineTime(ilSize);
1154 const bool result = (timeDelta + m_CurrentTimeEstimate > m_CurrentTimeBudget);
1158 JITDUMP("\nBudgetCheck: for IL Size %d, timeDelta %d + currentEstimate %d > currentBudget %d\n", ilSize,
1159 timeDelta, m_CurrentTimeEstimate, m_CurrentTimeBudget);
1165 //------------------------------------------------------------------------
1166 // NewRoot: construct an InlineContext for the root method
1169 // InlineContext for use as the root context
1172 // We leave m_Code as nullptr here (rather than the IL buffer
1173 // address of the root method) to preserve existing behavior, which
1174 // is to allow one recursive inline.
1176 InlineContext* InlineStrategy::NewRoot()
1178 InlineContext* rootContext = new (m_Compiler, CMK_Inlining) InlineContext(this);
1180 rootContext->m_ILSize = m_Compiler->info.compILCodeSize;
1181 rootContext->m_Code = m_Compiler->info.compCode;
1183 #if defined(DEBUG) || defined(INLINE_DATA)
1185 rootContext->m_Callee = m_Compiler->info.compMethodHnd;
1187 #endif // defined(DEBUG) || defined(INLINE_DATA)
1192 //------------------------------------------------------------------------
1193 // NewSuccess: construct an InlineContext for a successful inline
1194 // and link it into the context tree
1197 // inlineInfo - information about this inline
1200 // A new InlineContext for statements brought into the method by
1203 InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
1205 InlineContext* calleeContext = new (m_Compiler, CMK_Inlining) InlineContext(this);
1206 Statement* stmt = inlineInfo->iciStmt;
1207 BYTE* calleeIL = inlineInfo->inlineCandidateInfo->methInfo.ILCode;
1208 unsigned calleeILSize = inlineInfo->inlineCandidateInfo->methInfo.ILCodeSize;
1209 InlineContext* parentContext = stmt->GetInlineContext();
1210 GenTreeCall* originalCall = inlineInfo->inlineResult->GetCall();
1212 noway_assert(parentContext != nullptr);
1214 calleeContext->m_Code = calleeIL;
1215 calleeContext->m_ILSize = calleeILSize;
1216 calleeContext->m_Parent = parentContext;
1217 // Push on front here will put siblings in reverse lexical
1218 // order which we undo in the dumper
1219 calleeContext->m_Sibling = parentContext->m_Child;
1220 parentContext->m_Child = calleeContext;
1221 calleeContext->m_Child = nullptr;
1222 calleeContext->m_Offset = stmt->GetILOffsetX();
1223 calleeContext->m_Observation = inlineInfo->inlineResult->GetObservation();
1224 calleeContext->m_Success = true;
1225 calleeContext->m_Devirtualized = originalCall->IsDevirtualized();
1226 calleeContext->m_Guarded = originalCall->IsGuarded();
1227 calleeContext->m_Unboxed = originalCall->IsUnboxed();
1228 calleeContext->m_ImportedILSize = inlineInfo->inlineResult->GetImportedILSize();
1230 #if defined(DEBUG) || defined(INLINE_DATA)
1232 InlinePolicy* policy = inlineInfo->inlineResult->GetPolicy();
1234 calleeContext->m_Policy = policy;
1235 calleeContext->m_CodeSizeEstimate = policy->CodeSizeEstimate();
1236 calleeContext->m_Callee = inlineInfo->fncHandle;
1237 // +1 here since we set this before calling NoteOutcome.
1238 calleeContext->m_Ordinal = m_InlineCount + 1;
1239 // Update offset with more accurate info
1240 calleeContext->m_Offset = originalCall->gtRawILOffset;
1242 #endif // defined(DEBUG) || defined(INLINE_DATA)
1246 calleeContext->m_TreeID = originalCall->gtTreeID;
1248 #endif // defined(DEBUG)
1250 NoteOutcome(calleeContext);
1252 return calleeContext;
1255 #if defined(DEBUG) || defined(INLINE_DATA)
1257 //------------------------------------------------------------------------
1258 // NewFailure: construct an InlineContext for a failing inline
1259 // and link it into the context tree
1262 // stmt - statement containing the attempted inline
1263 // inlineResult - inlineResult for the attempt
1266 // A new InlineContext for diagnostic purposes
1268 InlineContext* InlineStrategy::NewFailure(Statement* stmt, InlineResult* inlineResult)
1270 // Check for a parent context first. We should now have a parent
1271 // context for all statements.
1272 InlineContext* parentContext = stmt->GetInlineContext();
1273 assert(parentContext != nullptr);
1274 InlineContext* failedContext = new (m_Compiler, CMK_Inlining) InlineContext(this);
1275 GenTreeCall* originalCall = inlineResult->GetCall();
1277 // Pushing the new context on the front of the parent child list
1278 // will put siblings in reverse lexical order which we undo in the
1280 failedContext->m_Parent = parentContext;
1281 failedContext->m_Sibling = parentContext->m_Child;
1282 parentContext->m_Child = failedContext;
1283 failedContext->m_Child = nullptr;
1284 failedContext->m_Offset = stmt->GetILOffsetX();
1285 failedContext->m_Observation = inlineResult->GetObservation();
1286 failedContext->m_Callee = inlineResult->GetCallee();
1287 failedContext->m_Success = false;
1288 failedContext->m_Devirtualized = originalCall->IsDevirtualized();
1289 failedContext->m_Guarded = originalCall->IsGuarded();
1290 failedContext->m_Unboxed = originalCall->IsUnboxed();
1292 assert(InlIsValidObservation(failedContext->m_Observation));
1294 #if defined(DEBUG) || defined(INLINE_DATA)
1296 // Update offset with more accurate info
1297 failedContext->m_Offset = originalCall->gtRawILOffset;
1299 #endif // #if defined(DEBUG) || defined(INLINE_DATA)
1303 failedContext->m_TreeID = originalCall->gtTreeID;
1305 #endif // defined(DEBUG)
1307 NoteOutcome(failedContext);
1309 return failedContext;
1312 //------------------------------------------------------------------------
1313 // Dump: dump description of inline behavior
1316 // showBudget - also dump final budget values
1318 void InlineStrategy::Dump(bool showBudget)
1320 m_RootContext->Dump();
1327 printf("Budget: initialTime=%d, finalTime=%d, initialBudget=%d, currentBudget=%d\n", m_InitialTimeEstimate,
1328 m_CurrentTimeEstimate, m_InitialTimeBudget, m_CurrentTimeBudget);
1330 if (m_CurrentTimeBudget > m_InitialTimeBudget)
1332 printf("Budget: increased by %d because of force inlines\n", m_CurrentTimeBudget - m_InitialTimeBudget);
1335 if (m_CurrentTimeEstimate > m_CurrentTimeBudget)
1337 printf("Budget: went over budget by %d\n", m_CurrentTimeEstimate - m_CurrentTimeBudget);
1340 if (m_HasForceViaDiscretionary)
1342 printf("Budget: discretionary inline caused a force inline\n");
1345 printf("Budget: initialSize=%d, finalSize=%d\n", m_InitialSizeEstimate, m_CurrentSizeEstimate);
1348 // Static to track emission of the inline data header
1350 bool InlineStrategy::s_HasDumpedDataHeader = false;
1352 //------------------------------------------------------------------------
1353 // DumpData: dump data about the last successful inline into this method
1354 // in a format suitable for automated analysis.
1356 void InlineStrategy::DumpData()
1358 // Is dumping enabled? If not, nothing to do.
1359 if (JitConfig.JitInlineDumpData() == 0)
1364 // If we're also dumping inline XML, we'll let it dump the data.
1365 if (JitConfig.JitInlineDumpXml() != 0)
1370 // Don't dump anything if limiting is on and we didn't reach
1371 // the limit while inlining.
1373 // This serves to filter out duplicate data.
1374 const int limit = JitConfig.JitInlineLimit();
1376 if ((limit >= 0) && (m_InlineCount < static_cast<unsigned>(limit)))
1381 // Dump header, if not already dumped
1382 if (!s_HasDumpedDataHeader)
1384 DumpDataHeader(stderr);
1385 s_HasDumpedDataHeader = true;
1389 DumpDataContents(stderr);
1390 fprintf(stderr, "\n");
1393 //------------------------------------------------------------------------
1394 // DumpDataEnsurePolicyIsSet: ensure m_LastSuccessfulPolicy describes the
1395 // inline policy in effect.
1398 // Needed for methods that don't have any successful inlines.
1400 void InlineStrategy::DumpDataEnsurePolicyIsSet()
1402 // Cache references to compiler substructures.
1403 const Compiler::Info& info = m_Compiler->info;
1404 const Compiler::Options& opts = m_Compiler->opts;
1406 // If there weren't any successful inlines, we won't have a
1407 // successful policy, so fake one up.
1408 if (m_LastSuccessfulPolicy == nullptr)
1410 const bool isPrejitRoot = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT);
1411 m_LastSuccessfulPolicy = InlinePolicy::GetPolicy(m_Compiler, isPrejitRoot);
1413 // Add in a bit of data....
1414 const bool isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0;
1415 m_LastSuccessfulPolicy->NoteBool(InlineObservation::CALLEE_IS_FORCE_INLINE, isForceInline);
1416 m_LastSuccessfulPolicy->NoteInt(InlineObservation::CALLEE_IL_CODE_SIZE, info.compMethodInfo->ILCodeSize);
1420 //------------------------------------------------------------------------
1421 // DumpDataHeader: dump header for inline data.
1424 // file - file for data output
1426 void InlineStrategy::DumpDataHeader(FILE* file)
1428 DumpDataEnsurePolicyIsSet();
1429 const int limit = JitConfig.JitInlineLimit();
1430 fprintf(file, "*** Inline Data: Policy=%s JitInlineLimit=%d ***\n", m_LastSuccessfulPolicy->GetName(), limit);
1431 DumpDataSchema(file);
1432 fprintf(file, "\n");
1435 //------------------------------------------------------------------------
1436 // DumpSchema: dump schema for inline data.
1439 // file - file for data output
1441 void InlineStrategy::DumpDataSchema(FILE* file)
1443 DumpDataEnsurePolicyIsSet();
1444 fprintf(file, "Method,Version,HotSize,ColdSize,JitTime,SizeEstimate,TimeEstimate,");
1445 m_LastSuccessfulPolicy->DumpSchema(file);
1448 //------------------------------------------------------------------------
1449 // DumpDataContents: dump contents of inline data
1452 // file - file for data output
1454 void InlineStrategy::DumpDataContents(FILE* file)
1456 DumpDataEnsurePolicyIsSet();
1458 // Cache references to compiler substructures.
1459 const Compiler::Info& info = m_Compiler->info;
1460 const Compiler::Options& opts = m_Compiler->opts;
1462 // We'd really like the method identifier to be unique and
1463 // durable across crossgen invocations. Not clear how to
1464 // accomplish this, so we'll use the token for now.
1466 // Post processing will have to filter out all data from
1467 // methods where the root entry appears multiple times.
1468 mdMethodDef currentMethodToken = info.compCompHnd->getMethodDefFromMethod(info.compMethodHnd);
1470 // Convert time spent jitting into microseconds
1471 unsigned microsecondsSpentJitting = 0;
1472 unsigned __int64 compCycles = m_Compiler->getInlineCycleCount();
1475 double countsPerSec = CachedCyclesPerSecond();
1476 double counts = (double)compCycles;
1477 microsecondsSpentJitting = (unsigned)((counts / countsPerSec) * 1000 * 1000);
1480 fprintf(file, "%08X,%u,%u,%u,%u,%d,%d,", currentMethodToken, m_InlineCount, info.compTotalHotCodeSize,
1481 info.compTotalColdCodeSize, microsecondsSpentJitting, m_CurrentSizeEstimate / 10, m_CurrentTimeEstimate);
1482 m_LastSuccessfulPolicy->DumpData(file);
1485 // Static to track emission of the xml data header
1486 // and lock to prevent interleaved file writes
1488 bool InlineStrategy::s_HasDumpedXmlHeader = false;
1489 CritSecObject InlineStrategy::s_XmlWriterLock;
1491 //------------------------------------------------------------------------
1492 // DumpXml: dump xml-formatted version of the inline tree.
1495 // file - file for data output
1496 // indent - indent level of this element
1498 void InlineStrategy::DumpXml(FILE* file, unsigned indent)
1500 if (JitConfig.JitInlineDumpXml() == 0)
1505 // Lock to prevent interleaving of trees.
1506 CritSecHolder writeLock(s_XmlWriterLock);
1509 if (!s_HasDumpedXmlHeader)
1511 DumpDataEnsurePolicyIsSet();
1513 fprintf(file, "<?xml version=\"1.0\"?>\n");
1514 fprintf(file, "<InlineForest>\n");
1515 fprintf(file, "<Policy>%s</Policy>\n", m_LastSuccessfulPolicy->GetName());
1517 const int dumpDataSetting = JitConfig.JitInlineDumpData();
1518 if (dumpDataSetting != 0)
1520 fprintf(file, "<DataSchema>");
1522 if (dumpDataSetting == 1)
1524 // JitInlineDumpData=1 -- dump schema for data plus deltas
1525 DumpDataSchema(file);
1527 else if (dumpDataSetting == 2)
1529 // JitInlineDumpData=2 -- dump schema for data only
1530 m_LastSuccessfulPolicy->DumpSchema(file);
1533 fprintf(file, "</DataSchema>\n");
1536 fprintf(file, "<Methods>\n");
1537 s_HasDumpedXmlHeader = true;
1540 // If we're dumping "minimal" Xml, and we didn't do
1541 // any inlines into this method, then there's nothing
1543 if ((m_InlineCount == 0) && (JitConfig.JitInlineDumpXml() >= 2))
1548 // Cache references to compiler substructures.
1549 const Compiler::Info& info = m_Compiler->info;
1550 const Compiler::Options& opts = m_Compiler->opts;
1552 const bool isPrejitRoot = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT);
1553 const bool isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0;
1555 // We'd really like the method identifier to be unique and
1556 // durable across crossgen invocations. Not clear how to
1557 // accomplish this, so we'll use the token for now.
1559 // Post processing will have to filter out all data from
1560 // methods where the root entry appears multiple times.
1561 mdMethodDef currentMethodToken = info.compCompHnd->getMethodDefFromMethod(info.compMethodHnd);
1563 unsigned hash = info.compMethodHash();
1565 // Convert time spent jitting into microseconds
1566 unsigned microsecondsSpentJitting = 0;
1567 unsigned __int64 compCycles = m_Compiler->getInlineCycleCount();
1570 double countsPerSec = CachedCyclesPerSecond();
1571 double counts = (double)compCycles;
1572 microsecondsSpentJitting = (unsigned)((counts / countsPerSec) * 1000 * 1000);
1575 // Get method name just for root method, to make it a bit easier
1576 // to search for things in the inline xml.
1577 const char* methodName = info.compCompHnd->getMethodName(info.compMethodHnd, nullptr);
1579 // Cheap xml quoting for values. Only < and & are troublemakers,
1580 // but change > for symmetry.
1582 // Ok to truncate name, just ensure it's null terminated.
1584 strncpy(buf, methodName, sizeof(buf));
1585 buf[sizeof(buf) - 1] = 0;
1587 for (size_t i = 0; i < _countof(buf); i++)
1605 fprintf(file, "%*s<Method>\n", indent, "");
1606 fprintf(file, "%*s<Token>%08x</Token>\n", indent + 2, "", currentMethodToken);
1607 fprintf(file, "%*s<Hash>%08x</Hash>\n", indent + 2, "", hash);
1608 fprintf(file, "%*s<Name>%s</Name>\n", indent + 2, "", buf);
1609 fprintf(file, "%*s<InlineCount>%u</InlineCount>\n", indent + 2, "", m_InlineCount);
1610 fprintf(file, "%*s<HotSize>%u</HotSize>\n", indent + 2, "", info.compTotalHotCodeSize);
1611 fprintf(file, "%*s<ColdSize>%u</ColdSize>\n", indent + 2, "", info.compTotalColdCodeSize);
1612 fprintf(file, "%*s<JitTime>%u</JitTime>\n", indent + 2, "", microsecondsSpentJitting);
1613 fprintf(file, "%*s<SizeEstimate>%u</SizeEstimate>\n", indent + 2, "", m_CurrentSizeEstimate / 10);
1614 fprintf(file, "%*s<TimeEstimate>%u</TimeEstimate>\n", indent + 2, "", m_CurrentTimeEstimate);
1616 // For prejit roots also propagate out the assessment of the root method
1619 fprintf(file, "%*s<PrejitDecision>%s</PrejitDecision>\n", indent + 2, "",
1620 InlGetDecisionString(m_PrejitRootDecision));
1621 fprintf(file, "%*s<PrejitObservation>%s</PrejitObservation>\n", indent + 2, "",
1622 InlGetObservationString(m_PrejitRootObservation));
1625 // Root context will be null if we're not optimizing the method.
1627 // Note there are cases of this in System.Private.CoreLib even in release builds,
1628 // eg Task.NotifyDebuggerOfWaitCompletion.
1630 // For such methods there aren't any inlines.
1631 if (m_RootContext != nullptr)
1633 m_RootContext->DumpXml(file, indent + 2);
1637 fprintf(file, "%*s<Inlines/>\n", indent + 2, "");
1640 fprintf(file, "%*s</Method>\n", indent, "");
1643 //------------------------------------------------------------------------
1644 // FinalizeXml: finalize the xml-formatted version of the inline tree.
1647 // file - file for data output
1649 void InlineStrategy::FinalizeXml(FILE* file)
1651 // If we dumped the header, dump a footer
1652 if (s_HasDumpedXmlHeader)
1654 fprintf(file, "</Methods>\n");
1655 fprintf(file, "</InlineForest>\n");
1658 // Workaroud compShutdown getting called twice.
1659 s_HasDumpedXmlHeader = false;
1662 // Finalize reading inline xml
1663 ReplayPolicy::FinalizeXml();
1666 //------------------------------------------------------------------------
1667 // GetRandom: setup or access random state
1670 // New or pre-existing random state.
1673 // Random state is kept per jit compilation request. Seed is partially
1674 // specified externally (via stress or policy setting) and partially
1675 // specified internally via method hash.
1677 CLRRandom* InlineStrategy::GetRandom()
1679 if (m_Random == nullptr)
1681 int externalSeed = 0;
1685 if (m_Compiler->compRandomInlineStress())
1687 externalSeed = getJitStressLevel();
1688 // We can set COMPlus_JitStressModeNames without setting COMPlus_JitStress,
1689 // but we need external seed to be non-zero.
1690 if (externalSeed == 0)
1698 int randomPolicyFlag = JitConfig.JitInlinePolicyRandom();
1699 if (randomPolicyFlag != 0)
1701 externalSeed = randomPolicyFlag;
1704 int internalSeed = m_Compiler->info.compMethodHash();
1706 assert(externalSeed != 0);
1707 assert(internalSeed != 0);
1709 int seed = externalSeed ^ internalSeed;
1711 m_Random = new (m_Compiler, CMK_Inlining) CLRRandom();
1712 m_Random->Init(seed);
1718 #endif // defined(DEBUG) || defined(INLINE_DATA)
1720 //------------------------------------------------------------------------
1721 // IsInliningDisabled: allow strategy to disable inlining in the method being jitted
1724 // Only will return true in debug or special release builds.
1725 // Expects JitNoInlineRange to be set to the hashes of methods
1726 // where inlining is disabled.
1728 bool InlineStrategy::IsInliningDisabled()
1731 #if defined(DEBUG) || defined(INLINE_DATA)
1733 static ConfigMethodRange range;
1734 const WCHAR* noInlineRange = JitConfig.JitNoInlineRange();
1736 if (noInlineRange == nullptr)
1741 // If we have a config string we have at least one entry. Count
1742 // number of spaces in our config string to see if there are
1743 // more. Number of ranges we need is 2x that value.
1744 unsigned entryCount = 1;
1745 for (const WCHAR* p = noInlineRange; *p != 0; p++)
1753 range.EnsureInit(noInlineRange, 2 * entryCount);
1754 assert(!range.Error());
1756 return range.Contains(m_Compiler->info.compMethodHash());
1762 #endif // defined(DEBUG) || defined(INLINE_DATA)