Improve the speed of using COMPlus_JitTimeLogCsv (#49798)
[platform/upstream/dotnet/runtime.git] / src / coreclr / jit / inline.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3
4 #include "jitpch.h"
5 #ifdef _MSC_VER
6 #pragma hdrstop
7 #endif
8
9 #include "inlinepolicy.h"
10
11 // Lookup table for inline description strings
12
13 static const char* InlineDescriptions[] = {
14 #define INLINE_OBSERVATION(name, type, description, impact, target) description,
15 #include "inline.def"
16 #undef INLINE_OBSERVATION
17 };
18
19 // Lookup table for inline targets
20
21 static const InlineTarget InlineTargets[] = {
22 #define INLINE_OBSERVATION(name, type, description, impact, target) InlineTarget::target,
23 #include "inline.def"
24 #undef INLINE_OBSERVATION
25 };
26
27 // Lookup table for inline impacts
28
29 static const InlineImpact InlineImpacts[] = {
30 #define INLINE_OBSERVATION(name, type, description, impact, target) InlineImpact::impact,
31 #include "inline.def"
32 #undef INLINE_OBSERVATION
33 };
34
35 #ifdef DEBUG
36
37 //------------------------------------------------------------------------
38 // InlIsValidObservation: run a validity check on an inline observation
39 //
40 // Arguments:
41 //    obs - the observation in question
42 //
43 // Return Value:
44 //    true if the observation is valid
45
46 bool InlIsValidObservation(InlineObservation obs)
47 {
48     return ((obs > InlineObservation::CALLEE_UNUSED_INITIAL) && (obs < InlineObservation::CALLEE_UNUSED_FINAL));
49 }
50
51 #endif // DEBUG
52
53 //------------------------------------------------------------------------
54 // InlGetObservationString: get a string describing this inline observation
55 //
56 // Arguments:
57 //    obs - the observation in question
58 //
59 // Return Value:
60 //    string describing the observation
61
62 const char* InlGetObservationString(InlineObservation obs)
63 {
64     assert(InlIsValidObservation(obs));
65     return InlineDescriptions[static_cast<int>(obs)];
66 }
67
68 //------------------------------------------------------------------------
69 // InlGetTarget: get the target of an inline observation
70 //
71 // Arguments:
72 //    obs - the observation in question
73 //
74 // Return Value:
75 //    enum describing the target
76
77 InlineTarget InlGetTarget(InlineObservation obs)
78 {
79     assert(InlIsValidObservation(obs));
80     return InlineTargets[static_cast<int>(obs)];
81 }
82
83 //------------------------------------------------------------------------
84 // InlGetTargetString: get a string describing the target of an inline observation
85 //
86 // Arguments:
87 //    obs - the observation in question
88 //
89 // Return Value:
90 //    string describing the target
91
92 const char* InlGetTargetString(InlineObservation obs)
93 {
94     InlineTarget t = InlGetTarget(obs);
95     switch (t)
96     {
97         case InlineTarget::CALLER:
98             return "caller";
99         case InlineTarget::CALLEE:
100             return "callee";
101         case InlineTarget::CALLSITE:
102             return "call site";
103         default:
104             return "unexpected target";
105     }
106 }
107
108 //------------------------------------------------------------------------
109 // InlGetImpact: get the impact of an inline observation
110 //
111 // Arguments:
112 //    obs - the observation in question
113 //
114 // Return Value:
115 //    enum value describing the impact
116
117 InlineImpact InlGetImpact(InlineObservation obs)
118 {
119     assert(InlIsValidObservation(obs));
120     return InlineImpacts[static_cast<int>(obs)];
121 }
122
123 //------------------------------------------------------------------------
124 // InlGetImpactString: get a string describing the impact of an inline observation
125 //
126 // Arguments:
127 //    obs - the observation in question
128 //
129 // Return Value:
130 //    string describing the impact
131
132 const char* InlGetImpactString(InlineObservation obs)
133 {
134     InlineImpact i = InlGetImpact(obs);
135     switch (i)
136     {
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";
147         default:
148             return "unexpected impact";
149     }
150 }
151
152 //------------------------------------------------------------------------
153 // InlGetCorInfoInlineDecision: translate decision into a CorInfoInline
154 //
155 // Arguments:
156 //    d - the decision in question
157 //
158 // Return Value:
159 //    CorInfoInline value representing the decision
160
161 CorInfoInline InlGetCorInfoInlineDecision(InlineDecision d)
162 {
163     switch (d)
164     {
165         case InlineDecision::SUCCESS:
166             return INLINE_PASS;
167         case InlineDecision::FAILURE:
168             return INLINE_FAIL;
169         case InlineDecision::NEVER:
170             return INLINE_NEVER;
171         default:
172             assert(!"Unexpected InlineDecision");
173             unreached();
174     }
175 }
176
177 //------------------------------------------------------------------------
178 // InlGetDecisionString: get a string representing this decision
179 //
180 // Arguments:
181 //    d - the decision in question
182 //
183 // Return Value:
184 //    string representing the decision
185
186 const char* InlGetDecisionString(InlineDecision d)
187 {
188     switch (d)
189     {
190         case InlineDecision::SUCCESS:
191             return "success";
192         case InlineDecision::FAILURE:
193             return "failed this call site";
194         case InlineDecision::NEVER:
195             return "failed this callee";
196         case InlineDecision::CANDIDATE:
197             return "candidate";
198         case InlineDecision::UNDECIDED:
199             return "undecided";
200         default:
201             assert(!"Unexpected InlineDecision");
202             unreached();
203     }
204 }
205
206 //------------------------------------------------------------------------
207 // InlDecisionIsFailure: check if this decision describes a failing inline
208 //
209 // Arguments:
210 //    d - the decision in question
211 //
212 // Return Value:
213 //    true if the inline is definitely a failure
214
215 bool InlDecisionIsFailure(InlineDecision d)
216 {
217     switch (d)
218     {
219         case InlineDecision::SUCCESS:
220         case InlineDecision::UNDECIDED:
221         case InlineDecision::CANDIDATE:
222             return false;
223         case InlineDecision::FAILURE:
224         case InlineDecision::NEVER:
225             return true;
226         default:
227             assert(!"Unexpected InlineDecision");
228             unreached();
229     }
230 }
231
232 //------------------------------------------------------------------------
233 // InlDecisionIsSuccess: check if this decision describes a sucessful inline
234 //
235 // Arguments:
236 //    d - the decision in question
237 //
238 // Return Value:
239 //    true if the inline is definitely a success
240
241 bool InlDecisionIsSuccess(InlineDecision d)
242 {
243     switch (d)
244     {
245         case InlineDecision::SUCCESS:
246             return true;
247         case InlineDecision::FAILURE:
248         case InlineDecision::NEVER:
249         case InlineDecision::UNDECIDED:
250         case InlineDecision::CANDIDATE:
251             return false;
252         default:
253             assert(!"Unexpected InlineDecision");
254             unreached();
255     }
256 }
257
258 //------------------------------------------------------------------------
259 // InlDecisionIsNever: check if this decision describes a never inline
260 //
261 // Arguments:
262 //    d - the decision in question
263 //
264 // Return Value:
265 //    true if the inline is a never inline case
266
267 bool InlDecisionIsNever(InlineDecision d)
268 {
269     switch (d)
270     {
271         case InlineDecision::NEVER:
272             return true;
273         case InlineDecision::FAILURE:
274         case InlineDecision::SUCCESS:
275         case InlineDecision::UNDECIDED:
276         case InlineDecision::CANDIDATE:
277             return false;
278         default:
279             assert(!"Unexpected InlineDecision");
280             unreached();
281     }
282 }
283
284 //------------------------------------------------------------------------
285 // InlDecisionIsCandidate: check if this decision describes a viable candidate
286 //
287 // Arguments:
288 //    d - the decision in question
289 //
290 // Return Value:
291 //    true if this inline still might happen
292
293 bool InlDecisionIsCandidate(InlineDecision d)
294 {
295     return !InlDecisionIsFailure(d);
296 }
297
298 //------------------------------------------------------------------------
299 // InlDecisionIsDecided: check if this decision has been made
300 //
301 // Arguments:
302 //    d - the decision in question
303 //
304 // Return Value:
305 //    true if this inline has been decided one way or another
306
307 bool InlDecisionIsDecided(InlineDecision d)
308 {
309     switch (d)
310     {
311         case InlineDecision::NEVER:
312         case InlineDecision::FAILURE:
313         case InlineDecision::SUCCESS:
314             return true;
315         case InlineDecision::UNDECIDED:
316         case InlineDecision::CANDIDATE:
317             return false;
318         default:
319             assert(!"Unexpected InlineDecision");
320             unreached();
321     }
322 }
323
324 //------------------------------------------------------------------------
325 // InlineContext: default constructor
326
327 InlineContext::InlineContext(InlineStrategy* strategy)
328     : m_InlineStrategy(strategy)
329     , m_Parent(nullptr)
330     , m_Child(nullptr)
331     , m_Sibling(nullptr)
332     , m_Code(nullptr)
333     , m_ILSize(0)
334     , m_ImportedILSize(0)
335     , m_Offset(BAD_IL_OFFSET)
336     , m_Observation(InlineObservation::CALLEE_UNUSED_INITIAL)
337     , m_CodeSizeEstimate(0)
338     , m_Success(true)
339     , m_Devirtualized(false)
340     , m_Guarded(false)
341     , m_Unboxed(false)
342 #if defined(DEBUG) || defined(INLINE_DATA)
343     , m_Policy(nullptr)
344     , m_Callee(nullptr)
345     , m_TreeID(0)
346     , m_Ordinal(0)
347 #endif // defined(DEBUG) || defined(INLINE_DATA)
348 {
349     // Empty
350 }
351
352 #if defined(DEBUG) || defined(INLINE_DATA)
353
354 //------------------------------------------------------------------------
355 // Dump: Dump an InlineContext entry and all descendants to jitstdout
356 //
357 // Arguments:
358 //    indent   - indentation level for this node
359
360 void InlineContext::Dump(unsigned indent)
361 {
362     // Handle fact that siblings are in reverse order.
363     if (m_Sibling != nullptr)
364     {
365         m_Sibling->Dump(indent);
366     }
367
368     // We may not know callee name in some of the failing cases
369     Compiler*   compiler   = m_InlineStrategy->GetCompiler();
370     const char* calleeName = nullptr;
371
372     if (m_Callee == nullptr)
373     {
374         assert(!m_Success);
375         calleeName = "<unknown>";
376     }
377     else
378     {
379
380 #if defined(DEBUG)
381         calleeName = compiler->eeGetMethodFullName(m_Callee);
382 #else
383         calleeName         = "callee";
384 #endif // defined(DEBUG)
385     }
386
387     mdMethodDef calleeToken = compiler->info.compCompHnd->getMethodDefFromMethod(m_Callee);
388
389     // Dump this node
390     if (m_Parent == nullptr)
391     {
392         // Root method
393         InlinePolicy* policy = InlinePolicy::GetPolicy(compiler, true);
394         printf("Inlines into %08X [via %s] %s\n", calleeToken, policy->GetName(), calleeName);
395     }
396     else
397     {
398         // Inline attempt.
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" : "";
404
405         if (m_Offset == BAD_IL_OFFSET)
406         {
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);
409         }
410         else
411         {
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);
415         }
416     }
417
418     // Recurse to first child
419     if (m_Child != nullptr)
420     {
421         m_Child->Dump(indent + 2);
422     }
423 }
424
425 //------------------------------------------------------------------------
426 // DumpData: Dump a successful InlineContext entry, detailed data, and
427 //  any successful descendant inlines
428 //
429 // Arguments:
430 //    indent   - indentation level for this node
431
432 void InlineContext::DumpData(unsigned indent)
433 {
434     // Handle fact that siblings are in reverse order.
435     if (m_Sibling != nullptr)
436     {
437         m_Sibling->DumpData(indent);
438     }
439
440     Compiler* compiler = m_InlineStrategy->GetCompiler();
441
442 #if defined(DEBUG)
443     const char* calleeName = compiler->eeGetMethodFullName(m_Callee);
444 #else
445     const char* calleeName = "callee";
446 #endif // defined(DEBUG)
447
448     if (m_Parent == nullptr)
449     {
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());
453     }
454     else if (m_Success)
455     {
456         const char* inlineReason = InlGetObservationString(m_Observation);
457         printf("%*s%u,\"%s\",\"%s\",", indent, "", m_Ordinal, inlineReason, calleeName);
458         m_Policy->DumpData(jitstdout);
459         printf("\n");
460     }
461
462     // Recurse to first child
463     if (m_Child != nullptr)
464     {
465         m_Child->DumpData(indent + 2);
466     }
467 }
468
469 //------------------------------------------------------------------------
470 // DumpXml: Dump an InlineContext entry and all descendants in xml format
471 //
472 // Arguments:
473 //    file     - file for output
474 //    indent   - indentation level for this node
475
476 void InlineContext::DumpXml(FILE* file, unsigned indent)
477 {
478     // Handle fact that siblings are in reverse order.
479     if (m_Sibling != nullptr)
480     {
481         m_Sibling->DumpXml(file, indent);
482     }
483
484     // Optionally suppress failing inline records
485     if ((JitConfig.JitInlineDumpXml() == 3) && !m_Success)
486     {
487         return;
488     }
489
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;
494
495     if (!isRoot)
496     {
497         Compiler* compiler = m_InlineStrategy->GetCompiler();
498
499         mdMethodDef calleeToken  = compiler->info.compCompHnd->getMethodDefFromMethod(m_Callee);
500         unsigned    calleeHash   = compiler->compMethodHash(m_Callee);
501         const char* inlineReason = InlGetObservationString(m_Observation);
502
503         int offset = -1;
504         if (m_Offset != BAD_IL_OFFSET)
505         {
506             offset = (int)jitGetILoffs(m_Offset);
507         }
508
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);
514
515         // Optionally, dump data about the inline
516         const int dumpDataSetting = JitConfig.JitInlineDumpData();
517
518         // JitInlineDumpData=1 -- dump data plus deltas for last inline only
519         if ((dumpDataSetting == 1) && (this == m_InlineStrategy->GetLastContext()))
520         {
521             fprintf(file, "%*s<Data>", indent + 2, "");
522             m_InlineStrategy->DumpDataContents(file);
523             fprintf(file, "</Data>\n");
524         }
525
526         // JitInlineDumpData=2 -- dump data for all inlines, no deltas
527         if ((dumpDataSetting == 2) && (m_Policy != nullptr))
528         {
529             fprintf(file, "%*s<Data>", indent + 2, "");
530             m_Policy->DumpData(file);
531             fprintf(file, "</Data>\n");
532         }
533
534         newIndent = indent + 2;
535     }
536
537     // Handle children
538
539     if (hasChild)
540     {
541         fprintf(file, "%*s<Inlines>\n", newIndent, "");
542         m_Child->DumpXml(file, newIndent + 2);
543         fprintf(file, "%*s</Inlines>\n", newIndent, "");
544     }
545     else
546     {
547         fprintf(file, "%*s<Inlines />\n", newIndent, "");
548     }
549
550     // Close out
551
552     if (!isRoot)
553     {
554         fprintf(file, "%*s</%s>\n", indent, "", inlineType);
555     }
556 }
557
558 #endif // defined(DEBUG) || defined(INLINE_DATA)
559
560 //------------------------------------------------------------------------
561 // InlineResult: Construct an InlineResult to evaluate a particular call
562 // for inlining.
563 //
564 // Arguments:
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
569
570 InlineResult::InlineResult(Compiler* compiler, GenTreeCall* call, Statement* stmt, const char* description)
571     : m_RootCompiler(nullptr)
572     , m_Policy(nullptr)
573     , m_Call(call)
574     , m_InlineContext(nullptr)
575     , m_Caller(nullptr)
576     , m_Callee(nullptr)
577     , m_ImportedILSize(0)
578     , m_Description(description)
579     , m_Reported(false)
580 {
581     // Set the compiler instance
582     m_RootCompiler = compiler->impInlineRoot();
583
584     // Set the policy
585     const bool isPrejitRoot = false;
586     m_Policy                = InlinePolicy::GetPolicy(m_RootCompiler, isPrejitRoot);
587
588     // Pass along some optional information to the policy.
589     if (stmt != nullptr)
590     {
591         m_InlineContext = stmt->GetInlineContext();
592         m_Policy->NoteContext(m_InlineContext);
593
594 #if defined(DEBUG) || defined(INLINE_DATA)
595         m_Policy->NoteOffset(call->gtRawILOffset);
596 #else
597         m_Policy->NoteOffset(stmt->GetILOffsetX());
598 #endif // defined(DEBUG) || defined(INLINE_DATA)
599     }
600
601     // Get method handle for caller. Note we use the
602     // handle for the "immediate" caller here.
603     m_Caller = compiler->info.compMethodHnd;
604
605     // Get method handle for callee, if known
606     if (m_Call->AsCall()->gtCallType == CT_USER_FUNC)
607     {
608         m_Callee = m_Call->AsCall()->gtCallMethHnd;
609     }
610 }
611
612 //------------------------------------------------------------------------
613 // InlineResult: Construct an InlineResult to evaluate a particular
614 // method as a possible inline candidate, while prejtting.
615 //
616 // Arguments:
617 //    compiler    - the compiler instance doing the prejitting
618 //    method      - the method in question
619 //    description - string describing the context of the decision
620 //
621 // Notes:
622 //    Used only during prejitting to try and pre-identify methods that
623 //    cannot be inlined, to help subsequent jit throughput.
624 //
625 //    We use the inlCallee member to track the method since logically
626 //    it is the callee here.
627
628 InlineResult::InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, const char* description)
629     : m_RootCompiler(nullptr)
630     , m_Policy(nullptr)
631     , m_Call(nullptr)
632     , m_InlineContext(nullptr)
633     , m_Caller(nullptr)
634     , m_Callee(method)
635     , m_Description(description)
636     , m_Reported(false)
637 {
638     // Set the compiler instance
639     m_RootCompiler = compiler->impInlineRoot();
640
641     // Set the policy
642     const bool isPrejitRoot = true;
643     m_Policy                = InlinePolicy::GetPolicy(m_RootCompiler, isPrejitRoot);
644 }
645
646 //------------------------------------------------------------------------
647 // Report: Dump, log, and report information about an inline decision.
648 //
649 // Notes:
650 //    Called (automatically via the InlineResult dtor) when the
651 //    inliner is done evaluating a candidate.
652 //
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.
657 //
658 //    All this can be suppressed if desired by calling setReported()
659 //    before the InlineResult goes out of scope.
660
661 void InlineResult::Report()
662 {
663
664 #ifdef DEBUG
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))
668     {
669         // compiler should have revoked candidacy on the call by now
670         assert((m_Call->gtFlags & GTF_CALL_INLINE_CANDIDATE) == 0);
671
672         if (m_Call->gtInlineObservation == InlineObservation::CALLEE_UNUSED_INITIAL)
673         {
674             m_Call->gtInlineObservation = m_Policy->GetObservation();
675         }
676     }
677 #endif // DEBUG
678
679     // If we weren't actually inlining, user may have suppressed
680     // reporting via setReported(). If so, do nothing.
681     if (m_Reported)
682     {
683         return;
684     }
685
686     m_Reported = true;
687
688 #ifdef DEBUG
689     const char* callee = nullptr;
690
691     // Optionally dump the result
692     if (VERBOSE || m_RootCompiler->fgPrintInlinedMethods)
693     {
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);
696
697         callee = (m_Callee == nullptr) ? "n/a" : m_RootCompiler->eeGetMethodFullName(m_Callee);
698
699         JITDUMP(format, m_Description, ResultString(), ReasonString(), caller, callee);
700     }
701 #endif // DEBUG
702
703     // Was the result NEVER? If so we might want to propagate this to
704     // the runtime.
705
706     if (IsNever() && m_Policy->PropagateNeverToRuntime())
707     {
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.
713
714         InlineObservation obs = m_Policy->GetObservation();
715
716         if ((m_Callee != nullptr) && (obs != InlineObservation::CALLEE_IS_NOINLINE))
717         {
718
719 #ifdef DEBUG
720
721             const char* obsString = InlGetObservationString(obs);
722
723             if (VERBOSE)
724             {
725                 JITDUMP("\nINLINER: Marking %s as NOINLINE because of %s\n", callee, obsString);
726             }
727             else if (m_RootCompiler->fgPrintInlinedMethods)
728             {
729                 printf("Marking %s as NOINLINE because of %s\n", callee, obsString);
730             }
731
732 #endif // DEBUG
733
734             COMP_HANDLE comp = m_RootCompiler->info.compCompHnd;
735             comp->setMethodAttribs(m_Callee, CORINFO_FLG_BAD_INLINEE);
736         }
737     }
738
739     if (IsDecided())
740     {
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());
745     }
746 }
747
748 //------------------------------------------------------------------------
749 // InlineStrategy construtor
750 //
751 // Arguments
752 //    compiler - root compiler instance
753
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)
760     , m_CallCount(0)
761     , m_CandidateCount(0)
762     , m_AlwaysCandidateCount(0)
763     , m_ForceCandidateCount(0)
764     , m_DiscretionaryCandidateCount(0)
765     , m_UnprofitableCandidateCount(0)
766     , m_ImportCount(0)
767     , m_InlineCount(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)
779     , m_Random(nullptr)
780 #endif // defined(DEBUG) || defined(INLINE_DATA)
781
782 {
783     // Verify compiler is a root compiler instance
784     assert(m_Compiler->impInlineRoot() == m_Compiler);
785
786 #ifdef DEBUG
787
788     // Possibly modify the max inline size.
789     //
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();
793
794     // Up the max size under stress
795     if (m_Compiler->compInlineStress())
796     {
797         m_MaxInlineSize *= 10;
798     }
799
800     // But don't overdo it
801     if (m_MaxInlineSize > IMPLEMENTATION_MAX_INLINE_SIZE)
802     {
803         m_MaxInlineSize = IMPLEMENTATION_MAX_INLINE_SIZE;
804     }
805
806     // Verify: not too small, not too big.
807     assert(m_MaxInlineSize >= ALWAYS_INLINE_SIZE);
808     assert(m_MaxInlineSize <= IMPLEMENTATION_MAX_INLINE_SIZE);
809
810     // Possibly modify the max inline depth
811     //
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();
815
816     // But don't overdo it
817     if (m_MaxInlineDepth > IMPLEMENTATION_MAX_INLINE_DEPTH)
818     {
819         m_MaxInlineDepth = IMPLEMENTATION_MAX_INLINE_DEPTH;
820     }
821
822 #endif // DEBUG
823 }
824
825 //------------------------------------------------------------------------
826 // GetRootContext: get the InlineContext for the root method
827 //
828 // Return Value:
829 //    Root context; describes the method being jitted.
830 //
831 // Note:
832 //    Also initializes the jit time estimate and budget.
833
834 InlineContext* InlineStrategy::GetRootContext()
835 {
836     if (m_RootContext == nullptr)
837     {
838         // Allocate on first demand.
839         m_RootContext = NewRoot();
840
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;
845
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;
851
852         // Estimate the code size  if there's no inlining
853         m_InitialSizeEstimate = EstimateSize(m_RootContext);
854         m_CurrentSizeEstimate = m_InitialSizeEstimate;
855
856         // Sanity check
857         assert(m_CurrentTimeEstimate > 0);
858         assert(m_CurrentSizeEstimate > 0);
859
860         // Cache as the "last" context created
861         m_LastContext = m_RootContext;
862     }
863
864     return m_RootContext;
865 }
866
867 //------------------------------------------------------------------------
868 // NoteAttempt: do bookkeeping for an inline attempt
869 //
870 // Arguments:
871 //    result -- InlineResult for successful inline candidate
872
873 void InlineStrategy::NoteAttempt(InlineResult* result)
874 {
875     assert(result->IsCandidate());
876     InlineObservation obs = result->GetObservation();
877
878     if (obs == InlineObservation::CALLEE_BELOW_ALWAYS_INLINE_SIZE)
879     {
880         m_AlwaysCandidateCount++;
881     }
882     else if (obs == InlineObservation::CALLEE_IS_FORCE_INLINE)
883     {
884         m_ForceCandidateCount++;
885     }
886     else
887     {
888         m_DiscretionaryCandidateCount++;
889     }
890 }
891
892 //------------------------------------------------------------------------
893 // DumpCsvHeader: dump header for csv inline stats
894 //
895 // Argument:
896 //     fp -- file for dump output
897
898 void InlineStrategy::DumpCsvHeader(FILE* fp)
899 {
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\",");
910 }
911
912 //------------------------------------------------------------------------
913 // DumpCsvData: dump data for csv inline stats
914 //
915 // Argument:
916 //     fp -- file for dump output
917
918 void InlineStrategy::DumpCsvData(FILE* fp)
919 {
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);
926
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.
930     //
931     // So they are "cheaper" that late failures.
932
933     unsigned profitableCandidateCount = m_DiscretionaryCandidateCount - m_UnprofitableCandidateCount;
934
935     unsigned earlyFailCount =
936         m_CandidateCount - m_AlwaysCandidateCount - m_ForceCandidateCount - profitableCandidateCount;
937
938     fprintf(fp, "%u,", earlyFailCount);
939
940     unsigned lateFailCount = m_ImportCount - m_InlineCount;
941
942     fprintf(fp, "%u,", m_ImportCount);
943     fprintf(fp, "%u,", lateFailCount);
944     fprintf(fp, "%u,", m_InlineCount);
945 }
946
947 //------------------------------------------------------------------------
948 // EstimateTime: estimate impact of this inline on the method jit time
949 //
950 // Arguments:
951 //     context - context describing this inline
952 //
953 // Return Value:
954 //    Nominal estimate of jit time.
955
956 int InlineStrategy::EstimateTime(InlineContext* context)
957 {
958     // Simple linear models based on observations
959     // show time is fairly well predicted by IL size.
960     //
961     // Prediction varies for root and inlines.
962     if (context == m_RootContext)
963     {
964         return EstimateRootTime(context->GetILSize());
965     }
966     else
967     {
968         // Use amount of IL actually imported
969         return EstimateInlineTime(context->GetImportedILSize());
970     }
971 }
972
973 //------------------------------------------------------------------------
974 // EstimteRootTime: estimate jit time for method of this size with
975 // no inlining.
976 //
977 // Arguments:
978 //    ilSize - size of the method's IL
979 //
980 // Return Value:
981 //    Nominal estimate of jit time.
982 //
983 // Notes:
984 //    Based on observational data. Time is nominally microseconds.
985
986 int InlineStrategy::EstimateRootTime(unsigned ilSize)
987 {
988     return 60 + 3 * ilSize;
989 }
990
991 //------------------------------------------------------------------------
992 // EstimteInlineTime: estimate time impact on jitting for an inline
993 // of this size.
994 //
995 // Arguments:
996 //    ilSize - size of the method's IL
997 //
998 // Return Value:
999 //    Nominal increase in jit time.
1000 //
1001 // Notes:
1002 //    Based on observational data. Time is nominally microseconds.
1003 //    Small inlines will make the jit a bit faster.
1004
1005 int InlineStrategy::EstimateInlineTime(unsigned ilSize)
1006 {
1007     return -14 + 2 * ilSize;
1008 }
1009
1010 //------------------------------------------------------------------------
1011 // EstimateSize: estimate impact of this inline on the method size
1012 //
1013 // Arguments:
1014 //     context - context describing this inline
1015 //
1016 // Return Value:
1017 //    Nominal estimate of method size (bytes * 10)
1018
1019 int InlineStrategy::EstimateSize(InlineContext* context)
1020 {
1021     // Prediction varies for root and inlines.
1022     if (context == m_RootContext)
1023     {
1024         // Simple linear models based on observations show root method
1025         // native code size is fairly well predicted by IL size.
1026         //
1027         // Model below is for x64 on windows.
1028         unsigned ilSize   = context->GetILSize();
1029         int      estimate = (1312 + 228 * ilSize) / 10;
1030
1031         return estimate;
1032     }
1033     else
1034     {
1035         // Use context's code size estimate.
1036         return context->GetCodeSizeEstimate();
1037     }
1038 }
1039
1040 //------------------------------------------------------------------------
1041 // NoteOutcome: do bookkeeping for an inline
1042 //
1043 // Arguments:
1044 //    context - context for the inlie
1045
1046 void InlineStrategy::NoteOutcome(InlineContext* context)
1047 {
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())
1052     {
1053         m_InlineCount++;
1054
1055 #if defined(DEBUG) || defined(INLINE_DATA)
1056
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();
1060
1061         if (updateLast)
1062         {
1063             m_LastContext          = context;
1064             m_LastSuccessfulPolicy = context->m_Policy;
1065         }
1066         else
1067         {
1068             // We only expect one inline to be a data collection
1069             // target.
1070             assert(!context->m_Policy->IsDataCollectionTarget());
1071         }
1072
1073 #endif // defined(DEBUG) || defined(INLINE_DATA)
1074
1075         // Budget update.
1076         //
1077         // If callee is a force inline, increase budget, provided all
1078         // parent contexts are likewise force inlines.
1079         //
1080         // If callee is discretionary or has a discretionary ancestor,
1081         // increase expense.
1082
1083         InlineContext* currentContext = context;
1084         bool           isForceInline  = false;
1085
1086         while (currentContext != m_RootContext)
1087         {
1088             InlineObservation observation = currentContext->GetObservation();
1089
1090             if (observation != InlineObservation::CALLEE_IS_FORCE_INLINE)
1091             {
1092                 if (isForceInline)
1093                 {
1094                     // Interesting case where discretionary inlines pull
1095                     // in a force inline...
1096                     m_HasForceViaDiscretionary = true;
1097                 }
1098
1099                 isForceInline = false;
1100                 break;
1101             }
1102
1103             isForceInline  = true;
1104             currentContext = currentContext->GetParent();
1105         }
1106
1107         int timeDelta = EstimateTime(context);
1108
1109         if (isForceInline)
1110         {
1111             // Update budget since this inline was forced.  Only allow
1112             // budget to increase.
1113             if (timeDelta > 0)
1114             {
1115                 m_CurrentTimeBudget += timeDelta;
1116             }
1117         }
1118
1119         // Update time estimate.
1120         m_CurrentTimeEstimate += timeDelta;
1121
1122         // Update size estimate.
1123         //
1124         // Sometimes estimates don't make sense. Don't let the method
1125         // size go negative.
1126         int sizeDelta = EstimateSize(context);
1127
1128         if (m_CurrentSizeEstimate + sizeDelta <= 0)
1129         {
1130             sizeDelta = 0;
1131         }
1132
1133         // Update the code size estimate.
1134         m_CurrentSizeEstimate += sizeDelta;
1135     }
1136 }
1137
1138 //------------------------------------------------------------------------
1139 // BudgetCheck: return true if an inline of this size would likely
1140 //     exceed the jit time budget for this method
1141 //
1142 // Arguments:
1143 //     ilSize - size of the method's IL
1144 //
1145 // Return Value:
1146 //     true if the inline would go over budget
1147 //
1148 // Notes:
1149 //     Presumes all IL in the method will be imported.
1150
1151 bool InlineStrategy::BudgetCheck(unsigned ilSize)
1152 {
1153     const int  timeDelta = EstimateInlineTime(ilSize);
1154     const bool result    = (timeDelta + m_CurrentTimeEstimate > m_CurrentTimeBudget);
1155
1156     if (result)
1157     {
1158         JITDUMP("\nBudgetCheck: for IL Size %d, timeDelta %d +  currentEstimate %d > currentBudget %d\n", ilSize,
1159                 timeDelta, m_CurrentTimeEstimate, m_CurrentTimeBudget);
1160     }
1161
1162     return result;
1163 }
1164
1165 //------------------------------------------------------------------------
1166 // NewRoot: construct an InlineContext for the root method
1167 //
1168 // Return Value:
1169 //    InlineContext for use as the root context
1170 //
1171 // Notes:
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.
1175
1176 InlineContext* InlineStrategy::NewRoot()
1177 {
1178     InlineContext* rootContext = new (m_Compiler, CMK_Inlining) InlineContext(this);
1179
1180     rootContext->m_ILSize = m_Compiler->info.compILCodeSize;
1181     rootContext->m_Code   = m_Compiler->info.compCode;
1182
1183 #if defined(DEBUG) || defined(INLINE_DATA)
1184
1185     rootContext->m_Callee = m_Compiler->info.compMethodHnd;
1186
1187 #endif // defined(DEBUG) || defined(INLINE_DATA)
1188
1189     return rootContext;
1190 }
1191
1192 //------------------------------------------------------------------------
1193 // NewSuccess: construct an InlineContext for a successful inline
1194 // and link it into the context tree
1195 //
1196 // Arguments:
1197 //    inlineInfo - information about this inline
1198 //
1199 // Return Value:
1200 //    A new InlineContext for statements brought into the method by
1201 //    this inline.
1202
1203 InlineContext* InlineStrategy::NewSuccess(InlineInfo* inlineInfo)
1204 {
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();
1211
1212     noway_assert(parentContext != nullptr);
1213
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();
1229
1230 #if defined(DEBUG) || defined(INLINE_DATA)
1231
1232     InlinePolicy* policy = inlineInfo->inlineResult->GetPolicy();
1233
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;
1241
1242 #endif // defined(DEBUG) || defined(INLINE_DATA)
1243
1244 #if defined(DEBUG)
1245
1246     calleeContext->m_TreeID = originalCall->gtTreeID;
1247
1248 #endif // defined(DEBUG)
1249
1250     NoteOutcome(calleeContext);
1251
1252     return calleeContext;
1253 }
1254
1255 #if defined(DEBUG) || defined(INLINE_DATA)
1256
1257 //------------------------------------------------------------------------
1258 // NewFailure: construct an InlineContext for a failing inline
1259 // and link it into the context tree
1260 //
1261 // Arguments:
1262 //    stmt         - statement containing the attempted inline
1263 //    inlineResult - inlineResult for the attempt
1264 //
1265 // Return Value:
1266 //    A new InlineContext for diagnostic purposes
1267
1268 InlineContext* InlineStrategy::NewFailure(Statement* stmt, InlineResult* inlineResult)
1269 {
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();
1276
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
1279     // dumper.
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();
1291
1292     assert(InlIsValidObservation(failedContext->m_Observation));
1293
1294 #if defined(DEBUG) || defined(INLINE_DATA)
1295
1296     // Update offset with more accurate info
1297     failedContext->m_Offset = originalCall->gtRawILOffset;
1298
1299 #endif // #if defined(DEBUG) || defined(INLINE_DATA)
1300
1301 #if defined(DEBUG)
1302
1303     failedContext->m_TreeID = originalCall->gtTreeID;
1304
1305 #endif // defined(DEBUG)
1306
1307     NoteOutcome(failedContext);
1308
1309     return failedContext;
1310 }
1311
1312 //------------------------------------------------------------------------
1313 // Dump: dump description of inline behavior
1314 //
1315 // Arguments:
1316 //   showBudget - also dump final budget values
1317
1318 void InlineStrategy::Dump(bool showBudget)
1319 {
1320     m_RootContext->Dump();
1321
1322     if (!showBudget)
1323     {
1324         return;
1325     }
1326
1327     printf("Budget: initialTime=%d, finalTime=%d, initialBudget=%d, currentBudget=%d\n", m_InitialTimeEstimate,
1328            m_CurrentTimeEstimate, m_InitialTimeBudget, m_CurrentTimeBudget);
1329
1330     if (m_CurrentTimeBudget > m_InitialTimeBudget)
1331     {
1332         printf("Budget: increased by %d because of force inlines\n", m_CurrentTimeBudget - m_InitialTimeBudget);
1333     }
1334
1335     if (m_CurrentTimeEstimate > m_CurrentTimeBudget)
1336     {
1337         printf("Budget: went over budget by %d\n", m_CurrentTimeEstimate - m_CurrentTimeBudget);
1338     }
1339
1340     if (m_HasForceViaDiscretionary)
1341     {
1342         printf("Budget: discretionary inline caused a force inline\n");
1343     }
1344
1345     printf("Budget: initialSize=%d, finalSize=%d\n", m_InitialSizeEstimate, m_CurrentSizeEstimate);
1346 }
1347
1348 // Static to track emission of the inline data header
1349
1350 bool InlineStrategy::s_HasDumpedDataHeader = false;
1351
1352 //------------------------------------------------------------------------
1353 // DumpData: dump data about the last successful inline into this method
1354 // in a format suitable for automated analysis.
1355
1356 void InlineStrategy::DumpData()
1357 {
1358     // Is dumping enabled? If not, nothing to do.
1359     if (JitConfig.JitInlineDumpData() == 0)
1360     {
1361         return;
1362     }
1363
1364     // If we're also dumping inline XML, we'll let it dump the data.
1365     if (JitConfig.JitInlineDumpXml() != 0)
1366     {
1367         return;
1368     }
1369
1370     // Don't dump anything if limiting is on and we didn't reach
1371     // the limit while inlining.
1372     //
1373     // This serves to filter out duplicate data.
1374     const int limit = JitConfig.JitInlineLimit();
1375
1376     if ((limit >= 0) && (m_InlineCount < static_cast<unsigned>(limit)))
1377     {
1378         return;
1379     }
1380
1381     // Dump header, if not already dumped
1382     if (!s_HasDumpedDataHeader)
1383     {
1384         DumpDataHeader(stderr);
1385         s_HasDumpedDataHeader = true;
1386     }
1387
1388     // Dump contents
1389     DumpDataContents(stderr);
1390     fprintf(stderr, "\n");
1391 }
1392
1393 //------------------------------------------------------------------------
1394 // DumpDataEnsurePolicyIsSet: ensure m_LastSuccessfulPolicy describes the
1395 //    inline policy in effect.
1396 //
1397 // Notes:
1398 //    Needed for methods that don't have any successful inlines.
1399
1400 void InlineStrategy::DumpDataEnsurePolicyIsSet()
1401 {
1402     // Cache references to compiler substructures.
1403     const Compiler::Info&    info = m_Compiler->info;
1404     const Compiler::Options& opts = m_Compiler->opts;
1405
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)
1409     {
1410         const bool isPrejitRoot = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT);
1411         m_LastSuccessfulPolicy  = InlinePolicy::GetPolicy(m_Compiler, isPrejitRoot);
1412
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);
1417     }
1418 }
1419
1420 //------------------------------------------------------------------------
1421 // DumpDataHeader: dump header for inline data.
1422 //
1423 // Arguments:
1424 //    file - file for data output
1425
1426 void InlineStrategy::DumpDataHeader(FILE* file)
1427 {
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");
1433 }
1434
1435 //------------------------------------------------------------------------
1436 // DumpSchema: dump schema for inline data.
1437 //
1438 // Arguments:
1439 //    file - file for data output
1440
1441 void InlineStrategy::DumpDataSchema(FILE* file)
1442 {
1443     DumpDataEnsurePolicyIsSet();
1444     fprintf(file, "Method,Version,HotSize,ColdSize,JitTime,SizeEstimate,TimeEstimate,");
1445     m_LastSuccessfulPolicy->DumpSchema(file);
1446 }
1447
1448 //------------------------------------------------------------------------
1449 // DumpDataContents: dump contents of inline data
1450 //
1451 // Arguments:
1452 //    file - file for data output
1453
1454 void InlineStrategy::DumpDataContents(FILE* file)
1455 {
1456     DumpDataEnsurePolicyIsSet();
1457
1458     // Cache references to compiler substructures.
1459     const Compiler::Info&    info = m_Compiler->info;
1460     const Compiler::Options& opts = m_Compiler->opts;
1461
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.
1465     //
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);
1469
1470     // Convert time spent jitting into microseconds
1471     unsigned         microsecondsSpentJitting = 0;
1472     unsigned __int64 compCycles               = m_Compiler->getInlineCycleCount();
1473     if (compCycles > 0)
1474     {
1475         double countsPerSec      = CachedCyclesPerSecond();
1476         double counts            = (double)compCycles;
1477         microsecondsSpentJitting = (unsigned)((counts / countsPerSec) * 1000 * 1000);
1478     }
1479
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);
1483 }
1484
1485 // Static to track emission of the xml data header
1486 // and lock to prevent interleaved file writes
1487
1488 bool          InlineStrategy::s_HasDumpedXmlHeader = false;
1489 CritSecObject InlineStrategy::s_XmlWriterLock;
1490
1491 //------------------------------------------------------------------------
1492 // DumpXml: dump xml-formatted version of the inline tree.
1493 //
1494 // Arguments
1495 //    file - file for data output
1496 //    indent - indent level of this element
1497
1498 void InlineStrategy::DumpXml(FILE* file, unsigned indent)
1499 {
1500     if (JitConfig.JitInlineDumpXml() == 0)
1501     {
1502         return;
1503     }
1504
1505     // Lock to prevent interleaving of trees.
1506     CritSecHolder writeLock(s_XmlWriterLock);
1507
1508     // Dump header
1509     if (!s_HasDumpedXmlHeader)
1510     {
1511         DumpDataEnsurePolicyIsSet();
1512
1513         fprintf(file, "<?xml version=\"1.0\"?>\n");
1514         fprintf(file, "<InlineForest>\n");
1515         fprintf(file, "<Policy>%s</Policy>\n", m_LastSuccessfulPolicy->GetName());
1516
1517         const int dumpDataSetting = JitConfig.JitInlineDumpData();
1518         if (dumpDataSetting != 0)
1519         {
1520             fprintf(file, "<DataSchema>");
1521
1522             if (dumpDataSetting == 1)
1523             {
1524                 // JitInlineDumpData=1 -- dump schema for data plus deltas
1525                 DumpDataSchema(file);
1526             }
1527             else if (dumpDataSetting == 2)
1528             {
1529                 // JitInlineDumpData=2 -- dump schema for data only
1530                 m_LastSuccessfulPolicy->DumpSchema(file);
1531             }
1532
1533             fprintf(file, "</DataSchema>\n");
1534         }
1535
1536         fprintf(file, "<Methods>\n");
1537         s_HasDumpedXmlHeader = true;
1538     }
1539
1540     // If we're dumping "minimal" Xml, and we didn't do
1541     // any inlines into this method, then there's nothing
1542     // to emit here.
1543     if ((m_InlineCount == 0) && (JitConfig.JitInlineDumpXml() >= 2))
1544     {
1545         return;
1546     }
1547
1548     // Cache references to compiler substructures.
1549     const Compiler::Info&    info = m_Compiler->info;
1550     const Compiler::Options& opts = m_Compiler->opts;
1551
1552     const bool isPrejitRoot  = opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT);
1553     const bool isForceInline = (info.compFlags & CORINFO_FLG_FORCEINLINE) != 0;
1554
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.
1558     //
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);
1562
1563     unsigned hash = info.compMethodHash();
1564
1565     // Convert time spent jitting into microseconds
1566     unsigned         microsecondsSpentJitting = 0;
1567     unsigned __int64 compCycles               = m_Compiler->getInlineCycleCount();
1568     if (compCycles > 0)
1569     {
1570         double countsPerSec      = CachedCyclesPerSecond();
1571         double counts            = (double)compCycles;
1572         microsecondsSpentJitting = (unsigned)((counts / countsPerSec) * 1000 * 1000);
1573     }
1574
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);
1578
1579     // Cheap xml quoting for values. Only < and & are troublemakers,
1580     // but change > for symmetry.
1581     //
1582     // Ok to truncate name, just ensure it's null terminated.
1583     char buf[64];
1584     strncpy(buf, methodName, sizeof(buf));
1585     buf[sizeof(buf) - 1] = 0;
1586
1587     for (size_t i = 0; i < _countof(buf); i++)
1588     {
1589         switch (buf[i])
1590         {
1591             case '<':
1592                 buf[i] = '[';
1593                 break;
1594             case '>':
1595                 buf[i] = ']';
1596                 break;
1597             case '&':
1598                 buf[i] = '#';
1599                 break;
1600             default:
1601                 break;
1602         }
1603     }
1604
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);
1615
1616     // For prejit roots also propagate out the assessment of the root method
1617     if (isPrejitRoot)
1618     {
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));
1623     }
1624
1625     // Root context will be null if we're not optimizing the method.
1626     //
1627     // Note there are cases of this in System.Private.CoreLib even in release builds,
1628     // eg Task.NotifyDebuggerOfWaitCompletion.
1629     //
1630     // For such methods there aren't any inlines.
1631     if (m_RootContext != nullptr)
1632     {
1633         m_RootContext->DumpXml(file, indent + 2);
1634     }
1635     else
1636     {
1637         fprintf(file, "%*s<Inlines/>\n", indent + 2, "");
1638     }
1639
1640     fprintf(file, "%*s</Method>\n", indent, "");
1641 }
1642
1643 //------------------------------------------------------------------------
1644 // FinalizeXml: finalize the xml-formatted version of the inline tree.
1645 //
1646 // Arguments
1647 //    file - file for data output
1648
1649 void InlineStrategy::FinalizeXml(FILE* file)
1650 {
1651     // If we dumped the header, dump a footer
1652     if (s_HasDumpedXmlHeader)
1653     {
1654         fprintf(file, "</Methods>\n");
1655         fprintf(file, "</InlineForest>\n");
1656         fflush(file);
1657
1658         // Workaroud compShutdown getting called twice.
1659         s_HasDumpedXmlHeader = false;
1660     }
1661
1662     // Finalize reading inline xml
1663     ReplayPolicy::FinalizeXml();
1664 }
1665
1666 //------------------------------------------------------------------------
1667 // GetRandom: setup or access random state
1668 //
1669 // Return Value:
1670 //    New or pre-existing random state.
1671 //
1672 // Notes:
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.
1676
1677 CLRRandom* InlineStrategy::GetRandom()
1678 {
1679     if (m_Random == nullptr)
1680     {
1681         int externalSeed = 0;
1682
1683 #ifdef DEBUG
1684
1685         if (m_Compiler->compRandomInlineStress())
1686         {
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)
1691             {
1692                 externalSeed = 2;
1693             }
1694         }
1695
1696 #endif // DEBUG
1697
1698         int randomPolicyFlag = JitConfig.JitInlinePolicyRandom();
1699         if (randomPolicyFlag != 0)
1700         {
1701             externalSeed = randomPolicyFlag;
1702         }
1703
1704         int internalSeed = m_Compiler->info.compMethodHash();
1705
1706         assert(externalSeed != 0);
1707         assert(internalSeed != 0);
1708
1709         int seed = externalSeed ^ internalSeed;
1710
1711         m_Random = new (m_Compiler, CMK_Inlining) CLRRandom();
1712         m_Random->Init(seed);
1713     }
1714
1715     return m_Random;
1716 }
1717
1718 #endif // defined(DEBUG) || defined(INLINE_DATA)
1719
1720 //------------------------------------------------------------------------
1721 // IsInliningDisabled: allow strategy to disable inlining in the method being jitted
1722 //
1723 // Notes:
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.
1727
1728 bool InlineStrategy::IsInliningDisabled()
1729 {
1730
1731 #if defined(DEBUG) || defined(INLINE_DATA)
1732
1733     static ConfigMethodRange range;
1734     const WCHAR*             noInlineRange = JitConfig.JitNoInlineRange();
1735
1736     if (noInlineRange == nullptr)
1737     {
1738         return false;
1739     }
1740
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++)
1746     {
1747         if (*p == L' ')
1748         {
1749             entryCount++;
1750         }
1751     }
1752
1753     range.EnsureInit(noInlineRange, 2 * entryCount);
1754     assert(!range.Error());
1755
1756     return range.Contains(m_Compiler->info.compMethodHash());
1757
1758 #else
1759
1760     return false;
1761
1762 #endif // defined(DEBUG) || defined(INLINE_DATA)
1763 }