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 | |
80 | const unsigned int MAX_INL_ARGS = 32; // does not include obj pointer |
81 | const unsigned int MAX_INL_LCLS = 32; |
82 | |
83 | // Forward declarations |
84 | |
85 | class InlineStrategy; |
86 | |
87 | // InlineCallsiteFrequency gives a rough classification of how |
88 | // often a call site will be excuted at runtime. |
89 | |
90 | enum 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 | |
105 | enum 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 | |
116 | CorInfoInline InlGetCorInfoInlineDecision(InlineDecision d); |
117 | |
118 | // Get a string describing this InlineDecision |
119 | |
120 | const char* InlGetDecisionString(InlineDecision d); |
121 | |
122 | // True if this InlineDecsion describes a failing inline |
123 | |
124 | bool InlDecisionIsFailure(InlineDecision d); |
125 | |
126 | // True if this decision describes a successful inline |
127 | |
128 | bool InlDecisionIsSuccess(InlineDecision d); |
129 | |
130 | // True if this InlineDecision is a never inline decision |
131 | |
132 | bool InlDecisionIsNever(InlineDecision d); |
133 | |
134 | // True if this InlineDecision describes a viable candidate |
135 | |
136 | bool InlDecisionIsCandidate(InlineDecision d); |
137 | |
138 | // True if this InlineDecsion describes a decision |
139 | |
140 | bool InlDecisionIsDecided(InlineDecision d); |
141 | |
142 | // InlineTarget describes the possible targets of an inline observation. |
143 | |
144 | enum 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 | |
153 | enum 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 | |
164 | enum 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 | |
175 | bool InlIsValidObservation(InlineObservation obs); |
176 | |
177 | #endif // DEBUG |
178 | |
179 | // Get a string describing this observation |
180 | |
181 | const char* InlGetObservationString(InlineObservation obs); |
182 | |
183 | // Get a string describing the target of this observation |
184 | |
185 | const char* InlGetTargetString(InlineObservation obs); |
186 | |
187 | // Get a string describing the impact of this observation |
188 | |
189 | const char* InlGetImpactString(InlineObservation obs); |
190 | |
191 | // Get the target of this observation |
192 | |
193 | InlineTarget InlGetTarget(InlineObservation obs); |
194 | |
195 | // Get the impact of this observation |
196 | |
197 | InlineImpact InlGetImpact(InlineObservation obs); |
198 | |
199 | // InlinePolicy is an abstract base class for a family of inline |
200 | // policies. |
201 | |
202 | class InlinePolicy |
203 | { |
204 | public: |
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 | |
273 | protected: |
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 | |
286 | private: |
287 | // No copying or assignment supported |
288 | InlinePolicy(const InlinePolicy&) = delete; |
289 | InlinePolicy& operator=(const InlinePolicy&) = delete; |
290 | |
291 | protected: |
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 | |
306 | class InlineResult |
307 | { |
308 | public: |
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 | |
479 | private: |
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 | |
500 | struct 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 | |
513 | struct 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 | |
531 | struct 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 | |
552 | struct 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 | |
564 | struct 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 | |
622 | class InlineContext |
623 | { |
624 | // InlineContexts are created by InlineStrategies |
625 | friend class InlineStrategy; |
626 | |
627 | public: |
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 | |
710 | private: |
711 | InlineContext(InlineStrategy* strategy); |
712 | |
713 | private: |
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 | |
742 | class InlineStrategy |
743 | { |
744 | |
745 | public: |
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 (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 (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 | |
890 | private: |
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 ; |
917 | static bool ; |
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 | |