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 Policies
6//
7// This file contains class definitions for various inlining
8// policies used by the jit.
9//
10// -- CLASSES --
11//
12// LegalPolicy - partial class providing common legality checks
13// DefaultPolicy - default inliner policy
14// DiscretionaryPolicy - default variant with uniform size policy
15// ModelPolicy - policy based on statistical modelling
16//
17// These experimental policies are available only in
18// DEBUG or release+INLINE_DATA builds of the jit.
19//
20// RandomPolicy - randomized inlining
21// FullPolicy - inlines everything up to size and depth limits
22// SizePolicy - tries not to increase method sizes
23//
24// The default policy in use is the DefaultPolicy.
25
26#ifndef _INLINE_POLICY_H_
27#define _INLINE_POLICY_H_
28
29#include "jit.h"
30#include "inline.h"
31
32// LegalPolicy is a partial policy that encapsulates the common
33// legality and ability checks the inliner must make.
34//
35// Generally speaking, the legal policy expects the inlining attempt
36// to fail fast when a fatal or equivalent observation is made. So
37// once an observation causes failure, no more observations are
38// expected. However for the prejit scan case (where the jit is not
39// actually inlining, but is assessing a method's general
40// inlinability) the legal policy allows multiple failing
41// observations provided they have the same impact. Only the first
42// observation that puts the policy into a failing state is
43// remembered. Transitions from failing states to candidate or success
44// states are not allowed.
45
46class LegalPolicy : public InlinePolicy
47{
48
49public:
50 // Constructor
51 LegalPolicy(bool isPrejitRoot) : InlinePolicy(isPrejitRoot)
52 {
53 // empty
54 }
55
56 // Handle an observation that must cause inlining to fail.
57 void NoteFatal(InlineObservation obs) override;
58
59#if defined(DEBUG) || defined(INLINE_DATA)
60
61 // Record observation for prior failure
62 void NotePriorFailure(InlineObservation obs) override;
63
64#endif // defined(DEBUG) || defined(INLINE_DATA)
65
66protected:
67 // Helper methods
68 void NoteInternal(InlineObservation obs);
69 void SetCandidate(InlineObservation obs);
70 void SetFailure(InlineObservation obs);
71 void SetNever(InlineObservation obs);
72};
73
74// Forward declaration for the state machine class used by the
75// DefaultPolicy
76
77class CodeSeqSM;
78
79// DefaultPolicy implements the default inlining policy for the jit.
80
81class DefaultPolicy : public LegalPolicy
82{
83public:
84 // Construct a DefaultPolicy
85 DefaultPolicy(Compiler* compiler, bool isPrejitRoot)
86 : LegalPolicy(isPrejitRoot)
87 , m_RootCompiler(compiler)
88 , m_StateMachine(nullptr)
89 , m_Multiplier(0.0)
90 , m_CodeSize(0)
91 , m_CallsiteFrequency(InlineCallsiteFrequency::UNUSED)
92 , m_InstructionCount(0)
93 , m_LoadStoreCount(0)
94 , m_ArgFeedsTest(0)
95 , m_ArgFeedsConstantTest(0)
96 , m_ArgFeedsRangeCheck(0)
97 , m_ConstantArgFeedsConstantTest(0)
98 , m_CalleeNativeSizeEstimate(0)
99 , m_CallsiteNativeSizeEstimate(0)
100 , m_IsForceInline(false)
101 , m_IsForceInlineKnown(false)
102 , m_IsInstanceCtor(false)
103 , m_IsFromPromotableValueClass(false)
104 , m_HasSimd(false)
105 , m_LooksLikeWrapperMethod(false)
106 , m_MethodIsMostlyLoadStore(false)
107 , m_CallsiteIsInTryRegion(false)
108 , m_CallsiteIsInLoop(false)
109 , m_IsNoReturn(false)
110 , m_IsNoReturnKnown(false)
111 {
112 // empty
113 }
114
115 // Policy observations
116 void NoteSuccess() override;
117 void NoteBool(InlineObservation obs, bool value) override;
118 void NoteInt(InlineObservation obs, int value) override;
119
120 // Policy determinations
121 void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) override;
122
123 // Policy policies
124 bool PropagateNeverToRuntime() const override;
125
126 // Policy estimates
127 int CodeSizeEstimate() override;
128
129#if defined(DEBUG) || defined(INLINE_DATA)
130
131 const char* GetName() const override
132 {
133 return "DefaultPolicy";
134 }
135
136#endif // (DEBUG) || defined(INLINE_DATA)
137
138protected:
139 // Constants
140 enum
141 {
142 MAX_BASIC_BLOCKS = 5,
143 SIZE_SCALE = 10
144 };
145
146 // Helper methods
147 double DetermineMultiplier();
148 int DetermineNativeSizeEstimate();
149 int DetermineCallsiteNativeSizeEstimate(CORINFO_METHOD_INFO* methodInfo);
150
151 // Data members
152 Compiler* m_RootCompiler; // root compiler instance
153 CodeSeqSM* m_StateMachine;
154 double m_Multiplier;
155 unsigned m_CodeSize;
156 InlineCallsiteFrequency m_CallsiteFrequency;
157 unsigned m_InstructionCount;
158 unsigned m_LoadStoreCount;
159 unsigned m_ArgFeedsTest;
160 unsigned m_ArgFeedsConstantTest;
161 unsigned m_ArgFeedsRangeCheck;
162 unsigned m_ConstantArgFeedsConstantTest;
163 int m_CalleeNativeSizeEstimate;
164 int m_CallsiteNativeSizeEstimate;
165 bool m_IsForceInline : 1;
166 bool m_IsForceInlineKnown : 1;
167 bool m_IsInstanceCtor : 1;
168 bool m_IsFromPromotableValueClass : 1;
169 bool m_HasSimd : 1;
170 bool m_LooksLikeWrapperMethod : 1;
171 bool m_MethodIsMostlyLoadStore : 1;
172 bool m_CallsiteIsInTryRegion : 1;
173 bool m_CallsiteIsInLoop : 1;
174 bool m_IsNoReturn : 1;
175 bool m_IsNoReturnKnown : 1;
176};
177
178// DiscretionaryPolicy is a variant of the default policy. It
179// differs in that there is no ALWAYS_INLINE class, there is no IL
180// size limit, and in prejit mode, discretionary failures do not
181// propagate the "NEVER" inline bit to the runtime.
182//
183// It is useful for gathering data about inline costs.
184
185class DiscretionaryPolicy : public DefaultPolicy
186{
187public:
188 // Construct a DiscretionaryPolicy
189 DiscretionaryPolicy(Compiler* compiler, bool isPrejitRoot);
190
191 // Policy observations
192 void NoteBool(InlineObservation obs, bool value) override;
193 void NoteInt(InlineObservation obs, int value) override;
194
195 // Policy policies
196 bool PropagateNeverToRuntime() const override;
197
198 // Policy determinations
199 void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) override;
200
201 // Policy estimates
202 int CodeSizeEstimate() override;
203
204#if defined(DEBUG) || defined(INLINE_DATA)
205
206 // Externalize data
207 void DumpData(FILE* file) const override;
208 void DumpSchema(FILE* file) const override;
209
210 // Miscellaneous
211 const char* GetName() const override
212 {
213 return "DiscretionaryPolicy";
214 }
215
216#endif // defined(DEBUG) || defined(INLINE_DATA)
217
218protected:
219 void ComputeOpcodeBin(OPCODE opcode);
220 void EstimateCodeSize();
221 void EstimatePerformanceImpact();
222 void MethodInfoObservations(CORINFO_METHOD_INFO* methodInfo);
223 enum
224 {
225 MAX_ARGS = 6
226 };
227
228 unsigned m_Depth;
229 unsigned m_BlockCount;
230 unsigned m_Maxstack;
231 unsigned m_ArgCount;
232 CorInfoType m_ArgType[MAX_ARGS];
233 size_t m_ArgSize[MAX_ARGS];
234 unsigned m_LocalCount;
235 CorInfoType m_ReturnType;
236 size_t m_ReturnSize;
237 unsigned m_ArgAccessCount;
238 unsigned m_LocalAccessCount;
239 unsigned m_IntConstantCount;
240 unsigned m_FloatConstantCount;
241 unsigned m_IntLoadCount;
242 unsigned m_FloatLoadCount;
243 unsigned m_IntStoreCount;
244 unsigned m_FloatStoreCount;
245 unsigned m_SimpleMathCount;
246 unsigned m_ComplexMathCount;
247 unsigned m_OverflowMathCount;
248 unsigned m_IntArrayLoadCount;
249 unsigned m_FloatArrayLoadCount;
250 unsigned m_RefArrayLoadCount;
251 unsigned m_StructArrayLoadCount;
252 unsigned m_IntArrayStoreCount;
253 unsigned m_FloatArrayStoreCount;
254 unsigned m_RefArrayStoreCount;
255 unsigned m_StructArrayStoreCount;
256 unsigned m_StructOperationCount;
257 unsigned m_ObjectModelCount;
258 unsigned m_FieldLoadCount;
259 unsigned m_FieldStoreCount;
260 unsigned m_StaticFieldLoadCount;
261 unsigned m_StaticFieldStoreCount;
262 unsigned m_LoadAddressCount;
263 unsigned m_ThrowCount;
264 unsigned m_ReturnCount;
265 unsigned m_CallCount;
266 unsigned m_CallSiteWeight;
267 int m_ModelCodeSizeEstimate;
268 int m_PerCallInstructionEstimate;
269 bool m_IsClassCtor;
270 bool m_IsSameThis;
271 bool m_CallerHasNewArray;
272 bool m_CallerHasNewObj;
273 bool m_CalleeHasGCStruct;
274};
275
276// ModelPolicy is an experimental policy that uses the results
277// of data modelling to make estimates.
278
279class ModelPolicy : public DiscretionaryPolicy
280{
281public:
282 // Construct a ModelPolicy
283 ModelPolicy(Compiler* compiler, bool isPrejitRoot);
284
285 // Policy observations
286 void NoteInt(InlineObservation obs, int value) override;
287
288 // Policy determinations
289 void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) override;
290
291 // Policy policies
292 bool PropagateNeverToRuntime() const override
293 {
294 return true;
295 }
296
297#if defined(DEBUG) || defined(INLINE_DATA)
298
299 // Miscellaneous
300 const char* GetName() const override
301 {
302 return "ModelPolicy";
303 }
304
305#endif // defined(DEBUG) || defined(INLINE_DATA)
306};
307
308#if defined(DEBUG) || defined(INLINE_DATA)
309
310// RandomPolicy implements a policy that inlines at random.
311// It is mostly useful for stress testing.
312
313class RandomPolicy : public DiscretionaryPolicy
314{
315public:
316 // Construct a RandomPolicy
317 RandomPolicy(Compiler* compiler, bool isPrejitRoot);
318
319 // Policy observations
320 void NoteInt(InlineObservation obs, int value) override;
321
322 // Policy determinations
323 void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) override;
324
325 const char* GetName() const override
326 {
327 return "RandomPolicy";
328 }
329
330private:
331 // Data members
332 CLRRandom* m_Random;
333};
334
335#endif // defined(DEBUG) || defined(INLINE_DATA)
336
337#if defined(DEBUG) || defined(INLINE_DATA)
338
339// FullPolicy is an experimental policy that will always inline if
340// possible, subject to externally settable depth and size limits.
341//
342// It's useful for uncovering the full set of possible inlines for
343// methods.
344
345class FullPolicy : public DiscretionaryPolicy
346{
347public:
348 // Construct a FullPolicy
349 FullPolicy(Compiler* compiler, bool isPrejitRoot);
350
351 // Policy determinations
352 void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) override;
353
354 // Miscellaneous
355 const char* GetName() const override
356 {
357 return "FullPolicy";
358 }
359};
360
361// SizePolicy is an experimental policy that will inline as much
362// as possible without increasing the (estimated) method size.
363//
364// It may be useful down the road as a policy to use for methods
365// that are rarely executed (eg class constructors).
366
367class SizePolicy : public DiscretionaryPolicy
368{
369public:
370 // Construct a SizePolicy
371 SizePolicy(Compiler* compiler, bool isPrejitRoot);
372
373 // Policy determinations
374 void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) override;
375
376 // Miscellaneous
377 const char* GetName() const override
378 {
379 return "SizePolicy";
380 }
381};
382
383// The ReplayPolicy performs only inlines specified by an external
384// inline replay log.
385
386class ReplayPolicy : public DiscretionaryPolicy
387{
388public:
389 // Construct a ReplayPolicy
390 ReplayPolicy(Compiler* compiler, bool isPrejitRoot);
391
392 // Policy observations
393 void NoteBool(InlineObservation obs, bool value) override;
394
395 // Optional observations
396 void NoteContext(InlineContext* context) override
397 {
398 m_InlineContext = context;
399 }
400
401 void NoteOffset(IL_OFFSETX offset) override
402 {
403 m_Offset = offset;
404 }
405
406 // Policy determinations
407 void DetermineProfitability(CORINFO_METHOD_INFO* methodInfo) override;
408
409 // Miscellaneous
410 const char* GetName() const override
411 {
412 return "ReplayPolicy";
413 }
414
415 static void FinalizeXml();
416
417private:
418 bool FindMethod();
419 bool FindContext(InlineContext* context);
420 bool FindInline(CORINFO_METHOD_HANDLE callee);
421 bool FindInline(unsigned token, unsigned hash, unsigned offset);
422
423 static bool s_WroteReplayBanner;
424 static FILE* s_ReplayFile;
425 static CritSecObject s_XmlReaderLock;
426 InlineContext* m_InlineContext;
427 IL_OFFSETX m_Offset;
428 bool m_WasForceInline;
429};
430
431#endif // defined(DEBUG) || defined(INLINE_DATA)
432
433#endif // _INLINE_POLICY_H_
434