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 | |
46 | class LegalPolicy : public InlinePolicy |
47 | { |
48 | |
49 | public: |
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 | |
66 | protected: |
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 | |
77 | class CodeSeqSM; |
78 | |
79 | // DefaultPolicy implements the default inlining policy for the jit. |
80 | |
81 | class DefaultPolicy : public LegalPolicy |
82 | { |
83 | public: |
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 | |
138 | protected: |
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 | |
185 | class DiscretionaryPolicy : public DefaultPolicy |
186 | { |
187 | public: |
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 | |
218 | protected: |
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 | |
279 | class ModelPolicy : public DiscretionaryPolicy |
280 | { |
281 | public: |
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 | |
313 | class RandomPolicy : public DiscretionaryPolicy |
314 | { |
315 | public: |
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 | |
330 | private: |
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 | |
345 | class FullPolicy : public DiscretionaryPolicy |
346 | { |
347 | public: |
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 | |
367 | class SizePolicy : public DiscretionaryPolicy |
368 | { |
369 | public: |
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 | |
386 | class ReplayPolicy : public DiscretionaryPolicy |
387 | { |
388 | public: |
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 | |
417 | private: |
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 | |