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