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// Inlining Support
6//
7// This file contains enum and class definitions and related
8// information that the jit uses to make inlining decisions.
9//
10// -- ENUMS --
11//
12// InlineCallFrequency - rough assessment of call site frequency
13// InlineDecision - overall decision made about an inline
14// InlineTarget - target of a particular observation
15// InlineImpact - impact of a particular observation
16// InlineObservation - facts observed when considering an inline
17//
18// -- CLASSES --
19//
20// InlineResult - accumulates observations, consults with policy
21// InlineCandidateInfo - basic information needed for inlining
22// InlArgInfo - information about a candidate's argument
23// InlLclVarInfo - information about a candidate's local variable
24// InlineInfo - detailed information needed for inlining
25// InlineContext - class, remembers what inlines happened
26// InlinePolicy - class, determines policy for inlining
27// InlineStrategy - class, determines overall inline strategy
28//
29// Enums are used throughout to provide various descriptions.
30//
31// There are 4 sitations where inline candidacy is evaluated. In each
32// case an InlineResult is allocated on the stack to collect
33// information about the inline candidate. Each InlineResult refers
34// to an InlinePolicy.
35//
36// 1. Importer Candidate Screen (impMarkInlineCandidate)
37//
38// Creates: InlineCandidateInfo
39//
40// During importing, the IL being imported is scanned to identify
41// inline candidates. This happens both when the root method is being
42// imported as well as when prospective inlines are being imported.
43// Candidates are marked in the IL and given an InlineCandidateInfo.
44//
45// 2. Inlining Optimization Pass -- candidates (fgInline)
46//
47// Creates / Uses: InlineContext
48// Creates: InlineInfo, InlArgInfo, InlLocalVarInfo
49//
50// During the inlining optimation pass, each candidate is further
51// analyzed. Viable candidates will eventually inspire creation of an
52// InlineInfo and a set of InlArgInfos (for call arguments) and
53// InlLocalVarInfos (for callee locals).
54//
55// The analysis will also examine InlineContexts from relevant prior
56// inlines. If the inline is successful, a new InlineContext will be
57// created to remember this inline. In DEBUG builds, failing inlines
58// also create InlineContexts.
59//
60// 3. Inlining Optimization Pass -- non-candidates (fgNoteNotInlineCandidate)
61//
62// Creates / Uses: InlineContext
63//
64// In DEBUG, the jit also searches for non-candidate calls to try
65// and get a complete picture of the set of failed inlines.
66//
67// 4. Prejit suitability screen (compCompileHelper)
68//
69// When prejitting, each method is scanned to see if it is a viable
70// inline candidate.
71
72#ifndef _INLINE_H_
73#define _INLINE_H_
74
75#include "jit.h"
76#include "gentree.h"
77
78// Implementation limits
79
80const unsigned int MAX_INL_ARGS = 32; // does not include obj pointer
81const unsigned int MAX_INL_LCLS = 32;
82
83// Forward declarations
84
85class InlineStrategy;
86
87// InlineCallsiteFrequency gives a rough classification of how
88// often a call site will be excuted at runtime.
89
90enum class InlineCallsiteFrequency
91{
92 UNUSED, // n/a
93 RARE, // once in a blue moon
94 BORING, // normal call site
95 WARM, // seen during profiling
96 LOOP, // in a loop
97 HOT // very frequent
98};
99
100// InlineDecision describes the various states the jit goes through when
101// evaluating an inline candidate. It is distinct from CorInfoInline
102// because it must capture internal states that don't get reported back
103// to the runtime.
104
105enum class InlineDecision
106{
107 UNDECIDED,
108 CANDIDATE,
109 SUCCESS,
110 FAILURE,
111 NEVER
112};
113
114// Translate a decision into a CorInfoInline for reporting back to the runtime.
115
116CorInfoInline InlGetCorInfoInlineDecision(InlineDecision d);
117
118// Get a string describing this InlineDecision
119
120const char* InlGetDecisionString(InlineDecision d);
121
122// True if this InlineDecsion describes a failing inline
123
124bool InlDecisionIsFailure(InlineDecision d);
125
126// True if this decision describes a successful inline
127
128bool InlDecisionIsSuccess(InlineDecision d);
129
130// True if this InlineDecision is a never inline decision
131
132bool InlDecisionIsNever(InlineDecision d);
133
134// True if this InlineDecision describes a viable candidate
135
136bool InlDecisionIsCandidate(InlineDecision d);
137
138// True if this InlineDecsion describes a decision
139
140bool InlDecisionIsDecided(InlineDecision d);
141
142// InlineTarget describes the possible targets of an inline observation.
143
144enum class InlineTarget
145{
146 CALLEE, // observation applies to all calls to this callee
147 CALLER, // observation applies to all calls made by this caller
148 CALLSITE // observation applies to a specific call site
149};
150
151// InlineImpact describe the possible impact of an inline observation.
152
153enum class InlineImpact
154{
155 FATAL, // inlining impossible, unsafe to evaluate further
156 FUNDAMENTAL, // inlining impossible for fundamental reasons, deeper exploration safe
157 LIMITATION, // inlining impossible because of jit limitations, deeper exploration safe
158 PERFORMANCE, // inlining inadvisable because of performance concerns
159 INFORMATION // policy-free observation to provide data for later decision making
160};
161
162// InlineObservation describes the set of possible inline observations.
163
164enum class InlineObservation
165{
166#define INLINE_OBSERVATION(name, type, description, impact, scope) scope##_##name,
167#include "inline.def"
168#undef INLINE_OBSERVATION
169};
170
171#ifdef DEBUG
172
173// Sanity check the observation value
174
175bool InlIsValidObservation(InlineObservation obs);
176
177#endif // DEBUG
178
179// Get a string describing this observation
180
181const char* InlGetObservationString(InlineObservation obs);
182
183// Get a string describing the target of this observation
184
185const char* InlGetTargetString(InlineObservation obs);
186
187// Get a string describing the impact of this observation
188
189const char* InlGetImpactString(InlineObservation obs);
190
191// Get the target of this observation
192
193InlineTarget InlGetTarget(InlineObservation obs);
194
195// Get the impact of this observation
196
197InlineImpact InlGetImpact(InlineObservation obs);
198
199// InlinePolicy is an abstract base class for a family of inline
200// policies.
201
202class InlinePolicy
203{
204public:
205 // Factory method for getting policies
206 static InlinePolicy* GetPolicy(Compiler* compiler, bool isPrejitRoot);
207
208 // Obligatory virtual dtor
209 virtual ~InlinePolicy()
210 {
211 }
212
213 // Get the current decision
214 InlineDecision GetDecision() const
215 {
216 return m_Decision;
217 }
218
219 // Get the observation responsible for the result
220 InlineObservation GetObservation() const
221 {
222 return m_Observation;
223 }
224
225 // Policy observations
226 virtual void NoteSuccess() = 0;
227 virtual void NoteBool(InlineObservation obs, bool value) = 0;
228 virtual void NoteFatal(InlineObservation obs) = 0;
229 virtual void NoteInt(InlineObservation obs, int value) = 0;
230
231 // Optional observations. Most policies ignore these.
232 virtual void NoteContext(InlineContext* context)
233 {
234 (void)context;
235 }
236 virtual void NoteOffset(IL_OFFSETX offset)
237 {
238 (void)offset;
239 }
240
241 // Policy determinations
242 virtual void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) = 0;
243
244 // Policy policies
245 virtual bool PropagateNeverToRuntime() const = 0;
246
247 // Policy estimates
248 virtual int CodeSizeEstimate() = 0;
249
250#if defined(DEBUG) || defined(INLINE_DATA)
251
252 // Record observation for prior failure
253 virtual void NotePriorFailure(InlineObservation obs) = 0;
254
255 // Name of the policy
256 virtual const char* GetName() const = 0;
257 // Detailed data value dump
258 virtual void DumpData(FILE* file) const
259 {
260 }
261 // Detailed data name dump
262 virtual void DumpSchema(FILE* file) const
263 {
264 }
265 // True if this is the inline targeted by data collection
266 bool IsDataCollectionTarget()
267 {
268 return m_IsDataCollectionTarget;
269 }
270
271#endif // defined(DEBUG) || defined(INLINE_DATA)
272
273protected:
274 InlinePolicy(bool isPrejitRoot)
275 : m_Decision(InlineDecision::UNDECIDED)
276 , m_Observation(InlineObservation::CALLEE_UNUSED_INITIAL)
277 , m_IsPrejitRoot(isPrejitRoot)
278#if defined(DEBUG) || defined(INLINE_DATA)
279 , m_IsDataCollectionTarget(false)
280#endif // defined(DEBUG) || defined(INLINE_DATA)
281
282 {
283 // empty
284 }
285
286private:
287 // No copying or assignment supported
288 InlinePolicy(const InlinePolicy&) = delete;
289 InlinePolicy& operator=(const InlinePolicy&) = delete;
290
291protected:
292 InlineDecision m_Decision;
293 InlineObservation m_Observation;
294 bool m_IsPrejitRoot;
295
296#if defined(DEBUG) || defined(INLINE_DATA)
297
298 bool m_IsDataCollectionTarget;
299
300#endif // defined(DEBUG) || defined(INLINE_DATA)
301};
302
303// InlineResult summarizes what is known about the viability of a
304// particular inline candiate.
305
306class InlineResult
307{
308public:
309 // Construct a new InlineResult to help evaluate a
310 // particular call for inlining.
311 InlineResult(Compiler* compiler, GenTreeCall* call, GenTreeStmt* stmt, const char* description);
312
313 // Construct a new InlineResult to evaluate a particular
314 // method to see if it is inlineable.
315 InlineResult(Compiler* compiler, CORINFO_METHOD_HANDLE method, const char* description);
316
317 // Has the policy determined this inline should fail?
318 bool IsFailure() const
319 {
320 return InlDecisionIsFailure(m_Policy->GetDecision());
321 }
322
323 // Has the policy determined this inline will succeed?
324 bool IsSuccess() const
325 {
326 return InlDecisionIsSuccess(m_Policy->GetDecision());
327 }
328
329 // Has the policy determined this inline will fail,
330 // and that the callee should never be inlined?
331 bool IsNever() const
332 {
333 return InlDecisionIsNever(m_Policy->GetDecision());
334 }
335
336 // Has the policy determined this inline attempt is still viable?
337 bool IsCandidate() const
338 {
339 return InlDecisionIsCandidate(m_Policy->GetDecision());
340 }
341
342 // Has the policy determined this inline attempt is still viable
343 // and is a discretionary inline?
344 bool IsDiscretionaryCandidate() const
345 {
346 bool result = InlDecisionIsCandidate(m_Policy->GetDecision()) &&
347 (m_Policy->GetObservation() == InlineObservation::CALLEE_IS_DISCRETIONARY_INLINE);
348
349 return result;
350 }
351
352 // Has the policy made a determination?
353 bool IsDecided() const
354 {
355 return InlDecisionIsDecided(m_Policy->GetDecision());
356 }
357
358 // NoteSuccess means the all the various checks have passed and
359 // the inline can happen.
360 void NoteSuccess()
361 {
362 assert(IsCandidate());
363 m_Policy->NoteSuccess();
364 }
365
366 // Make a true observation, and update internal state
367 // appropriately.
368 //
369 // Caller is expected to call isFailure after this to see whether
370 // more observation is desired.
371 void Note(InlineObservation obs)
372 {
373 m_Policy->NoteBool(obs, true);
374 }
375
376 // Make a boolean observation, and update internal state
377 // appropriately.
378 //
379 // Caller is expected to call isFailure after this to see whether
380 // more observation is desired.
381 void NoteBool(InlineObservation obs, bool value)
382 {
383 m_Policy->NoteBool(obs, value);
384 }
385
386 // Make an observation that must lead to immediate failure.
387 void NoteFatal(InlineObservation obs)
388 {
389 m_Policy->NoteFatal(obs);
390 assert(IsFailure());
391 }
392
393 // Make an observation with an int value
394 void NoteInt(InlineObservation obs, int value)
395 {
396 m_Policy->NoteInt(obs, value);
397 }
398
399#if defined(DEBUG) || defined(INLINE_DATA)
400
401 // Record observation from an earlier failure.
402 void NotePriorFailure(InlineObservation obs)
403 {
404 m_Policy->NotePriorFailure(obs);
405 assert(IsFailure());
406 }
407
408#endif // defined(DEBUG) || defined(INLINE_DATA)
409
410 // Determine if this inline is profitable
411 void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo)
412 {
413 m_Policy->DetermineProfitability(methodInfo);
414 }
415
416 // Ensure details of this inlining process are appropriately
417 // reported when the result goes out of scope.
418 ~InlineResult()
419 {
420 Report();
421 }
422
423 // The observation leading to this particular result
424 InlineObservation GetObservation() const
425 {
426 return m_Policy->GetObservation();
427 }
428
429 // The callee handle for this result
430 CORINFO_METHOD_HANDLE GetCallee() const
431 {
432 return m_Callee;
433 }
434
435 // The call being considered
436 GenTreeCall* GetCall() const
437 {
438 return m_Call;
439 }
440
441 // Result that can be reported back to the runtime
442 CorInfoInline Result() const
443 {
444 return InlGetCorInfoInlineDecision(m_Policy->GetDecision());
445 }
446
447 // String describing the decision made
448 const char* ResultString() const
449 {
450 return InlGetDecisionString(m_Policy->GetDecision());
451 }
452
453 // String describing the reason for the decision
454 const char* ReasonString() const
455 {
456 return InlGetObservationString(m_Policy->GetObservation());
457 }
458
459 // Get the policy that evaluated this result.
460 InlinePolicy* GetPolicy() const
461 {
462 return m_Policy;
463 }
464
465 // SetReported indicates that this particular result doesn't need
466 // to be reported back to the runtime, either because the runtime
467 // already knows, or we aren't actually inlining yet.
468 void SetReported()
469 {
470 m_Reported = true;
471 }
472
473 // Get the InlineContext for this inline
474 InlineContext* GetInlineContext() const
475 {
476 return m_InlineContext;
477 }
478
479private:
480 // No copying or assignment allowed.
481 InlineResult(const InlineResult&) = delete;
482 InlineResult& operator=(const InlineResult&) = delete;
483
484 // Report/log/dump decision as appropriate
485 void Report();
486
487 Compiler* m_RootCompiler;
488 InlinePolicy* m_Policy;
489 GenTreeCall* m_Call;
490 InlineContext* m_InlineContext;
491 CORINFO_METHOD_HANDLE m_Caller; // immediate caller's handle
492 CORINFO_METHOD_HANDLE m_Callee;
493 const char* m_Description;
494 bool m_Reported;
495};
496
497// GuardedDevirtualizationCandidateInfo provides information about
498// a potential target of a virtual call.
499
500struct GuardedDevirtualizationCandidateInfo
501{
502 CORINFO_CLASS_HANDLE guardedClassHandle;
503 CORINFO_METHOD_HANDLE guardedMethodHandle;
504 void* stubAddr;
505};
506
507// InlineCandidateInfo provides basic information about a particular
508// inline candidate.
509//
510// It is a superset of GuardedDevirtualizationCandidateInfo: calls
511// can start out as GDv candidates and turn into inline candidates
512
513struct InlineCandidateInfo : public GuardedDevirtualizationCandidateInfo
514{
515 CORINFO_METHOD_INFO methInfo;
516 CORINFO_METHOD_HANDLE ilCallerHandle; // the logical IL caller of this inlinee.
517 CORINFO_CLASS_HANDLE clsHandle;
518 CORINFO_CONTEXT_HANDLE exactContextHnd;
519 GenTree* retExpr;
520 DWORD dwRestrictions;
521 unsigned preexistingSpillTemp;
522 unsigned clsAttr;
523 unsigned methAttr;
524 CorInfoInitClassResult initClassResult;
525 var_types fncRetType;
526 bool exactContextNeedsRuntimeLookup;
527};
528
529// InlArgInfo describes inline candidate argument properties.
530
531struct InlArgInfo
532{
533 GenTree* argNode; // caller node for this argument
534 GenTree* argBashTmpNode; // tmp node created, if it may be replaced with actual arg
535 unsigned argTmpNum; // the argument tmp number
536 unsigned argIsUsed : 1; // is this arg used at all?
537 unsigned argIsInvariant : 1; // the argument is a constant or a local variable address
538 unsigned argIsLclVar : 1; // the argument is a local variable
539 unsigned argIsThis : 1; // the argument is the 'this' pointer
540 unsigned argHasSideEff : 1; // the argument has side effects
541 unsigned argHasGlobRef : 1; // the argument has a global ref
542 unsigned argHasCallerLocalRef : 1; // the argument value depends on an aliased caller local
543 unsigned argHasTmp : 1; // the argument will be evaluated to a temp
544 unsigned argHasLdargaOp : 1; // Is there LDARGA(s) operation on this argument?
545 unsigned argHasStargOp : 1; // Is there STARG(s) operation on this argument?
546 unsigned argIsByRefToStructLocal : 1; // Is this arg an address of a struct local or a normed struct local or a
547 // field in them?
548};
549
550// InlLclVarInfo describes inline candidate argument and local variable properties.
551
552struct InlLclVarInfo
553{
554 typeInfo lclVerTypeInfo;
555 var_types lclTypeInfo;
556 unsigned lclHasLdlocaOp : 1; // Is there LDLOCA(s) operation on this local?
557 unsigned lclHasStlocOp : 1; // Is there a STLOC on this local?
558 unsigned lclHasMultipleStlocOp : 1; // Is there more than one STLOC on this local
559 unsigned lclIsPinned : 1;
560};
561
562// InlineInfo provides detailed information about a particular inline candidate.
563
564struct InlineInfo
565{
566 Compiler* InlinerCompiler; // The Compiler instance for the caller (i.e. the inliner)
567 Compiler* InlineRoot; // The Compiler instance that is the root of the inlining tree of which the owner of "this" is
568 // a member.
569
570 CORINFO_METHOD_HANDLE fncHandle;
571 InlineCandidateInfo* inlineCandidateInfo;
572
573 InlineResult* inlineResult;
574
575 GenTree* retExpr; // The return expression of the inlined candidate.
576 CORINFO_CLASS_HANDLE retExprClassHnd;
577 bool retExprClassHndIsExact;
578
579 CORINFO_CONTEXT_HANDLE tokenLookupContextHandle; // The context handle that will be passed to
580 // impTokenLookupContextHandle in Inlinee's Compiler.
581
582 unsigned argCnt;
583 InlArgInfo inlArgInfo[MAX_INL_ARGS + 1];
584 int lclTmpNum[MAX_INL_LCLS]; // map local# -> temp# (-1 if unused)
585 InlLclVarInfo lclVarInfo[MAX_INL_LCLS + MAX_INL_ARGS + 1]; // type information from local sig
586
587 unsigned numberOfGcRefLocals; // Number of TYP_REF and TYP_BYREF locals
588
589 bool HasGcRefLocals() const
590 {
591 return numberOfGcRefLocals > 0;
592 }
593
594 bool thisDereferencedFirst;
595 unsigned typeContextArg;
596
597#ifdef FEATURE_SIMD
598 bool hasSIMDTypeArgLocalOrReturn;
599#endif // FEATURE_SIMD
600
601 GenTreeCall* iciCall; // The GT_CALL node to be inlined.
602 GenTreeStmt* iciStmt; // The statement iciCall is in.
603 BasicBlock* iciBlock; // The basic block iciStmt is in.
604};
605
606// InlineContext tracks the inline history in a method.
607//
608// Notes:
609//
610// InlineContexts form a tree with the root method as the root and
611// inlines as children. Nested inlines are represented as granchildren
612// and so on.
613//
614// Leaves in the tree represent successful inlines of leaf methods.
615// In DEBUG builds we also keep track of failed inline attempts.
616//
617// During inlining, all statements in the IR refer back to the
618// InlineContext that is responsible for those statements existing.
619// This makes it possible to detect recursion and to keep track of the
620// depth of each inline attempt.
621
622class InlineContext
623{
624 // InlineContexts are created by InlineStrategies
625 friend class InlineStrategy;
626
627public:
628#if defined(DEBUG) || defined(INLINE_DATA)
629
630 // Dump the full subtree, including failures
631 void Dump(unsigned indent = 0);
632
633 // Dump only the success subtree, with rich data
634 void DumpData(unsigned indent = 0);
635
636 // Dump full subtree in xml format
637 void DumpXml(FILE* file = stderr, unsigned indent = 0);
638
639 // Get callee handle
640 CORINFO_METHOD_HANDLE GetCallee() const
641 {
642 return m_Callee;
643 }
644
645#endif // defined(DEBUG) || defined(INLINE_DATA)
646
647 // Get the parent context for this context.
648 InlineContext* GetParent() const
649 {
650 return m_Parent;
651 }
652
653 // Get the code pointer for this context.
654 BYTE* GetCode() const
655 {
656 return m_Code;
657 }
658
659 // True if this context describes a successful inline.
660 bool IsSuccess() const
661 {
662 return m_Success;
663 }
664
665 // Get the observation that supported or disqualified this inline.
666 InlineObservation GetObservation() const
667 {
668 return m_Observation;
669 }
670
671 // Get the IL code size for this inline.
672 unsigned GetILSize() const
673 {
674 return m_ILSize;
675 }
676
677 // Get the native code size estimate for this inline.
678 unsigned GetCodeSizeEstimate() const
679 {
680 return m_CodeSizeEstimate;
681 }
682
683 // Get the offset of the call site
684 IL_OFFSETX GetOffset() const
685 {
686 return m_Offset;
687 }
688
689 // True if this is the root context
690 bool IsRoot() const
691 {
692 return m_Parent == nullptr;
693 }
694
695 bool IsDevirtualized() const
696 {
697 return m_Devirtualized;
698 }
699
700 bool IsGuarded() const
701 {
702 return m_Guarded;
703 }
704
705 bool IsUnboxed() const
706 {
707 return m_Unboxed;
708 }
709
710private:
711 InlineContext(InlineStrategy* strategy);
712
713private:
714 InlineStrategy* m_InlineStrategy; // overall strategy
715 InlineContext* m_Parent; // logical caller (parent)
716 InlineContext* m_Child; // first child
717 InlineContext* m_Sibling; // next child of the parent
718 BYTE* m_Code; // address of IL buffer for the method
719 unsigned m_ILSize; // size of IL buffer for the method
720 IL_OFFSETX m_Offset; // call site location within parent
721 InlineObservation m_Observation; // what lead to this inline
722 int m_CodeSizeEstimate; // in bytes * 10
723 bool m_Success : 1; // true if this was a successful inline
724 bool m_Devirtualized : 1; // true if this was a devirtualized call
725 bool m_Guarded : 1; // true if this was a guarded call
726 bool m_Unboxed : 1; // true if this call now invokes the unboxed entry
727
728#if defined(DEBUG) || defined(INLINE_DATA)
729
730 InlinePolicy* m_Policy; // policy that evaluated this inline
731 CORINFO_METHOD_HANDLE m_Callee; // handle to the method
732 unsigned m_TreeID; // ID of the GenTreeCall
733 unsigned m_Ordinal; // Ordinal number of this inline
734
735#endif // defined(DEBUG) || defined(INLINE_DATA)
736};
737
738// The InlineStrategy holds the per-method persistent inline state.
739// It is responsible for providing information that applies to
740// multiple inlining decisions.
741
742class InlineStrategy
743{
744
745public:
746 // Construct a new inline strategy.
747 InlineStrategy(Compiler* compiler);
748
749 // Create context for a successful inline.
750 InlineContext* NewSuccess(InlineInfo* inlineInfo);
751
752 // Create context for a failing inline.
753 InlineContext* NewFailure(GenTreeStmt* stmt, InlineResult* inlineResult);
754
755 // Compiler associated with this strategy
756 Compiler* GetCompiler() const
757 {
758 return m_Compiler;
759 }
760
761 // Root context
762 InlineContext* GetRootContext();
763
764 // Context for the last sucessful inline
765 // (or root if no inlines)
766 InlineContext* GetLastContext() const
767 {
768 return m_LastContext;
769 }
770
771 // Get IL size for maximum allowable inline
772 unsigned GetMaxInlineILSize() const
773 {
774 return m_MaxInlineSize;
775 }
776
777 // Get depth of maximum allowable inline
778 unsigned GetMaxInlineDepth() const
779 {
780 return m_MaxInlineDepth;
781 }
782
783 // Number of successful inlines into the root
784 unsigned GetInlineCount() const
785 {
786 return m_InlineCount;
787 }
788
789 // Return the current code size estimate for this method
790 int GetCurrentSizeEstimate() const
791 {
792 return m_CurrentSizeEstimate;
793 }
794
795 // Return the initial code size estimate for this method
796 int GetInitialSizeEstimate() const
797 {
798 return m_InitialSizeEstimate;
799 }
800
801 // Inform strategy that there's another call
802 void NoteCall()
803 {
804 m_CallCount++;
805 }
806
807 // Inform strategy that there's a new inline candidate.
808 void NoteCandidate()
809 {
810 m_CandidateCount++;
811 }
812
813 // Inform strategy that a candidate was assessed and determined to
814 // be unprofitable.
815 void NoteUnprofitable()
816 {
817 m_UnprofitableCandidateCount++;
818 }
819
820 // Inform strategy that a candidate has passed screening
821 // and that the jit will attempt to inline.
822 void NoteAttempt(InlineResult* result);
823
824 // Inform strategy that jit is about to import the inlinee IL.
825 void NoteImport()
826 {
827 m_ImportCount++;
828 }
829
830 // Inform strategy about the inline decision for a prejit root
831 void NotePrejitDecision(const InlineResult& r)
832 {
833 m_PrejitRootDecision = r.GetPolicy()->GetDecision();
834 m_PrejitRootObservation = r.GetPolicy()->GetObservation();
835 }
836
837 // Dump csv header for inline stats to indicated file.
838 static void DumpCsvHeader(FILE* f);
839
840 // Dump csv data for inline stats to indicated file.
841 void DumpCsvData(FILE* f);
842
843 // See if an inline of this size would fit within the current jit
844 // time budget.
845 bool BudgetCheck(unsigned ilSize);
846
847 // Check if this method is not allowing inlines.
848 static bool IsNoInline(ICorJitInfo* info, CORINFO_METHOD_HANDLE method);
849
850#if defined(DEBUG) || defined(INLINE_DATA)
851
852 // Dump textual description of inlines done so far.
853 void Dump(bool showBudget);
854
855 // Dump data-format description of inlines done so far.
856 void DumpData();
857 void DumpDataEnsurePolicyIsSet();
858 void DumpDataHeader(FILE* file);
859 void DumpDataSchema(FILE* file);
860 void DumpDataContents(FILE* file);
861
862 // Dump xml-formatted description of inlines
863 void DumpXml(FILE* file = stderr, unsigned indent = 0);
864 static void FinalizeXml(FILE* file = stderr);
865
866 // Cache for file position of this method in the inline xml
867 long GetMethodXmlFilePosition()
868 {
869 return m_MethodXmlFilePosition;
870 }
871
872 void SetMethodXmlFilePosition(long val)
873 {
874 m_MethodXmlFilePosition = val;
875 }
876
877 // Set up or access random state (for use by RandomPolicy)
878 CLRRandom* GetRandom();
879
880#endif // defined(DEBUG) || defined(INLINE_DATA)
881
882 // Some inline limit values
883 enum
884 {
885 ALWAYS_INLINE_SIZE = 16,
886 IMPLEMENTATION_MAX_INLINE_SIZE = _UI16_MAX,
887 IMPLEMENTATION_MAX_INLINE_DEPTH = 1000
888 };
889
890private:
891 // Create a context for the root method.
892 InlineContext* NewRoot();
893
894 // Accounting updates for a successful or failed inline.
895 void NoteOutcome(InlineContext* context);
896
897 // Cap on allowable increase in jit time due to inlining.
898 // Multiplicative, so BUDGET = 10 means up to 10x increase
899 // in jit time.
900 enum
901 {
902 BUDGET = 10
903 };
904
905 // Estimate the jit time change because of this inline.
906 int EstimateTime(InlineContext* context);
907
908 // EstimateTime helpers
909 int EstimateRootTime(unsigned ilSize);
910 int EstimateInlineTime(unsigned ilSize);
911
912 // Estimate native code size change because of this inline.
913 int EstimateSize(InlineContext* context);
914
915#if defined(DEBUG) || defined(INLINE_DATA)
916 static bool s_HasDumpedDataHeader;
917 static bool s_HasDumpedXmlHeader;
918 static CritSecObject s_XmlWriterLock;
919#endif // defined(DEBUG) || defined(INLINE_DATA)
920
921 Compiler* m_Compiler;
922 InlineContext* m_RootContext;
923 InlinePolicy* m_LastSuccessfulPolicy;
924 InlineContext* m_LastContext;
925 InlineDecision m_PrejitRootDecision;
926 InlineObservation m_PrejitRootObservation;
927 unsigned m_CallCount;
928 unsigned m_CandidateCount;
929 unsigned m_AlwaysCandidateCount;
930 unsigned m_ForceCandidateCount;
931 unsigned m_DiscretionaryCandidateCount;
932 unsigned m_UnprofitableCandidateCount;
933 unsigned m_ImportCount;
934 unsigned m_InlineCount;
935 unsigned m_MaxInlineSize;
936 unsigned m_MaxInlineDepth;
937 int m_InitialTimeBudget;
938 int m_InitialTimeEstimate;
939 int m_CurrentTimeBudget;
940 int m_CurrentTimeEstimate;
941 int m_InitialSizeEstimate;
942 int m_CurrentSizeEstimate;
943 bool m_HasForceViaDiscretionary;
944
945#if defined(DEBUG) || defined(INLINE_DATA)
946 long m_MethodXmlFilePosition;
947 CLRRandom* m_Random;
948#endif // defined(DEBUG) || defined(INLINE_DATA)
949};
950
951#endif // _INLINE_H_
952