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 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7 | XX XX |
8 | XX Compiler XX |
9 | XX XX |
10 | XX Represents the method data we are currently JIT-compiling. XX |
11 | XX An instance of this class is created for every method we JIT. XX |
12 | XX This contains all the info needed for the method. So allocating a XX |
13 | XX a new instance per method makes it thread-safe. XX |
14 | XX It should be used to do all the memory management for the compiler run. XX |
15 | XX XX |
16 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
17 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
18 | */ |
19 | |
20 | /*****************************************************************************/ |
21 | #ifndef _COMPILER_H_ |
22 | #define _COMPILER_H_ |
23 | /*****************************************************************************/ |
24 | |
25 | #include "jit.h" |
26 | #include "opcode.h" |
27 | #include "varset.h" |
28 | #include "jitstd.h" |
29 | #include "jithashtable.h" |
30 | #include "gentree.h" |
31 | #include "lir.h" |
32 | #include "block.h" |
33 | #include "inline.h" |
34 | #include "jiteh.h" |
35 | #include "instr.h" |
36 | #include "regalloc.h" |
37 | #include "sm.h" |
38 | #include "cycletimer.h" |
39 | #include "blockset.h" |
40 | #include "arraystack.h" |
41 | #include "hashbv.h" |
42 | #include "jitexpandarray.h" |
43 | #include "tinyarray.h" |
44 | #include "valuenum.h" |
45 | #include "reglist.h" |
46 | #include "jittelemetry.h" |
47 | #include "namedintrinsiclist.h" |
48 | #ifdef LATE_DISASM |
49 | #include "disasm.h" |
50 | #endif |
51 | |
52 | #include "codegeninterface.h" |
53 | #include "regset.h" |
54 | #include "jitgcinfo.h" |
55 | |
56 | #if DUMP_GC_TABLES && defined(JIT32_GCENCODER) |
57 | #include "gcdump.h" |
58 | #endif |
59 | |
60 | #include "emit.h" |
61 | |
62 | #include "hwintrinsic.h" |
63 | #include "simd.h" |
64 | |
65 | // This is only used locally in the JIT to indicate that |
66 | // a verification block should be inserted |
67 | #define SEH_VERIFICATION_EXCEPTION 0xe0564552 // VER |
68 | |
69 | /***************************************************************************** |
70 | * Forward declarations |
71 | */ |
72 | |
73 | struct InfoHdr; // defined in GCInfo.h |
74 | struct escapeMapping_t; // defined in flowgraph.cpp |
75 | class emitter; // defined in emit.h |
76 | struct ShadowParamVarInfo; // defined in GSChecks.cpp |
77 | struct InitVarDscInfo; // defined in register_arg_convention.h |
78 | class FgStack; // defined in flowgraph.cpp |
79 | #if FEATURE_ANYCSE |
80 | class CSE_DataFlow; // defined in OptCSE.cpp |
81 | #endif |
82 | #ifdef DEBUG |
83 | struct IndentStack; |
84 | #endif |
85 | |
86 | class Lowering; // defined in lower.h |
87 | |
88 | // The following are defined in this file, Compiler.h |
89 | |
90 | class Compiler; |
91 | |
92 | /***************************************************************************** |
93 | * Unwind info |
94 | */ |
95 | |
96 | #include "unwind.h" |
97 | |
98 | /*****************************************************************************/ |
99 | |
100 | // |
101 | // Declare global operator new overloads that use the compiler's arena allocator |
102 | // |
103 | |
104 | // I wanted to make the second argument optional, with default = CMK_Unknown, but that |
105 | // caused these to be ambiguous with the global placement new operators. |
106 | void* __cdecl operator new(size_t n, Compiler* context, CompMemKind cmk); |
107 | void* __cdecl operator new[](size_t n, Compiler* context, CompMemKind cmk); |
108 | void* __cdecl operator new(size_t n, void* p, const jitstd::placement_t& syntax_difference); |
109 | |
110 | // Requires the definitions of "operator new" so including "LoopCloning.h" after the definitions. |
111 | #include "loopcloning.h" |
112 | |
113 | /*****************************************************************************/ |
114 | |
115 | /* This is included here and not earlier as it needs the definition of "CSE" |
116 | * which is defined in the section above */ |
117 | |
118 | /*****************************************************************************/ |
119 | |
120 | unsigned genLog2(unsigned value); |
121 | unsigned genLog2(unsigned __int64 value); |
122 | |
123 | var_types genActualType(var_types type); |
124 | var_types genUnsignedType(var_types type); |
125 | var_types genSignedType(var_types type); |
126 | |
127 | unsigned ReinterpretHexAsDecimal(unsigned); |
128 | |
129 | /*****************************************************************************/ |
130 | |
131 | const unsigned FLG_CCTOR = (CORINFO_FLG_CONSTRUCTOR | CORINFO_FLG_STATIC); |
132 | |
133 | #ifdef DEBUG |
134 | const int BAD_STK_OFFS = 0xBAADF00D; // for LclVarDsc::lvStkOffs |
135 | #endif |
136 | |
137 | // The following holds the Local var info (scope information) |
138 | typedef const char* VarName; // Actual ASCII string |
139 | struct VarScopeDsc |
140 | { |
141 | IL_OFFSET vsdLifeBeg; // instr offset of beg of life |
142 | IL_OFFSET vsdLifeEnd; // instr offset of end of life |
143 | unsigned vsdVarNum; // (remapped) LclVarDsc number |
144 | |
145 | #ifdef DEBUG |
146 | VarName vsdName; // name of the var |
147 | #endif |
148 | |
149 | unsigned vsdLVnum; // 'which' in eeGetLVinfo(). |
150 | // Also, it is the index of this entry in the info.compVarScopes array, |
151 | // which is useful since the array is also accessed via the |
152 | // compEnterScopeList and compExitScopeList sorted arrays. |
153 | }; |
154 | |
155 | // This is the location of a SSA definition. |
156 | struct DefLoc |
157 | { |
158 | BasicBlock* m_blk; |
159 | GenTree* m_tree; |
160 | |
161 | DefLoc() : m_blk(nullptr), m_tree(nullptr) |
162 | { |
163 | } |
164 | |
165 | DefLoc(BasicBlock* block, GenTree* tree) : m_blk(block), m_tree(tree) |
166 | { |
167 | } |
168 | }; |
169 | |
170 | // This class stores information associated with a LclVar SSA definition. |
171 | class LclSsaVarDsc |
172 | { |
173 | public: |
174 | LclSsaVarDsc() |
175 | { |
176 | } |
177 | |
178 | LclSsaVarDsc(BasicBlock* block, GenTree* tree) : m_defLoc(block, tree) |
179 | { |
180 | } |
181 | |
182 | ValueNumPair m_vnPair; |
183 | DefLoc m_defLoc; |
184 | }; |
185 | |
186 | // This class stores information associated with a memory SSA definition. |
187 | class SsaMemDef |
188 | { |
189 | public: |
190 | ValueNumPair m_vnPair; |
191 | }; |
192 | |
193 | //------------------------------------------------------------------------ |
194 | // SsaDefArray: A resizable array of SSA definitions. |
195 | // |
196 | // Unlike an ordinary resizable array implementation, this allows only element |
197 | // addition (by calling AllocSsaNum) and has special handling for RESERVED_SSA_NUM |
198 | // (basically it's a 1-based array). The array doesn't impose any particular |
199 | // requirements on the elements it stores and AllocSsaNum forwards its arguments |
200 | // to the array element constructor, this way the array supports both LclSsaVarDsc |
201 | // and SsaMemDef elements. |
202 | // |
203 | template <typename T> |
204 | class SsaDefArray |
205 | { |
206 | T* m_array; |
207 | unsigned m_arraySize; |
208 | unsigned m_count; |
209 | |
210 | static_assert_no_msg(SsaConfig::RESERVED_SSA_NUM == 0); |
211 | static_assert_no_msg(SsaConfig::FIRST_SSA_NUM == 1); |
212 | |
213 | // Get the minimum valid SSA number. |
214 | unsigned GetMinSsaNum() const |
215 | { |
216 | return SsaConfig::FIRST_SSA_NUM; |
217 | } |
218 | |
219 | // Increase (double) the size of the array. |
220 | void GrowArray(CompAllocator alloc) |
221 | { |
222 | unsigned oldSize = m_arraySize; |
223 | unsigned newSize = max(2, oldSize * 2); |
224 | |
225 | T* newArray = alloc.allocate<T>(newSize); |
226 | |
227 | for (unsigned i = 0; i < oldSize; i++) |
228 | { |
229 | newArray[i] = m_array[i]; |
230 | } |
231 | |
232 | m_array = newArray; |
233 | m_arraySize = newSize; |
234 | } |
235 | |
236 | public: |
237 | // Construct an empty SsaDefArray. |
238 | SsaDefArray() : m_array(nullptr), m_arraySize(0), m_count(0) |
239 | { |
240 | } |
241 | |
242 | // Reset the array (used only if the SSA form is reconstructed). |
243 | void Reset() |
244 | { |
245 | m_count = 0; |
246 | } |
247 | |
248 | // Allocate a new SSA number (starting with SsaConfig::FIRST_SSA_NUM). |
249 | template <class... Args> |
250 | unsigned AllocSsaNum(CompAllocator alloc, Args&&... args) |
251 | { |
252 | if (m_count == m_arraySize) |
253 | { |
254 | GrowArray(alloc); |
255 | } |
256 | |
257 | unsigned ssaNum = GetMinSsaNum() + m_count; |
258 | m_array[m_count++] = T(jitstd::forward<Args>(args)...); |
259 | |
260 | // Ensure that the first SSA number we allocate is SsaConfig::FIRST_SSA_NUM |
261 | assert((ssaNum == SsaConfig::FIRST_SSA_NUM) || (m_count > 1)); |
262 | |
263 | return ssaNum; |
264 | } |
265 | |
266 | // Get the number of SSA definitions in the array. |
267 | unsigned GetCount() const |
268 | { |
269 | return m_count; |
270 | } |
271 | |
272 | // Get a pointer to the SSA definition at the specified index. |
273 | T* GetSsaDefByIndex(unsigned index) |
274 | { |
275 | assert(index < m_count); |
276 | return &m_array[index]; |
277 | } |
278 | |
279 | // Check if the specified SSA number is valid. |
280 | bool IsValidSsaNum(unsigned ssaNum) const |
281 | { |
282 | return (GetMinSsaNum() <= ssaNum) && (ssaNum < (GetMinSsaNum() + m_count)); |
283 | } |
284 | |
285 | // Get a pointer to the SSA definition associated with the specified SSA number. |
286 | T* GetSsaDef(unsigned ssaNum) |
287 | { |
288 | assert(ssaNum != SsaConfig::RESERVED_SSA_NUM); |
289 | return GetSsaDefByIndex(ssaNum - GetMinSsaNum()); |
290 | } |
291 | }; |
292 | |
293 | enum RefCountState |
294 | { |
295 | RCS_INVALID, // not valid to get/set ref counts |
296 | RCS_EARLY, // early counts for struct promotion and struct passing |
297 | RCS_NORMAL, // normal ref counts (from lvaMarkRefs onward) |
298 | }; |
299 | |
300 | class LclVarDsc |
301 | { |
302 | public: |
303 | // The constructor. Most things can just be zero'ed. |
304 | // |
305 | // Initialize the ArgRegs to REG_STK. |
306 | // Morph will update if this local is passed in a register. |
307 | LclVarDsc() |
308 | : _lvArgReg(REG_STK) |
309 | , |
310 | #if FEATURE_MULTIREG_ARGS |
311 | _lvOtherArgReg(REG_STK) |
312 | , |
313 | #endif // FEATURE_MULTIREG_ARGS |
314 | #if ASSERTION_PROP |
315 | lvRefBlks(BlockSetOps::UninitVal()) |
316 | , |
317 | #endif // ASSERTION_PROP |
318 | lvPerSsaData() |
319 | { |
320 | } |
321 | |
322 | // note this only packs because var_types is a typedef of unsigned char |
323 | var_types lvType : 5; // TYP_INT/LONG/FLOAT/DOUBLE/REF |
324 | |
325 | unsigned char lvIsParam : 1; // is this a parameter? |
326 | unsigned char lvIsRegArg : 1; // is this a register argument? |
327 | unsigned char lvFramePointerBased : 1; // 0 = off of REG_SPBASE (e.g., ESP), 1 = off of REG_FPBASE (e.g., EBP) |
328 | |
329 | unsigned char lvStructGcCount : 3; // if struct, how many GC pointer (stop counting at 7). The only use of values >1 |
330 | // is to help determine whether to use block init in the prolog. |
331 | unsigned char lvOnFrame : 1; // (part of) the variable lives on the frame |
332 | unsigned char lvRegister : 1; // assigned to live in a register? For RyuJIT backend, this is only set if the |
333 | // variable is in the same register for the entire function. |
334 | unsigned char lvTracked : 1; // is this a tracked variable? |
335 | bool lvTrackedNonStruct() |
336 | { |
337 | return lvTracked && lvType != TYP_STRUCT; |
338 | } |
339 | unsigned char lvPinned : 1; // is this a pinned variable? |
340 | |
341 | unsigned char lvMustInit : 1; // must be initialized |
342 | unsigned char lvAddrExposed : 1; // The address of this variable is "exposed" -- passed as an argument, stored in a |
343 | // global location, etc. |
344 | // We cannot reason reliably about the value of the variable. |
345 | unsigned char lvDoNotEnregister : 1; // Do not enregister this variable. |
346 | unsigned char lvFieldAccessed : 1; // The var is a struct local, and a field of the variable is accessed. Affects |
347 | // struct promotion. |
348 | |
349 | unsigned char lvInSsa : 1; // The variable is in SSA form (set by SsaBuilder) |
350 | |
351 | #ifdef DEBUG |
352 | // These further document the reasons for setting "lvDoNotEnregister". (Note that "lvAddrExposed" is one of the |
353 | // reasons; |
354 | // also, lvType == TYP_STRUCT prevents enregistration. At least one of the reasons should be true. |
355 | unsigned char lvVMNeedsStackAddr : 1; // The VM may have access to a stack-relative address of the variable, and |
356 | // read/write its value. |
357 | unsigned char lvLiveInOutOfHndlr : 1; // The variable was live in or out of an exception handler, and this required |
358 | // the variable to be |
359 | // in the stack (at least at those boundaries.) |
360 | unsigned char lvLclFieldExpr : 1; // The variable is not a struct, but was accessed like one (e.g., reading a |
361 | // particular byte from an int). |
362 | unsigned char lvLclBlockOpAddr : 1; // The variable was written to via a block operation that took its address. |
363 | unsigned char lvLiveAcrossUCall : 1; // The variable is live across an unmanaged call. |
364 | #endif |
365 | unsigned char lvIsCSE : 1; // Indicates if this LclVar is a CSE variable. |
366 | unsigned char lvHasLdAddrOp : 1; // has ldloca or ldarga opcode on this local. |
367 | unsigned char lvStackByref : 1; // This is a compiler temporary of TYP_BYREF that is known to point into our local |
368 | // stack frame. |
369 | |
370 | unsigned char lvHasILStoreOp : 1; // there is at least one STLOC or STARG on this local |
371 | unsigned char lvHasMultipleILStoreOp : 1; // there is more than one STLOC on this local |
372 | |
373 | unsigned char lvIsTemp : 1; // Short-lifetime compiler temp (if lvIsParam is false), or implicit byref parameter |
374 | // (if lvIsParam is true) |
375 | #if OPT_BOOL_OPS |
376 | unsigned char lvIsBoolean : 1; // set if variable is boolean |
377 | #endif |
378 | unsigned char lvSingleDef : 1; // variable has a single def |
379 | // before lvaMarkLocalVars: identifies ref type locals that can get type updates |
380 | // after lvaMarkLocalVars: identifies locals that are suitable for optAddCopies |
381 | |
382 | #if ASSERTION_PROP |
383 | unsigned char lvDisqualify : 1; // variable is no longer OK for add copy optimization |
384 | unsigned char lvVolatileHint : 1; // hint for AssertionProp |
385 | #endif |
386 | |
387 | #ifndef _TARGET_64BIT_ |
388 | unsigned char lvStructDoubleAlign : 1; // Must we double align this struct? |
389 | #endif // !_TARGET_64BIT_ |
390 | #ifdef _TARGET_64BIT_ |
391 | unsigned char lvQuirkToLong : 1; // Quirk to allocate this LclVar as a 64-bit long |
392 | #endif |
393 | #ifdef DEBUG |
394 | unsigned char lvKeepType : 1; // Don't change the type of this variable |
395 | unsigned char lvNoLclFldStress : 1; // Can't apply local field stress on this one |
396 | #endif |
397 | unsigned char lvIsPtr : 1; // Might this be used in an address computation? (used by buffer overflow security |
398 | // checks) |
399 | unsigned char lvIsUnsafeBuffer : 1; // Does this contain an unsafe buffer requiring buffer overflow security checks? |
400 | unsigned char lvPromoted : 1; // True when this local is a promoted struct, a normed struct, or a "split" long on a |
401 | // 32-bit target. For implicit byref parameters, this gets hijacked between |
402 | // fgRetypeImplicitByRefArgs and fgMarkDemotedImplicitByRefArgs to indicate whether |
403 | // references to the arg are being rewritten as references to a promoted shadow local. |
404 | unsigned char lvIsStructField : 1; // Is this local var a field of a promoted struct local? |
405 | unsigned char lvOverlappingFields : 1; // True when we have a struct with possibly overlapping fields |
406 | unsigned char lvContainsHoles : 1; // True when we have a promoted struct that contains holes |
407 | unsigned char lvCustomLayout : 1; // True when this struct has "CustomLayout" |
408 | |
409 | unsigned char lvIsMultiRegArg : 1; // true if this is a multireg LclVar struct used in an argument context |
410 | unsigned char lvIsMultiRegRet : 1; // true if this is a multireg LclVar struct assigned from a multireg call |
411 | |
412 | #ifdef FEATURE_HFA |
413 | unsigned char _lvIsHfa : 1; // Is this a struct variable who's class handle is an HFA type |
414 | unsigned char _lvIsHfaRegArg : 1; // Is this a HFA argument variable? // TODO-CLEANUP: Remove this and replace |
415 | // with (lvIsRegArg && lvIsHfa()) |
416 | unsigned char _lvHfaTypeIsFloat : 1; // Is the HFA type float or double? |
417 | #endif // FEATURE_HFA |
418 | |
419 | #ifdef DEBUG |
420 | // TODO-Cleanup: See the note on lvSize() - this flag is only in use by asserts that are checking for struct |
421 | // types, and is needed because of cases where TYP_STRUCT is bashed to an integral type. |
422 | // Consider cleaning this up so this workaround is not required. |
423 | unsigned char lvUnusedStruct : 1; // All references to this promoted struct are through its field locals. |
424 | // I.e. there is no longer any reference to the struct directly. |
425 | // In this case we can simply remove this struct local. |
426 | #endif |
427 | |
428 | unsigned char lvLRACandidate : 1; // Tracked for linear scan register allocation purposes |
429 | |
430 | #ifdef FEATURE_SIMD |
431 | // Note that both SIMD vector args and locals are marked as lvSIMDType = true, but the |
432 | // type of an arg node is TYP_BYREF and a local node is TYP_SIMD*. |
433 | unsigned char lvSIMDType : 1; // This is a SIMD struct |
434 | unsigned char lvUsedInSIMDIntrinsic : 1; // This tells lclvar is used for simd intrinsic |
435 | var_types lvBaseType : 5; // Note: this only packs because var_types is a typedef of unsigned char |
436 | #endif // FEATURE_SIMD |
437 | unsigned char lvRegStruct : 1; // This is a reg-sized non-field-addressed struct. |
438 | |
439 | unsigned char lvClassIsExact : 1; // lvClassHandle is the exact type |
440 | |
441 | #ifdef DEBUG |
442 | unsigned char lvClassInfoUpdated : 1; // true if this var has updated class handle or exactness |
443 | #endif |
444 | |
445 | unsigned char lvImplicitlyReferenced : 1; // true if there are non-IR references to this local (prolog, epilog, gc, |
446 | // eh) |
447 | |
448 | union { |
449 | unsigned lvFieldLclStart; // The index of the local var representing the first field in the promoted struct |
450 | // local. For implicit byref parameters, this gets hijacked between |
451 | // fgRetypeImplicitByRefArgs and fgMarkDemotedImplicitByRefArgs to point to the |
452 | // struct local created to model the parameter's struct promotion, if any. |
453 | unsigned lvParentLcl; // The index of the local var representing the parent (i.e. the promoted struct local). |
454 | // Valid on promoted struct local fields. |
455 | }; |
456 | |
457 | unsigned char lvFieldCnt; // Number of fields in the promoted VarDsc. |
458 | unsigned char lvFldOffset; |
459 | unsigned char lvFldOrdinal; |
460 | |
461 | #if FEATURE_MULTIREG_ARGS |
462 | regNumber lvRegNumForSlot(unsigned slotNum) |
463 | { |
464 | if (slotNum == 0) |
465 | { |
466 | return lvArgReg; |
467 | } |
468 | else if (slotNum == 1) |
469 | { |
470 | return lvOtherArgReg; |
471 | } |
472 | else |
473 | { |
474 | assert(false && "Invalid slotNum!" ); |
475 | } |
476 | |
477 | unreached(); |
478 | } |
479 | #endif // FEATURE_MULTIREG_ARGS |
480 | |
481 | bool lvIsHfa() const |
482 | { |
483 | #ifdef FEATURE_HFA |
484 | return _lvIsHfa; |
485 | #else |
486 | return false; |
487 | #endif |
488 | } |
489 | |
490 | void lvSetIsHfa() |
491 | { |
492 | #ifdef FEATURE_HFA |
493 | _lvIsHfa = true; |
494 | #endif |
495 | } |
496 | |
497 | bool lvIsHfaRegArg() const |
498 | { |
499 | #ifdef FEATURE_HFA |
500 | return _lvIsHfaRegArg; |
501 | #else |
502 | return false; |
503 | #endif |
504 | } |
505 | |
506 | void lvSetIsHfaRegArg(bool value = true) |
507 | { |
508 | #ifdef FEATURE_HFA |
509 | _lvIsHfaRegArg = value; |
510 | #endif |
511 | } |
512 | |
513 | bool lvHfaTypeIsFloat() const |
514 | { |
515 | #ifdef FEATURE_HFA |
516 | return _lvHfaTypeIsFloat; |
517 | #else |
518 | return false; |
519 | #endif |
520 | } |
521 | |
522 | void lvSetHfaTypeIsFloat(bool value) |
523 | { |
524 | #ifdef FEATURE_HFA |
525 | _lvHfaTypeIsFloat = value; |
526 | #endif |
527 | } |
528 | |
529 | // on Arm64 - Returns 1-4 indicating the number of register slots used by the HFA |
530 | // on Arm32 - Returns the total number of single FP register slots used by the HFA, max is 8 |
531 | // |
532 | unsigned lvHfaSlots() const |
533 | { |
534 | assert(lvIsHfa()); |
535 | assert(varTypeIsStruct(lvType)); |
536 | #ifdef _TARGET_ARM_ |
537 | return lvExactSize / sizeof(float); |
538 | #else // _TARGET_ARM64_ |
539 | if (lvHfaTypeIsFloat()) |
540 | { |
541 | return lvExactSize / sizeof(float); |
542 | } |
543 | else |
544 | { |
545 | return lvExactSize / sizeof(double); |
546 | } |
547 | #endif // _TARGET_ARM64_ |
548 | } |
549 | |
550 | // lvIsMultiRegArgOrRet() |
551 | // returns true if this is a multireg LclVar struct used in an argument context |
552 | // or if this is a multireg LclVar struct assigned from a multireg call |
553 | bool lvIsMultiRegArgOrRet() |
554 | { |
555 | return lvIsMultiRegArg || lvIsMultiRegRet; |
556 | } |
557 | |
558 | private: |
559 | regNumberSmall _lvRegNum; // Used to store the register this variable is in (or, the low register of a |
560 | // register pair). It is set during codegen any time the |
561 | // variable is enregistered (lvRegister is only set |
562 | // to non-zero if the variable gets the same register assignment for its entire |
563 | // lifetime). |
564 | #if !defined(_TARGET_64BIT_) |
565 | regNumberSmall _lvOtherReg; // Used for "upper half" of long var. |
566 | #endif // !defined(_TARGET_64BIT_) |
567 | |
568 | regNumberSmall _lvArgReg; // The register in which this argument is passed. |
569 | |
570 | #if FEATURE_MULTIREG_ARGS |
571 | regNumberSmall _lvOtherArgReg; // Used for the second part of the struct passed in a register. |
572 | // Note this is defined but not used by ARM32 |
573 | #endif // FEATURE_MULTIREG_ARGS |
574 | |
575 | regNumberSmall _lvArgInitReg; // the register into which the argument is moved at entry |
576 | |
577 | public: |
578 | // The register number is stored in a small format (8 bits), but the getters return and the setters take |
579 | // a full-size (unsigned) format, to localize the casts here. |
580 | |
581 | ///////////////////// |
582 | |
583 | __declspec(property(get = GetRegNum, put = SetRegNum)) regNumber lvRegNum; |
584 | |
585 | regNumber GetRegNum() const |
586 | { |
587 | return (regNumber)_lvRegNum; |
588 | } |
589 | |
590 | void SetRegNum(regNumber reg) |
591 | { |
592 | _lvRegNum = (regNumberSmall)reg; |
593 | assert(_lvRegNum == reg); |
594 | } |
595 | |
596 | ///////////////////// |
597 | |
598 | #if defined(_TARGET_64BIT_) |
599 | __declspec(property(get = GetOtherReg, put = SetOtherReg)) regNumber lvOtherReg; |
600 | |
601 | regNumber GetOtherReg() const |
602 | { |
603 | assert(!"shouldn't get here" ); // can't use "unreached();" because it's NORETURN, which causes C4072 |
604 | // "unreachable code" warnings |
605 | return REG_NA; |
606 | } |
607 | |
608 | void SetOtherReg(regNumber reg) |
609 | { |
610 | assert(!"shouldn't get here" ); // can't use "unreached();" because it's NORETURN, which causes C4072 |
611 | // "unreachable code" warnings |
612 | } |
613 | #else // !_TARGET_64BIT_ |
614 | __declspec(property(get = GetOtherReg, put = SetOtherReg)) regNumber lvOtherReg; |
615 | |
616 | regNumber GetOtherReg() const |
617 | { |
618 | return (regNumber)_lvOtherReg; |
619 | } |
620 | |
621 | void SetOtherReg(regNumber reg) |
622 | { |
623 | _lvOtherReg = (regNumberSmall)reg; |
624 | assert(_lvOtherReg == reg); |
625 | } |
626 | #endif // !_TARGET_64BIT_ |
627 | |
628 | ///////////////////// |
629 | |
630 | __declspec(property(get = GetArgReg, put = SetArgReg)) regNumber lvArgReg; |
631 | |
632 | regNumber GetArgReg() const |
633 | { |
634 | return (regNumber)_lvArgReg; |
635 | } |
636 | |
637 | void SetArgReg(regNumber reg) |
638 | { |
639 | _lvArgReg = (regNumberSmall)reg; |
640 | assert(_lvArgReg == reg); |
641 | } |
642 | |
643 | #if FEATURE_MULTIREG_ARGS |
644 | __declspec(property(get = GetOtherArgReg, put = SetOtherArgReg)) regNumber lvOtherArgReg; |
645 | |
646 | regNumber GetOtherArgReg() const |
647 | { |
648 | return (regNumber)_lvOtherArgReg; |
649 | } |
650 | |
651 | void SetOtherArgReg(regNumber reg) |
652 | { |
653 | _lvOtherArgReg = (regNumberSmall)reg; |
654 | assert(_lvOtherArgReg == reg); |
655 | } |
656 | #endif // FEATURE_MULTIREG_ARGS |
657 | |
658 | #ifdef FEATURE_SIMD |
659 | // Is this is a SIMD struct? |
660 | bool lvIsSIMDType() const |
661 | { |
662 | return lvSIMDType; |
663 | } |
664 | |
665 | // Is this is a SIMD struct which is used for SIMD intrinsic? |
666 | bool lvIsUsedInSIMDIntrinsic() const |
667 | { |
668 | return lvUsedInSIMDIntrinsic; |
669 | } |
670 | #else |
671 | // If feature_simd not enabled, return false |
672 | bool lvIsSIMDType() const |
673 | { |
674 | return false; |
675 | } |
676 | bool lvIsUsedInSIMDIntrinsic() const |
677 | { |
678 | return false; |
679 | } |
680 | #endif |
681 | |
682 | ///////////////////// |
683 | |
684 | __declspec(property(get = GetArgInitReg, put = SetArgInitReg)) regNumber lvArgInitReg; |
685 | |
686 | regNumber GetArgInitReg() const |
687 | { |
688 | return (regNumber)_lvArgInitReg; |
689 | } |
690 | |
691 | void SetArgInitReg(regNumber reg) |
692 | { |
693 | _lvArgInitReg = (regNumberSmall)reg; |
694 | assert(_lvArgInitReg == reg); |
695 | } |
696 | |
697 | ///////////////////// |
698 | |
699 | bool lvIsRegCandidate() const |
700 | { |
701 | return lvLRACandidate != 0; |
702 | } |
703 | |
704 | bool lvIsInReg() const |
705 | { |
706 | return lvIsRegCandidate() && (lvRegNum != REG_STK); |
707 | } |
708 | |
709 | regMaskTP lvRegMask() const |
710 | { |
711 | regMaskTP regMask = RBM_NONE; |
712 | if (varTypeIsFloating(TypeGet())) |
713 | { |
714 | if (lvRegNum != REG_STK) |
715 | { |
716 | regMask = genRegMaskFloat(lvRegNum, TypeGet()); |
717 | } |
718 | } |
719 | else |
720 | { |
721 | if (lvRegNum != REG_STK) |
722 | { |
723 | regMask = genRegMask(lvRegNum); |
724 | } |
725 | } |
726 | return regMask; |
727 | } |
728 | |
729 | unsigned short lvVarIndex; // variable tracking index |
730 | |
731 | private: |
732 | unsigned short m_lvRefCnt; // unweighted (real) reference count. For implicit by reference |
733 | // parameters, this gets hijacked from fgMarkImplicitByRefArgs |
734 | // through fgMarkDemotedImplicitByRefArgs, to provide a static |
735 | // appearance count (computed during address-exposed analysis) |
736 | // that fgMakeOutgoingStructArgCopy consults during global morph |
737 | // to determine if eliding its copy is legal. |
738 | |
739 | BasicBlock::weight_t m_lvRefCntWtd; // weighted reference count |
740 | |
741 | public: |
742 | unsigned short lvRefCnt(RefCountState state = RCS_NORMAL) const; |
743 | void incLvRefCnt(unsigned short delta, RefCountState state = RCS_NORMAL); |
744 | void setLvRefCnt(unsigned short newValue, RefCountState state = RCS_NORMAL); |
745 | |
746 | BasicBlock::weight_t lvRefCntWtd(RefCountState state = RCS_NORMAL) const; |
747 | void incLvRefCntWtd(BasicBlock::weight_t delta, RefCountState state = RCS_NORMAL); |
748 | void setLvRefCntWtd(BasicBlock::weight_t newValue, RefCountState state = RCS_NORMAL); |
749 | |
750 | int lvStkOffs; // stack offset of home |
751 | unsigned lvExactSize; // (exact) size of the type in bytes |
752 | |
753 | // Is this a promoted struct? |
754 | // This method returns true only for structs (including SIMD structs), not for |
755 | // locals that are split on a 32-bit target. |
756 | // It is only necessary to use this: |
757 | // 1) if only structs are wanted, and |
758 | // 2) if Lowering has already been done. |
759 | // Otherwise lvPromoted is valid. |
760 | bool lvPromotedStruct() |
761 | { |
762 | #if !defined(_TARGET_64BIT_) |
763 | return (lvPromoted && !varTypeIsLong(lvType)); |
764 | #else // defined(_TARGET_64BIT_) |
765 | return lvPromoted; |
766 | #endif // defined(_TARGET_64BIT_) |
767 | } |
768 | |
769 | unsigned lvSize() const // Size needed for storage representation. Only used for structs or TYP_BLK. |
770 | { |
771 | // TODO-Review: Sometimes we get called on ARM with HFA struct variables that have been promoted, |
772 | // where the struct itself is no longer used because all access is via its member fields. |
773 | // When that happens, the struct is marked as unused and its type has been changed to |
774 | // TYP_INT (to keep the GC tracking code from looking at it). |
775 | // See Compiler::raAssignVars() for details. For example: |
776 | // N002 ( 4, 3) [00EA067C] ------------- return struct $346 |
777 | // N001 ( 3, 2) [00EA0628] ------------- lclVar struct(U) V03 loc2 |
778 | // float V03.f1 (offs=0x00) -> V12 tmp7 |
779 | // f8 (last use) (last use) $345 |
780 | // Here, the "struct(U)" shows that the "V03 loc2" variable is unused. Not shown is that V03 |
781 | // is now TYP_INT in the local variable table. It's not really unused, because it's in the tree. |
782 | |
783 | assert(varTypeIsStruct(lvType) || (lvType == TYP_BLK) || (lvPromoted && lvUnusedStruct)); |
784 | |
785 | #if defined(FEATURE_SIMD) && !defined(_TARGET_64BIT_) |
786 | // For 32-bit architectures, we make local variable SIMD12 types 16 bytes instead of just 12. We can't do |
787 | // this for arguments, which must be passed according the defined ABI. We don't want to do this for |
788 | // dependently promoted struct fields, but we don't know that here. See lvaMapSimd12ToSimd16(). |
789 | // (Note that for 64-bits, we are already rounding up to 16.) |
790 | if ((lvType == TYP_SIMD12) && !lvIsParam) |
791 | { |
792 | assert(lvExactSize == 12); |
793 | return 16; |
794 | } |
795 | #endif // defined(FEATURE_SIMD) && !defined(_TARGET_64BIT_) |
796 | |
797 | return roundUp(lvExactSize, TARGET_POINTER_SIZE); |
798 | } |
799 | |
800 | size_t lvArgStackSize() const; |
801 | |
802 | unsigned lvSlotNum; // original slot # (if remapped) |
803 | |
804 | typeInfo lvVerTypeInfo; // type info needed for verification |
805 | |
806 | CORINFO_CLASS_HANDLE lvClassHnd; // class handle for the local, or null if not known |
807 | |
808 | CORINFO_FIELD_HANDLE lvFieldHnd; // field handle for promoted struct fields |
809 | |
810 | BYTE* lvGcLayout; // GC layout info for structs |
811 | |
812 | #if ASSERTION_PROP |
813 | BlockSet lvRefBlks; // Set of blocks that contain refs |
814 | GenTree* lvDefStmt; // Pointer to the statement with the single definition |
815 | void lvaDisqualifyVar(); // Call to disqualify a local variable from use in optAddCopies |
816 | #endif |
817 | var_types TypeGet() const |
818 | { |
819 | return (var_types)lvType; |
820 | } |
821 | bool lvStackAligned() const |
822 | { |
823 | assert(lvIsStructField); |
824 | return ((lvFldOffset % TARGET_POINTER_SIZE) == 0); |
825 | } |
826 | bool lvNormalizeOnLoad() const |
827 | { |
828 | return varTypeIsSmall(TypeGet()) && |
829 | // lvIsStructField is treated the same as the aliased local, see fgDoNormalizeOnStore. |
830 | (lvIsParam || lvAddrExposed || lvIsStructField); |
831 | } |
832 | |
833 | bool lvNormalizeOnStore() |
834 | { |
835 | return varTypeIsSmall(TypeGet()) && |
836 | // lvIsStructField is treated the same as the aliased local, see fgDoNormalizeOnStore. |
837 | !(lvIsParam || lvAddrExposed || lvIsStructField); |
838 | } |
839 | |
840 | void incRefCnts(BasicBlock::weight_t weight, |
841 | Compiler* pComp, |
842 | RefCountState state = RCS_NORMAL, |
843 | bool propagate = true); |
844 | bool IsFloatRegType() const |
845 | { |
846 | return isFloatRegType(lvType) || lvIsHfaRegArg(); |
847 | } |
848 | var_types GetHfaType() const |
849 | { |
850 | return lvIsHfa() ? (lvHfaTypeIsFloat() ? TYP_FLOAT : TYP_DOUBLE) : TYP_UNDEF; |
851 | } |
852 | void SetHfaType(var_types type) |
853 | { |
854 | assert(varTypeIsFloating(type)); |
855 | lvSetHfaTypeIsFloat(type == TYP_FLOAT); |
856 | } |
857 | |
858 | var_types lvaArgType(); |
859 | |
860 | SsaDefArray<LclSsaVarDsc> ; |
861 | |
862 | // Returns the address of the per-Ssa data for the given ssaNum (which is required |
863 | // not to be the SsaConfig::RESERVED_SSA_NUM, which indicates that the variable is |
864 | // not an SSA variable). |
865 | LclSsaVarDsc* (unsigned ssaNum) |
866 | { |
867 | return lvPerSsaData.GetSsaDef(ssaNum); |
868 | } |
869 | |
870 | #ifdef DEBUG |
871 | public: |
872 | const char* lvReason; |
873 | |
874 | void PrintVarReg() const |
875 | { |
876 | printf("%s" , getRegName(lvRegNum)); |
877 | } |
878 | #endif // DEBUG |
879 | |
880 | }; // class LclVarDsc |
881 | |
882 | /* |
883 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
884 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
885 | XX XX |
886 | XX TempsInfo XX |
887 | XX XX |
888 | XX The temporary lclVars allocated by the compiler for code generation XX |
889 | XX XX |
890 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
891 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
892 | */ |
893 | |
894 | /***************************************************************************** |
895 | * |
896 | * The following keeps track of temporaries allocated in the stack frame |
897 | * during code-generation (after register allocation). These spill-temps are |
898 | * only used if we run out of registers while evaluating a tree. |
899 | * |
900 | * These are different from the more common temps allocated by lvaGrabTemp(). |
901 | */ |
902 | |
903 | class TempDsc |
904 | { |
905 | public: |
906 | TempDsc* tdNext; |
907 | |
908 | private: |
909 | int tdOffs; |
910 | #ifdef DEBUG |
911 | static const int BAD_TEMP_OFFSET = 0xDDDDDDDD; // used as a sentinel "bad value" for tdOffs in DEBUG |
912 | #endif // DEBUG |
913 | |
914 | int tdNum; |
915 | BYTE tdSize; |
916 | var_types tdType; |
917 | |
918 | public: |
919 | TempDsc(int _tdNum, unsigned _tdSize, var_types _tdType) : tdNum(_tdNum), tdSize((BYTE)_tdSize), tdType(_tdType) |
920 | { |
921 | #ifdef DEBUG |
922 | assert(tdNum < |
923 | 0); // temps must have a negative number (so they have a different number from all local variables) |
924 | tdOffs = BAD_TEMP_OFFSET; |
925 | #endif // DEBUG |
926 | if (tdNum != _tdNum) |
927 | { |
928 | IMPL_LIMITATION("too many spill temps" ); |
929 | } |
930 | } |
931 | |
932 | #ifdef DEBUG |
933 | bool tdLegalOffset() const |
934 | { |
935 | return tdOffs != BAD_TEMP_OFFSET; |
936 | } |
937 | #endif // DEBUG |
938 | |
939 | int tdTempOffs() const |
940 | { |
941 | assert(tdLegalOffset()); |
942 | return tdOffs; |
943 | } |
944 | void tdSetTempOffs(int offs) |
945 | { |
946 | tdOffs = offs; |
947 | assert(tdLegalOffset()); |
948 | } |
949 | void tdAdjustTempOffs(int offs) |
950 | { |
951 | tdOffs += offs; |
952 | assert(tdLegalOffset()); |
953 | } |
954 | |
955 | int tdTempNum() const |
956 | { |
957 | assert(tdNum < 0); |
958 | return tdNum; |
959 | } |
960 | unsigned tdTempSize() const |
961 | { |
962 | return tdSize; |
963 | } |
964 | var_types tdTempType() const |
965 | { |
966 | return tdType; |
967 | } |
968 | }; |
969 | |
970 | // interface to hide linearscan implementation from rest of compiler |
971 | class LinearScanInterface |
972 | { |
973 | public: |
974 | virtual void doLinearScan() = 0; |
975 | virtual void recordVarLocationsAtStartOfBB(BasicBlock* bb) = 0; |
976 | virtual bool willEnregisterLocalVars() const = 0; |
977 | }; |
978 | |
979 | LinearScanInterface* getLinearScanAllocator(Compiler* comp); |
980 | |
981 | // Information about arrays: their element type and size, and the offset of the first element. |
982 | // We label GT_IND's that are array indices with GTF_IND_ARR_INDEX, and, for such nodes, |
983 | // associate an array info via the map retrieved by GetArrayInfoMap(). This information is used, |
984 | // for example, in value numbering of array index expressions. |
985 | struct ArrayInfo |
986 | { |
987 | var_types m_elemType; |
988 | CORINFO_CLASS_HANDLE m_elemStructType; |
989 | unsigned m_elemSize; |
990 | unsigned m_elemOffset; |
991 | |
992 | ArrayInfo() : m_elemType(TYP_UNDEF), m_elemStructType(nullptr), m_elemSize(0), m_elemOffset(0) |
993 | { |
994 | } |
995 | |
996 | ArrayInfo(var_types elemType, unsigned elemSize, unsigned elemOffset, CORINFO_CLASS_HANDLE elemStructType) |
997 | : m_elemType(elemType), m_elemStructType(elemStructType), m_elemSize(elemSize), m_elemOffset(elemOffset) |
998 | { |
999 | } |
1000 | }; |
1001 | |
1002 | // This enumeration names the phases into which we divide compilation. The phases should completely |
1003 | // partition a compilation. |
1004 | enum Phases |
1005 | { |
1006 | #define CompPhaseNameMacro(enum_nm, string_nm, short_nm, hasChildren, parent, measureIR) enum_nm, |
1007 | #include "compphases.h" |
1008 | PHASE_NUMBER_OF |
1009 | }; |
1010 | |
1011 | extern const char* PhaseNames[]; |
1012 | extern const char* PhaseEnums[]; |
1013 | extern const LPCWSTR PhaseShortNames[]; |
1014 | |
1015 | // The following enum provides a simple 1:1 mapping to CLR API's |
1016 | enum API_ICorJitInfo_Names |
1017 | { |
1018 | #define DEF_CLR_API(name) API_##name, |
1019 | #include "ICorJitInfo_API_names.h" |
1020 | API_COUNT |
1021 | }; |
1022 | |
1023 | //--------------------------------------------------------------- |
1024 | // Compilation time. |
1025 | // |
1026 | |
1027 | // A "CompTimeInfo" is a structure for tracking the compilation time of one or more methods. |
1028 | // We divide a compilation into a sequence of contiguous phases, and track the total (per-thread) cycles |
1029 | // of the compilation, as well as the cycles for each phase. We also track the number of bytecodes. |
1030 | // If there is a failure in reading a timer at any point, the "CompTimeInfo" becomes invalid, as indicated |
1031 | // by "m_timerFailure" being true. |
1032 | // If FEATURE_JIT_METHOD_PERF is not set, we define a minimal form of this, enough to let other code compile. |
1033 | struct CompTimeInfo |
1034 | { |
1035 | #ifdef FEATURE_JIT_METHOD_PERF |
1036 | // The string names of the phases. |
1037 | static const char* PhaseNames[]; |
1038 | |
1039 | static bool PhaseHasChildren[]; |
1040 | static int PhaseParent[]; |
1041 | static bool PhaseReportsIRSize[]; |
1042 | |
1043 | unsigned m_byteCodeBytes; |
1044 | unsigned __int64 m_totalCycles; |
1045 | unsigned __int64 m_invokesByPhase[PHASE_NUMBER_OF]; |
1046 | unsigned __int64 m_cyclesByPhase[PHASE_NUMBER_OF]; |
1047 | #if MEASURE_CLRAPI_CALLS |
1048 | unsigned __int64 m_CLRinvokesByPhase[PHASE_NUMBER_OF]; |
1049 | unsigned __int64 m_CLRcyclesByPhase[PHASE_NUMBER_OF]; |
1050 | #endif |
1051 | |
1052 | unsigned m_nodeCountAfterPhase[PHASE_NUMBER_OF]; |
1053 | |
1054 | // For better documentation, we call EndPhase on |
1055 | // non-leaf phases. We should also call EndPhase on the |
1056 | // last leaf subphase; obviously, the elapsed cycles between the EndPhase |
1057 | // for the last leaf subphase and the EndPhase for an ancestor should be very small. |
1058 | // We add all such "redundant end phase" intervals to this variable below; we print |
1059 | // it out in a report, so we can verify that it is, indeed, very small. If it ever |
1060 | // isn't, this means that we're doing something significant between the end of the last |
1061 | // declared subphase and the end of its parent. |
1062 | unsigned __int64 m_parentPhaseEndSlop; |
1063 | bool m_timerFailure; |
1064 | |
1065 | #if MEASURE_CLRAPI_CALLS |
1066 | // The following measures the time spent inside each individual CLR API call. |
1067 | unsigned m_allClrAPIcalls; |
1068 | unsigned m_perClrAPIcalls[API_ICorJitInfo_Names::API_COUNT]; |
1069 | unsigned __int64 m_allClrAPIcycles; |
1070 | unsigned __int64 m_perClrAPIcycles[API_ICorJitInfo_Names::API_COUNT]; |
1071 | unsigned __int32 m_maxClrAPIcycles[API_ICorJitInfo_Names::API_COUNT]; |
1072 | #endif // MEASURE_CLRAPI_CALLS |
1073 | |
1074 | CompTimeInfo(unsigned byteCodeBytes); |
1075 | #endif |
1076 | }; |
1077 | |
1078 | #ifdef FEATURE_JIT_METHOD_PERF |
1079 | |
1080 | #if MEASURE_CLRAPI_CALLS |
1081 | struct WrapICorJitInfo; |
1082 | #endif |
1083 | |
1084 | // This class summarizes the JIT time information over the course of a run: the number of methods compiled, |
1085 | // and the total and maximum timings. (These are instances of the "CompTimeInfo" type described above). |
1086 | // The operation of adding a single method's timing to the summary may be performed concurrently by several |
1087 | // threads, so it is protected by a lock. |
1088 | // This class is intended to be used as a singleton type, with only a single instance. |
1089 | class CompTimeSummaryInfo |
1090 | { |
1091 | // This lock protects the fields of all CompTimeSummaryInfo(s) (of which we expect there to be one). |
1092 | static CritSecObject s_compTimeSummaryLock; |
1093 | |
1094 | int m_numMethods; |
1095 | int m_totMethods; |
1096 | CompTimeInfo m_total; |
1097 | CompTimeInfo m_maximum; |
1098 | |
1099 | int m_numFilteredMethods; |
1100 | CompTimeInfo m_filtered; |
1101 | |
1102 | // This can use what ever data you want to determine if the value to be added |
1103 | // belongs in the filtered section (it's always included in the unfiltered section) |
1104 | bool IncludedInFilteredData(CompTimeInfo& info); |
1105 | |
1106 | public: |
1107 | // This is the unique CompTimeSummaryInfo object for this instance of the runtime. |
1108 | static CompTimeSummaryInfo s_compTimeSummary; |
1109 | |
1110 | CompTimeSummaryInfo() |
1111 | : m_numMethods(0), m_totMethods(0), m_total(0), m_maximum(0), m_numFilteredMethods(0), m_filtered(0) |
1112 | { |
1113 | } |
1114 | |
1115 | // Assumes that "info" is a completed CompTimeInfo for a compilation; adds it to the summary. |
1116 | // This is thread safe. |
1117 | void AddInfo(CompTimeInfo& info, bool includePhases); |
1118 | |
1119 | // Print the summary information to "f". |
1120 | // This is not thread-safe; assumed to be called by only one thread. |
1121 | void Print(FILE* f); |
1122 | }; |
1123 | |
1124 | // A JitTimer encapsulates a CompTimeInfo for a single compilation. It also tracks the start of compilation, |
1125 | // and when the current phase started. This is intended to be part of a Compilation object. This is |
1126 | // disabled (FEATURE_JIT_METHOD_PERF not defined) when FEATURE_CORECLR is set, or on non-windows platforms. |
1127 | // |
1128 | class JitTimer |
1129 | { |
1130 | unsigned __int64 m_start; // Start of the compilation. |
1131 | unsigned __int64 m_curPhaseStart; // Start of the current phase. |
1132 | #if MEASURE_CLRAPI_CALLS |
1133 | unsigned __int64 m_CLRcallStart; // Start of the current CLR API call (if any). |
1134 | unsigned __int64 m_CLRcallInvokes; // CLR API invokes under current outer so far |
1135 | unsigned __int64 m_CLRcallCycles; // CLR API cycles under current outer so far. |
1136 | int m_CLRcallAPInum; // The enum/index of the current CLR API call (or -1). |
1137 | static double s_cyclesPerSec; // Cached for speedier measurements |
1138 | #endif |
1139 | #ifdef DEBUG |
1140 | Phases m_lastPhase; // The last phase that was completed (or (Phases)-1 to start). |
1141 | #endif |
1142 | CompTimeInfo m_info; // The CompTimeInfo for this compilation. |
1143 | |
1144 | static CritSecObject s_csvLock; // Lock to protect the time log file. |
1145 | void PrintCsvMethodStats(Compiler* comp); |
1146 | |
1147 | private: |
1148 | void* operator new(size_t); |
1149 | void* operator new[](size_t); |
1150 | void operator delete(void*); |
1151 | void operator delete[](void*); |
1152 | |
1153 | public: |
1154 | // Initialized the timer instance |
1155 | JitTimer(unsigned byteCodeSize); |
1156 | |
1157 | static JitTimer* Create(Compiler* comp, unsigned byteCodeSize) |
1158 | { |
1159 | return ::new (comp, CMK_Unknown) JitTimer(byteCodeSize); |
1160 | } |
1161 | |
1162 | static void (); |
1163 | |
1164 | // Ends the current phase (argument is for a redundant check). |
1165 | void EndPhase(Compiler* compiler, Phases phase); |
1166 | |
1167 | #if MEASURE_CLRAPI_CALLS |
1168 | // Start and end a timed CLR API call. |
1169 | void CLRApiCallEnter(unsigned apix); |
1170 | void CLRApiCallLeave(unsigned apix); |
1171 | #endif // MEASURE_CLRAPI_CALLS |
1172 | |
1173 | // Completes the timing of the current method, which is assumed to have "byteCodeBytes" bytes of bytecode, |
1174 | // and adds it to "sum". |
1175 | void Terminate(Compiler* comp, CompTimeSummaryInfo& sum, bool includePhases); |
1176 | |
1177 | // Attempts to query the cycle counter of the current thread. If successful, returns "true" and sets |
1178 | // *cycles to the cycle counter value. Otherwise, returns false and sets the "m_timerFailure" flag of |
1179 | // "m_info" to true. |
1180 | bool GetThreadCycles(unsigned __int64* cycles) |
1181 | { |
1182 | bool res = CycleTimer::GetThreadCyclesS(cycles); |
1183 | if (!res) |
1184 | { |
1185 | m_info.m_timerFailure = true; |
1186 | } |
1187 | return res; |
1188 | } |
1189 | }; |
1190 | #endif // FEATURE_JIT_METHOD_PERF |
1191 | |
1192 | //------------------- Function/Funclet info ------------------------------- |
1193 | enum FuncKind : BYTE |
1194 | { |
1195 | FUNC_ROOT, // The main/root function (always id==0) |
1196 | FUNC_HANDLER, // a funclet associated with an EH handler (finally, fault, catch, filter handler) |
1197 | FUNC_FILTER, // a funclet associated with an EH filter |
1198 | FUNC_COUNT |
1199 | }; |
1200 | |
1201 | class emitLocation; |
1202 | |
1203 | struct FuncInfoDsc |
1204 | { |
1205 | FuncKind funKind; |
1206 | BYTE funFlags; // Currently unused, just here for padding |
1207 | unsigned short funEHIndex; // index, into the ebd table, of innermost EH clause corresponding to this |
1208 | // funclet. It is only valid if funKind field indicates this is a |
1209 | // EH-related funclet: FUNC_HANDLER or FUNC_FILTER |
1210 | |
1211 | #if defined(_TARGET_AMD64_) |
1212 | |
1213 | // TODO-AMD64-Throughput: make the AMD64 info more like the ARM info to avoid having this large static array. |
1214 | emitLocation* startLoc; |
1215 | emitLocation* endLoc; |
1216 | emitLocation* coldStartLoc; // locations for the cold section, if there is one. |
1217 | emitLocation* coldEndLoc; |
1218 | UNWIND_INFO ; |
1219 | // Maximum of 255 UNWIND_CODE 'nodes' and then the unwind header. If there are an odd |
1220 | // number of codes, the VM or Zapper will 4-byte align the whole thing. |
1221 | BYTE unwindCodes[offsetof(UNWIND_INFO, UnwindCode) + (0xFF * sizeof(UNWIND_CODE))]; |
1222 | unsigned unwindCodeSlot; |
1223 | |
1224 | #elif defined(_TARGET_X86_) |
1225 | |
1226 | #if defined(_TARGET_UNIX_) |
1227 | emitLocation* startLoc; |
1228 | emitLocation* endLoc; |
1229 | emitLocation* coldStartLoc; // locations for the cold section, if there is one. |
1230 | emitLocation* coldEndLoc; |
1231 | #endif // _TARGET_UNIX_ |
1232 | |
1233 | #elif defined(_TARGET_ARMARCH_) |
1234 | |
1235 | UnwindInfo uwi; // Unwind information for this function/funclet's hot section |
1236 | UnwindInfo* uwiCold; // Unwind information for this function/funclet's cold section |
1237 | // Note: we only have a pointer here instead of the actual object, |
1238 | // to save memory in the JIT case (compared to the NGEN case), |
1239 | // where we don't have any cold section. |
1240 | // Note 2: we currently don't support hot/cold splitting in functions |
1241 | // with EH, so uwiCold will be NULL for all funclets. |
1242 | |
1243 | #if defined(_TARGET_UNIX_) |
1244 | emitLocation* startLoc; |
1245 | emitLocation* endLoc; |
1246 | emitLocation* coldStartLoc; // locations for the cold section, if there is one. |
1247 | emitLocation* coldEndLoc; |
1248 | #endif // _TARGET_UNIX_ |
1249 | |
1250 | #endif // _TARGET_ARMARCH_ |
1251 | |
1252 | #if defined(_TARGET_UNIX_) |
1253 | jitstd::vector<CFI_CODE>* cfiCodes; |
1254 | #endif // _TARGET_UNIX_ |
1255 | |
1256 | // Eventually we may want to move rsModifiedRegsMask, lvaOutgoingArgSize, and anything else |
1257 | // that isn't shared between the main function body and funclets. |
1258 | }; |
1259 | |
1260 | struct fgArgTabEntry |
1261 | { |
1262 | GenTree* node; // Initially points at the Op1 field of 'parent', but if the argument is replaced with an GT_ASG or |
1263 | // placeholder it will point at the actual argument in the gtCallLateArgs list. |
1264 | GenTree* parent; // Points at the GT_LIST node in the gtCallArgs for this argument |
1265 | |
1266 | unsigned argNum; // The original argument number, also specifies the required argument evaluation order from the IL |
1267 | |
1268 | private: |
1269 | regNumberSmall regNums[MAX_ARG_REG_COUNT]; // The registers to use when passing this argument, set to REG_STK for |
1270 | // arguments passed on the stack |
1271 | public: |
1272 | unsigned numRegs; // Count of number of registers that this argument uses. |
1273 | // Note that on ARM, if we have a double hfa, this reflects the number |
1274 | // of DOUBLE registers. |
1275 | |
1276 | // A slot is a pointer sized region in the OutArg area. |
1277 | unsigned slotNum; // When an argument is passed in the OutArg area this is the slot number in the OutArg area |
1278 | unsigned numSlots; // Count of number of slots that this argument uses |
1279 | |
1280 | unsigned alignment; // 1 or 2 (slots/registers) |
1281 | private: |
1282 | unsigned _lateArgInx; // index into gtCallLateArgs list; UINT_MAX if this is not a late arg. |
1283 | public: |
1284 | unsigned tmpNum; // the LclVar number if we had to force evaluation of this arg |
1285 | |
1286 | var_types argType; // The type used to pass this argument. This is generally the original argument type, but when a |
1287 | // struct is passed as a scalar type, this is that type. |
1288 | // Note that if a struct is passed by reference, this will still be the struct type. |
1289 | |
1290 | bool needTmp : 1; // True when we force this argument's evaluation into a temp LclVar |
1291 | bool needPlace : 1; // True when we must replace this argument with a placeholder node |
1292 | bool isTmp : 1; // True when we setup a temp LclVar for this argument due to size issues with the struct |
1293 | bool processed : 1; // True when we have decided the evaluation order for this argument in the gtCallLateArgs |
1294 | bool isBackFilled : 1; // True when the argument fills a register slot skipped due to alignment requirements of |
1295 | // previous arguments. |
1296 | bool isNonStandard : 1; // True if it is an arg that is passed in a reg other than a standard arg reg, or is forced |
1297 | // to be on the stack despite its arg list position. |
1298 | bool isStruct : 1; // True if this is a struct arg |
1299 | bool _isVararg : 1; // True if the argument is in a vararg context. |
1300 | bool passedByRef : 1; // True iff the argument is passed by reference. |
1301 | #ifdef FEATURE_ARG_SPLIT |
1302 | bool _isSplit : 1; // True when this argument is split between the registers and OutArg area |
1303 | #endif // FEATURE_ARG_SPLIT |
1304 | #ifdef FEATURE_HFA |
1305 | bool _isHfaArg : 1; // True when the argument is an HFA type. |
1306 | bool _isDoubleHfa : 1; // True when the argument is an HFA, with an element type of DOUBLE. |
1307 | #endif |
1308 | |
1309 | bool isLateArg() |
1310 | { |
1311 | bool isLate = (_lateArgInx != UINT_MAX); |
1312 | return isLate; |
1313 | } |
1314 | |
1315 | __declspec(property(get = getLateArgInx, put = setLateArgInx)) unsigned lateArgInx; |
1316 | unsigned getLateArgInx() |
1317 | { |
1318 | assert(isLateArg()); |
1319 | return _lateArgInx; |
1320 | } |
1321 | void setLateArgInx(unsigned inx) |
1322 | { |
1323 | _lateArgInx = inx; |
1324 | } |
1325 | __declspec(property(get = getRegNum)) regNumber regNum; |
1326 | regNumber getRegNum() |
1327 | { |
1328 | return (regNumber)regNums[0]; |
1329 | } |
1330 | __declspec(property(get = getOtherRegNum)) regNumber otherRegNum; |
1331 | regNumber getOtherRegNum() |
1332 | { |
1333 | return (regNumber)regNums[1]; |
1334 | } |
1335 | |
1336 | #if defined(UNIX_AMD64_ABI) |
1337 | SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc; |
1338 | #endif |
1339 | |
1340 | void setRegNum(unsigned int i, regNumber regNum) |
1341 | { |
1342 | assert(i < MAX_ARG_REG_COUNT); |
1343 | regNums[i] = (regNumberSmall)regNum; |
1344 | } |
1345 | regNumber getRegNum(unsigned int i) |
1346 | { |
1347 | assert(i < MAX_ARG_REG_COUNT); |
1348 | return (regNumber)regNums[i]; |
1349 | } |
1350 | |
1351 | __declspec(property(get = getIsSplit, put = setIsSplit)) bool isSplit; |
1352 | bool getIsSplit() |
1353 | { |
1354 | #ifdef FEATURE_ARG_SPLIT |
1355 | return _isSplit; |
1356 | #else // FEATURE_ARG_SPLIT |
1357 | return false; |
1358 | #endif |
1359 | } |
1360 | void setIsSplit(bool value) |
1361 | { |
1362 | #ifdef FEATURE_ARG_SPLIT |
1363 | _isSplit = value; |
1364 | #endif |
1365 | } |
1366 | |
1367 | __declspec(property(get = getIsVararg, put = setIsVararg)) bool isVararg; |
1368 | bool getIsVararg() |
1369 | { |
1370 | #ifdef FEATURE_VARARG |
1371 | return _isVararg; |
1372 | #else |
1373 | return false; |
1374 | #endif |
1375 | } |
1376 | void setIsVararg(bool value) |
1377 | { |
1378 | #ifdef FEATURE_VARARG |
1379 | _isVararg = value; |
1380 | #endif // FEATURE_VARARG |
1381 | } |
1382 | |
1383 | __declspec(property(get = getIsHfaArg)) bool isHfaArg; |
1384 | bool getIsHfaArg() |
1385 | { |
1386 | #ifdef FEATURE_HFA |
1387 | return _isHfaArg; |
1388 | #else |
1389 | return false; |
1390 | #endif |
1391 | } |
1392 | |
1393 | __declspec(property(get = getIsHfaRegArg)) bool isHfaRegArg; |
1394 | bool getIsHfaRegArg() |
1395 | { |
1396 | #ifdef FEATURE_HFA |
1397 | return _isHfaArg && isPassedInRegisters(); |
1398 | #else |
1399 | return false; |
1400 | #endif |
1401 | } |
1402 | |
1403 | __declspec(property(get = getHfaType)) var_types hfaType; |
1404 | var_types getHfaType() |
1405 | { |
1406 | #ifdef FEATURE_HFA |
1407 | return _isHfaArg ? (_isDoubleHfa ? TYP_DOUBLE : TYP_FLOAT) : TYP_UNDEF; |
1408 | #else |
1409 | return TYP_UNDEF; |
1410 | #endif |
1411 | } |
1412 | |
1413 | void setHfaType(var_types type, unsigned hfaSlots) |
1414 | { |
1415 | #ifdef FEATURE_HFA |
1416 | if (type != TYP_UNDEF) |
1417 | { |
1418 | // We must already have set the passing mode. |
1419 | assert(numRegs != 0 || numSlots != 0); |
1420 | // We originally set numRegs according to the size of the struct, but if the size of the |
1421 | // hfaType is not the same as the pointer size, we need to correct it. |
1422 | // Note that hfaSlots is the number of registers we will use. For ARM, that is twice |
1423 | // the number of "double registers". |
1424 | unsigned numHfaRegs = hfaSlots; |
1425 | if (isPassedInRegisters()) |
1426 | { |
1427 | #ifdef _TARGET_ARM_ |
1428 | if (type == TYP_DOUBLE) |
1429 | { |
1430 | // Must be an even number of registers. |
1431 | assert((numRegs & 1) == 0); |
1432 | numHfaRegs = hfaSlots / 2; |
1433 | } |
1434 | #endif // _TARGET_ARM_ |
1435 | if (_isHfaArg) |
1436 | { |
1437 | // This should already be set correctly. |
1438 | assert(numRegs == numHfaRegs); |
1439 | assert(_isDoubleHfa == (type == TYP_DOUBLE)); |
1440 | } |
1441 | else |
1442 | { |
1443 | numRegs = numHfaRegs; |
1444 | } |
1445 | } |
1446 | _isDoubleHfa = (type == TYP_DOUBLE); |
1447 | _isHfaArg = true; |
1448 | } |
1449 | #endif // FEATURE_HFA |
1450 | } |
1451 | |
1452 | #ifdef _TARGET_ARM_ |
1453 | void SetIsBackFilled(bool backFilled) |
1454 | { |
1455 | isBackFilled = backFilled; |
1456 | } |
1457 | |
1458 | bool IsBackFilled() const |
1459 | { |
1460 | return isBackFilled; |
1461 | } |
1462 | #else // !_TARGET_ARM_ |
1463 | void SetIsBackFilled(bool backFilled) |
1464 | { |
1465 | } |
1466 | |
1467 | bool IsBackFilled() const |
1468 | { |
1469 | return false; |
1470 | } |
1471 | #endif // !_TARGET_ARM_ |
1472 | |
1473 | bool isPassedInRegisters() |
1474 | { |
1475 | return !isSplit && (numRegs != 0); |
1476 | } |
1477 | |
1478 | bool isPassedInFloatRegisters() |
1479 | { |
1480 | #ifdef _TARGET_X86 |
1481 | return false; |
1482 | #else |
1483 | return isValidFloatArgReg(regNum); |
1484 | #endif |
1485 | } |
1486 | |
1487 | bool isSingleRegOrSlot() |
1488 | { |
1489 | return !isSplit && ((numRegs == 1) || (numSlots == 1)); |
1490 | } |
1491 | |
1492 | // Returns the number of "slots" used, where for this purpose a |
1493 | // register counts as a slot. |
1494 | unsigned getSlotCount() |
1495 | { |
1496 | if (isBackFilled) |
1497 | { |
1498 | assert(isPassedInRegisters()); |
1499 | assert(numRegs == 1); |
1500 | } |
1501 | else if (regNum == REG_STK) |
1502 | { |
1503 | assert(!isPassedInRegisters()); |
1504 | assert(numRegs == 0); |
1505 | } |
1506 | else |
1507 | { |
1508 | assert(numRegs > 0); |
1509 | } |
1510 | return numSlots + numRegs; |
1511 | } |
1512 | |
1513 | // Returns the size as a multiple of pointer-size. |
1514 | // For targets without HFAs, this is the same as getSlotCount(). |
1515 | unsigned getSize() |
1516 | { |
1517 | unsigned size = getSlotCount(); |
1518 | #ifdef FEATURE_HFA |
1519 | #ifdef _TARGET_ARM_ |
1520 | // We counted the number of regs, but if they are DOUBLE hfa regs we have to double the size. |
1521 | if (isHfaRegArg && (hfaType == TYP_DOUBLE)) |
1522 | { |
1523 | assert(!isSplit); |
1524 | size <<= 1; |
1525 | } |
1526 | #elif defined(_TARGET_ARM64_) |
1527 | // We counted the number of regs, but if they are FLOAT hfa regs we have to halve the size. |
1528 | if (isHfaRegArg && (hfaType == TYP_FLOAT)) |
1529 | { |
1530 | // Round up in case of odd HFA count. |
1531 | size = (size + 1) >> 1; |
1532 | } |
1533 | #endif // _TARGET_ARM64_ |
1534 | #endif |
1535 | return size; |
1536 | } |
1537 | |
1538 | // Set the register numbers for a multireg argument. |
1539 | // There's nothing to do on x64/Ux because the structDesc has already been used to set the |
1540 | // register numbers. |
1541 | void SetMultiRegNums() |
1542 | { |
1543 | #if FEATURE_MULTIREG_ARGS && !defined(UNIX_AMD64_ABI) |
1544 | if (numRegs == 1) |
1545 | { |
1546 | return; |
1547 | } |
1548 | |
1549 | regNumber argReg = getRegNum(0); |
1550 | #ifdef _TARGET_ARM_ |
1551 | unsigned int regSize = (hfaType == TYP_DOUBLE) ? 2 : 1; |
1552 | #else |
1553 | unsigned int regSize = 1; |
1554 | #endif |
1555 | for (unsigned int regIndex = 1; regIndex < numRegs; regIndex++) |
1556 | { |
1557 | argReg = (regNumber)(argReg + regSize); |
1558 | setRegNum(regIndex, argReg); |
1559 | } |
1560 | #endif // FEATURE_MULTIREG_ARGS && !defined(UNIX_AMD64_ABI) |
1561 | } |
1562 | |
1563 | // Check that the value of 'isStruct' is consistent. |
1564 | // A struct arg must be one of the following: |
1565 | // - A node of struct type, |
1566 | // - A GT_FIELD_LIST, or |
1567 | // - A node of a scalar type, passed in a single register or slot |
1568 | // (or two slots in the case of a struct pass on the stack as TYP_DOUBLE). |
1569 | // |
1570 | void checkIsStruct() |
1571 | { |
1572 | if (isStruct) |
1573 | { |
1574 | if (!varTypeIsStruct(node) && !node->OperIs(GT_FIELD_LIST)) |
1575 | { |
1576 | // This is the case where we are passing a struct as a primitive type. |
1577 | // On most targets, this is always a single register or slot. |
1578 | // However, on ARM this could be two slots if it is TYP_DOUBLE. |
1579 | bool isPassedAsPrimitiveType = ((numRegs == 1) || ((numRegs == 0) && (numSlots == 1))); |
1580 | #ifdef _TARGET_ARM_ |
1581 | if (!isPassedAsPrimitiveType) |
1582 | { |
1583 | if (node->TypeGet() == TYP_DOUBLE && numRegs == 0 && (numSlots == 2)) |
1584 | { |
1585 | isPassedAsPrimitiveType = true; |
1586 | } |
1587 | } |
1588 | #endif // _TARGET_ARM_ |
1589 | assert(isPassedAsPrimitiveType); |
1590 | } |
1591 | } |
1592 | else |
1593 | { |
1594 | assert(!varTypeIsStruct(node)); |
1595 | } |
1596 | } |
1597 | |
1598 | #ifdef DEBUG |
1599 | void Dump(); |
1600 | #endif |
1601 | }; |
1602 | |
1603 | //------------------------------------------------------------------------- |
1604 | // |
1605 | // The class fgArgInfo is used to handle the arguments |
1606 | // when morphing a GT_CALL node. |
1607 | // |
1608 | |
1609 | class fgArgInfo |
1610 | { |
1611 | Compiler* compiler; // Back pointer to the compiler instance so that we can allocate memory |
1612 | GenTreeCall* callTree; // Back pointer to the GT_CALL node for this fgArgInfo |
1613 | unsigned argCount; // Updatable arg count value |
1614 | unsigned nextSlotNum; // Updatable slot count value |
1615 | unsigned stkLevel; // Stack depth when we make this call (for x86) |
1616 | |
1617 | #if defined(UNIX_X86_ABI) |
1618 | bool alignmentDone; // Updateable flag, set to 'true' after we've done any required alignment. |
1619 | unsigned stkSizeBytes; // Size of stack used by this call, in bytes. Calculated during fgMorphArgs(). |
1620 | unsigned padStkAlign; // Stack alignment in bytes required before arguments are pushed for this call. |
1621 | // Computed dynamically during codegen, based on stkSizeBytes and the current |
1622 | // stack level (genStackLevel) when the first stack adjustment is made for |
1623 | // this call. |
1624 | #endif |
1625 | |
1626 | #if FEATURE_FIXED_OUT_ARGS |
1627 | unsigned outArgSize; // Size of the out arg area for the call, will be at least MIN_ARG_AREA_FOR_CALL |
1628 | #endif |
1629 | |
1630 | unsigned argTableSize; // size of argTable array (equal to the argCount when done with fgMorphArgs) |
1631 | bool hasRegArgs; // true if we have one or more register arguments |
1632 | bool hasStackArgs; // true if we have one or more stack arguments |
1633 | bool argsComplete; // marker for state |
1634 | bool argsSorted; // marker for state |
1635 | fgArgTabEntry** argTable; // variable sized array of per argument descrption: (i.e. argTable[argTableSize]) |
1636 | |
1637 | private: |
1638 | void AddArg(fgArgTabEntry* curArgTabEntry); |
1639 | |
1640 | public: |
1641 | fgArgInfo(Compiler* comp, GenTreeCall* call, unsigned argCount); |
1642 | fgArgInfo(GenTreeCall* newCall, GenTreeCall* oldCall); |
1643 | |
1644 | fgArgTabEntry* AddRegArg(unsigned argNum, |
1645 | GenTree* node, |
1646 | GenTree* parent, |
1647 | regNumber regNum, |
1648 | unsigned numRegs, |
1649 | unsigned alignment, |
1650 | bool isStruct, |
1651 | bool isVararg = false); |
1652 | |
1653 | #ifdef UNIX_AMD64_ABI |
1654 | fgArgTabEntry* AddRegArg(unsigned argNum, |
1655 | GenTree* node, |
1656 | GenTree* parent, |
1657 | regNumber regNum, |
1658 | unsigned numRegs, |
1659 | unsigned alignment, |
1660 | const bool isStruct, |
1661 | const bool isVararg, |
1662 | const regNumber otherRegNum, |
1663 | const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* const structDescPtr = nullptr); |
1664 | #endif // UNIX_AMD64_ABI |
1665 | |
1666 | fgArgTabEntry* AddStkArg(unsigned argNum, |
1667 | GenTree* node, |
1668 | GenTree* parent, |
1669 | unsigned numSlots, |
1670 | unsigned alignment, |
1671 | bool isStruct, |
1672 | bool isVararg = false); |
1673 | |
1674 | void RemorphReset(); |
1675 | void UpdateRegArg(fgArgTabEntry* argEntry, GenTree* node, bool reMorphing); |
1676 | void UpdateStkArg(fgArgTabEntry* argEntry, GenTree* node, bool reMorphing); |
1677 | |
1678 | void SplitArg(unsigned argNum, unsigned numRegs, unsigned numSlots); |
1679 | |
1680 | void EvalToTmp(fgArgTabEntry* curArgTabEntry, unsigned tmpNum, GenTree* newNode); |
1681 | |
1682 | void ArgsComplete(); |
1683 | |
1684 | void SortArgs(); |
1685 | |
1686 | void EvalArgsToTemps(); |
1687 | |
1688 | unsigned ArgCount() |
1689 | { |
1690 | return argCount; |
1691 | } |
1692 | fgArgTabEntry** ArgTable() |
1693 | { |
1694 | return argTable; |
1695 | } |
1696 | unsigned GetNextSlotNum() |
1697 | { |
1698 | return nextSlotNum; |
1699 | } |
1700 | bool HasRegArgs() |
1701 | { |
1702 | return hasRegArgs; |
1703 | } |
1704 | bool HasStackArgs() |
1705 | { |
1706 | return hasStackArgs; |
1707 | } |
1708 | bool AreArgsComplete() const |
1709 | { |
1710 | return argsComplete; |
1711 | } |
1712 | #if FEATURE_FIXED_OUT_ARGS |
1713 | unsigned GetOutArgSize() const |
1714 | { |
1715 | return outArgSize; |
1716 | } |
1717 | void SetOutArgSize(unsigned newVal) |
1718 | { |
1719 | outArgSize = newVal; |
1720 | } |
1721 | #endif // FEATURE_FIXED_OUT_ARGS |
1722 | |
1723 | #if defined(UNIX_X86_ABI) |
1724 | void ComputeStackAlignment(unsigned curStackLevelInBytes) |
1725 | { |
1726 | padStkAlign = AlignmentPad(curStackLevelInBytes, STACK_ALIGN); |
1727 | } |
1728 | |
1729 | unsigned GetStkAlign() |
1730 | { |
1731 | return padStkAlign; |
1732 | } |
1733 | |
1734 | void SetStkSizeBytes(unsigned newStkSizeBytes) |
1735 | { |
1736 | stkSizeBytes = newStkSizeBytes; |
1737 | } |
1738 | |
1739 | unsigned GetStkSizeBytes() const |
1740 | { |
1741 | return stkSizeBytes; |
1742 | } |
1743 | |
1744 | bool IsStkAlignmentDone() const |
1745 | { |
1746 | return alignmentDone; |
1747 | } |
1748 | |
1749 | void SetStkAlignmentDone() |
1750 | { |
1751 | alignmentDone = true; |
1752 | } |
1753 | #endif // defined(UNIX_X86_ABI) |
1754 | |
1755 | // Get the fgArgTabEntry for the arg at position argNum. |
1756 | fgArgTabEntry* GetArgEntry(unsigned argNum, bool reMorphing = true) |
1757 | { |
1758 | fgArgTabEntry* curArgTabEntry = nullptr; |
1759 | |
1760 | if (!reMorphing) |
1761 | { |
1762 | // The arg table has not yet been sorted. |
1763 | curArgTabEntry = argTable[argNum]; |
1764 | assert(curArgTabEntry->argNum == argNum); |
1765 | return curArgTabEntry; |
1766 | } |
1767 | |
1768 | for (unsigned i = 0; i < argCount; i++) |
1769 | { |
1770 | curArgTabEntry = argTable[i]; |
1771 | if (curArgTabEntry->argNum == argNum) |
1772 | { |
1773 | return curArgTabEntry; |
1774 | } |
1775 | } |
1776 | noway_assert(!"GetArgEntry: argNum not found" ); |
1777 | return nullptr; |
1778 | } |
1779 | |
1780 | // Get the node for the arg at position argIndex. |
1781 | // Caller must ensure that this index is a valid arg index. |
1782 | GenTree* GetArgNode(unsigned argIndex) |
1783 | { |
1784 | return GetArgEntry(argIndex)->node; |
1785 | } |
1786 | |
1787 | void Dump(Compiler* compiler); |
1788 | }; |
1789 | |
1790 | #ifdef DEBUG |
1791 | // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
1792 | // We have the ability to mark source expressions with "Test Labels." |
1793 | // These drive assertions within the JIT, or internal JIT testing. For example, we could label expressions |
1794 | // that should be CSE defs, and other expressions that should uses of those defs, with a shared label. |
1795 | |
1796 | enum TestLabel // This must be kept identical to System.Runtime.CompilerServices.JitTestLabel.TestLabel. |
1797 | { |
1798 | TL_SsaName, |
1799 | TL_VN, // Defines a "VN equivalence class". (For full VN, including exceptions thrown). |
1800 | TL_VNNorm, // Like above, but uses the non-exceptional value of the expression. |
1801 | TL_CSE_Def, // This must be identified in the JIT as a CSE def |
1802 | TL_CSE_Use, // This must be identified in the JIT as a CSE use |
1803 | TL_LoopHoist, // Expression must (or must not) be hoisted out of the loop. |
1804 | }; |
1805 | |
1806 | struct TestLabelAndNum |
1807 | { |
1808 | TestLabel m_tl; |
1809 | ssize_t m_num; |
1810 | |
1811 | TestLabelAndNum() : m_tl(TestLabel(0)), m_num(0) |
1812 | { |
1813 | } |
1814 | }; |
1815 | |
1816 | typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, TestLabelAndNum> NodeToTestDataMap; |
1817 | |
1818 | // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
1819 | #endif // DEBUG |
1820 | |
1821 | /* |
1822 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
1823 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
1824 | XX XX |
1825 | XX The big guy. The sections are currently organized as : XX |
1826 | XX XX |
1827 | XX o GenTree and BasicBlock XX |
1828 | XX o LclVarsInfo XX |
1829 | XX o Importer XX |
1830 | XX o FlowGraph XX |
1831 | XX o Optimizer XX |
1832 | XX o RegAlloc XX |
1833 | XX o EEInterface XX |
1834 | XX o TempsInfo XX |
1835 | XX o RegSet XX |
1836 | XX o GCInfo XX |
1837 | XX o Instruction XX |
1838 | XX o ScopeInfo XX |
1839 | XX o PrologScopeInfo XX |
1840 | XX o CodeGenerator XX |
1841 | XX o UnwindInfo XX |
1842 | XX o Compiler XX |
1843 | XX o typeInfo XX |
1844 | XX XX |
1845 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
1846 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
1847 | */ |
1848 | |
1849 | struct HWIntrinsicInfo; |
1850 | |
1851 | class Compiler |
1852 | { |
1853 | friend class emitter; |
1854 | friend class UnwindInfo; |
1855 | friend class UnwindFragmentInfo; |
1856 | friend class UnwindEpilogInfo; |
1857 | friend class JitTimer; |
1858 | friend class LinearScan; |
1859 | friend class fgArgInfo; |
1860 | friend class Rationalizer; |
1861 | friend class Phase; |
1862 | friend class Lowering; |
1863 | friend class CSE_DataFlow; |
1864 | friend class CSE_Heuristic; |
1865 | friend class CodeGenInterface; |
1866 | friend class CodeGen; |
1867 | friend class LclVarDsc; |
1868 | friend class TempDsc; |
1869 | friend class LIR; |
1870 | friend class ObjectAllocator; |
1871 | friend class LocalAddressVisitor; |
1872 | friend struct GenTree; |
1873 | |
1874 | #ifdef FEATURE_HW_INTRINSICS |
1875 | friend struct HWIntrinsicInfo; |
1876 | #endif // FEATURE_HW_INTRINSICS |
1877 | |
1878 | #ifndef _TARGET_64BIT_ |
1879 | friend class DecomposeLongs; |
1880 | #endif // !_TARGET_64BIT_ |
1881 | |
1882 | /* |
1883 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
1884 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
1885 | XX XX |
1886 | XX Misc structs definitions XX |
1887 | XX XX |
1888 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
1889 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
1890 | */ |
1891 | |
1892 | public: |
1893 | hashBvGlobalData hbvGlobalData; // Used by the hashBv bitvector package. |
1894 | |
1895 | #ifdef DEBUG |
1896 | bool verbose; |
1897 | bool dumpIR; |
1898 | bool dumpIRNodes; |
1899 | bool dumpIRTypes; |
1900 | bool dumpIRKinds; |
1901 | bool dumpIRLocals; |
1902 | bool dumpIRRegs; |
1903 | bool ; |
1904 | bool dumpIRValnums; |
1905 | bool dumpIRCosts; |
1906 | bool dumpIRFlags; |
1907 | bool dumpIRNoLists; |
1908 | bool dumpIRNoLeafs; |
1909 | bool dumpIRNoStmts; |
1910 | bool dumpIRTrees; |
1911 | bool dumpIRLinear; |
1912 | bool dumpIRDataflow; |
1913 | bool ; |
1914 | bool dumpIRExit; |
1915 | LPCWSTR dumpIRPhase; |
1916 | LPCWSTR dumpIRFormat; |
1917 | bool verboseTrees; |
1918 | bool shouldUseVerboseTrees(); |
1919 | bool asciiTrees; // If true, dump trees using only ASCII characters |
1920 | bool shouldDumpASCIITrees(); |
1921 | bool verboseSsa; // If true, produce especially verbose dump output in SSA construction. |
1922 | bool shouldUseVerboseSsa(); |
1923 | bool treesBeforeAfterMorph; // If true, print trees before/after morphing (paired by an intra-compilation id: |
1924 | int morphNum; // This counts the the trees that have been morphed, allowing us to label each uniquely. |
1925 | |
1926 | const char* VarNameToStr(VarName name) |
1927 | { |
1928 | return name; |
1929 | } |
1930 | |
1931 | DWORD expensiveDebugCheckLevel; |
1932 | #endif |
1933 | |
1934 | #if FEATURE_MULTIREG_RET |
1935 | GenTree* impAssignMultiRegTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass); |
1936 | #endif // FEATURE_MULTIREG_RET |
1937 | |
1938 | GenTree* impAssignSmallStructTypeToVar(GenTree* op, CORINFO_CLASS_HANDLE hClass); |
1939 | |
1940 | #ifdef ARM_SOFTFP |
1941 | bool isSingleFloat32Struct(CORINFO_CLASS_HANDLE hClass); |
1942 | #endif // ARM_SOFTFP |
1943 | |
1944 | //------------------------------------------------------------------------- |
1945 | // Functions to handle homogeneous floating-point aggregates (HFAs) in ARM. |
1946 | // HFAs are one to four element structs where each element is the same |
1947 | // type, either all float or all double. They are treated specially |
1948 | // in the ARM Procedure Call Standard, specifically, they are passed in |
1949 | // floating-point registers instead of the general purpose registers. |
1950 | // |
1951 | |
1952 | bool IsHfa(CORINFO_CLASS_HANDLE hClass); |
1953 | bool IsHfa(GenTree* tree); |
1954 | |
1955 | var_types GetHfaType(GenTree* tree); |
1956 | unsigned GetHfaCount(GenTree* tree); |
1957 | |
1958 | var_types GetHfaType(CORINFO_CLASS_HANDLE hClass); |
1959 | unsigned GetHfaCount(CORINFO_CLASS_HANDLE hClass); |
1960 | |
1961 | bool IsMultiRegReturnedType(CORINFO_CLASS_HANDLE hClass); |
1962 | |
1963 | //------------------------------------------------------------------------- |
1964 | // The following is used for validating format of EH table |
1965 | // |
1966 | |
1967 | struct EHNodeDsc; |
1968 | typedef struct EHNodeDsc* pEHNodeDsc; |
1969 | |
1970 | EHNodeDsc* ehnTree; // root of the tree comprising the EHnodes. |
1971 | EHNodeDsc* ehnNext; // root of the tree comprising the EHnodes. |
1972 | |
1973 | struct EHNodeDsc |
1974 | { |
1975 | enum EHBlockType |
1976 | { |
1977 | TryNode, |
1978 | FilterNode, |
1979 | HandlerNode, |
1980 | FinallyNode, |
1981 | FaultNode |
1982 | }; |
1983 | |
1984 | EHBlockType ehnBlockType; // kind of EH block |
1985 | IL_OFFSET ehnStartOffset; // IL offset of start of the EH block |
1986 | IL_OFFSET ehnEndOffset; // IL offset past end of the EH block. (TODO: looks like verInsertEhNode() sets this to |
1987 | // the last IL offset, not "one past the last one", i.e., the range Start to End is |
1988 | // inclusive). |
1989 | pEHNodeDsc ehnNext; // next (non-nested) block in sequential order |
1990 | pEHNodeDsc ehnChild; // leftmost nested block |
1991 | union { |
1992 | pEHNodeDsc ehnTryNode; // for filters and handlers, the corresponding try node |
1993 | pEHNodeDsc ehnHandlerNode; // for a try node, the corresponding handler node |
1994 | }; |
1995 | pEHNodeDsc ehnFilterNode; // if this is a try node and has a filter, otherwise 0 |
1996 | pEHNodeDsc ehnEquivalent; // if blockType=tryNode, start offset and end offset is same, |
1997 | |
1998 | void ehnSetTryNodeType() |
1999 | { |
2000 | ehnBlockType = TryNode; |
2001 | } |
2002 | void ehnSetFilterNodeType() |
2003 | { |
2004 | ehnBlockType = FilterNode; |
2005 | } |
2006 | void ehnSetHandlerNodeType() |
2007 | { |
2008 | ehnBlockType = HandlerNode; |
2009 | } |
2010 | void ehnSetFinallyNodeType() |
2011 | { |
2012 | ehnBlockType = FinallyNode; |
2013 | } |
2014 | void ehnSetFaultNodeType() |
2015 | { |
2016 | ehnBlockType = FaultNode; |
2017 | } |
2018 | |
2019 | BOOL ehnIsTryBlock() |
2020 | { |
2021 | return ehnBlockType == TryNode; |
2022 | } |
2023 | BOOL ehnIsFilterBlock() |
2024 | { |
2025 | return ehnBlockType == FilterNode; |
2026 | } |
2027 | BOOL ehnIsHandlerBlock() |
2028 | { |
2029 | return ehnBlockType == HandlerNode; |
2030 | } |
2031 | BOOL ehnIsFinallyBlock() |
2032 | { |
2033 | return ehnBlockType == FinallyNode; |
2034 | } |
2035 | BOOL ehnIsFaultBlock() |
2036 | { |
2037 | return ehnBlockType == FaultNode; |
2038 | } |
2039 | |
2040 | // returns true if there is any overlap between the two nodes |
2041 | static BOOL ehnIsOverlap(pEHNodeDsc node1, pEHNodeDsc node2) |
2042 | { |
2043 | if (node1->ehnStartOffset < node2->ehnStartOffset) |
2044 | { |
2045 | return (node1->ehnEndOffset >= node2->ehnStartOffset); |
2046 | } |
2047 | else |
2048 | { |
2049 | return (node1->ehnStartOffset <= node2->ehnEndOffset); |
2050 | } |
2051 | } |
2052 | |
2053 | // fails with BADCODE if inner is not completely nested inside outer |
2054 | static BOOL ehnIsNested(pEHNodeDsc inner, pEHNodeDsc outer) |
2055 | { |
2056 | return ((inner->ehnStartOffset >= outer->ehnStartOffset) && (inner->ehnEndOffset <= outer->ehnEndOffset)); |
2057 | } |
2058 | }; |
2059 | |
2060 | //------------------------------------------------------------------------- |
2061 | // Exception handling functions |
2062 | // |
2063 | |
2064 | #if !FEATURE_EH_FUNCLETS |
2065 | |
2066 | bool ehNeedsShadowSPslots() |
2067 | { |
2068 | return (info.compXcptnsCount || opts.compDbgEnC); |
2069 | } |
2070 | |
2071 | // 0 for methods with no EH |
2072 | // 1 for methods with non-nested EH, or where only the try blocks are nested |
2073 | // 2 for a method with a catch within a catch |
2074 | // etc. |
2075 | unsigned ehMaxHndNestingCount; |
2076 | |
2077 | #endif // !FEATURE_EH_FUNCLETS |
2078 | |
2079 | static bool jitIsBetween(unsigned value, unsigned start, unsigned end); |
2080 | static bool jitIsBetweenInclusive(unsigned value, unsigned start, unsigned end); |
2081 | |
2082 | bool bbInCatchHandlerILRange(BasicBlock* blk); |
2083 | bool bbInFilterILRange(BasicBlock* blk); |
2084 | bool bbInTryRegions(unsigned regionIndex, BasicBlock* blk); |
2085 | bool bbInExnFlowRegions(unsigned regionIndex, BasicBlock* blk); |
2086 | bool bbInHandlerRegions(unsigned regionIndex, BasicBlock* blk); |
2087 | bool bbInCatchHandlerRegions(BasicBlock* tryBlk, BasicBlock* hndBlk); |
2088 | unsigned short bbFindInnermostCommonTryRegion(BasicBlock* bbOne, BasicBlock* bbTwo); |
2089 | |
2090 | unsigned short bbFindInnermostTryRegionContainingHandlerRegion(unsigned handlerIndex); |
2091 | unsigned short bbFindInnermostHandlerRegionContainingTryRegion(unsigned tryIndex); |
2092 | |
2093 | // Returns true if "block" is the start of a try region. |
2094 | bool bbIsTryBeg(BasicBlock* block); |
2095 | |
2096 | // Returns true if "block" is the start of a handler or filter region. |
2097 | bool bbIsHandlerBeg(BasicBlock* block); |
2098 | |
2099 | // Returns true iff "block" is where control flows if an exception is raised in the |
2100 | // try region, and sets "*regionIndex" to the index of the try for the handler. |
2101 | // Differs from "IsHandlerBeg" in the case of filters, where this is true for the first |
2102 | // block of the filter, but not for the filter's handler. |
2103 | bool bbIsExFlowBlock(BasicBlock* block, unsigned* regionIndex); |
2104 | |
2105 | bool ehHasCallableHandlers(); |
2106 | |
2107 | // Return the EH descriptor for the given region index. |
2108 | EHblkDsc* ehGetDsc(unsigned regionIndex); |
2109 | |
2110 | // Return the EH index given a region descriptor. |
2111 | unsigned ehGetIndex(EHblkDsc* ehDsc); |
2112 | |
2113 | // Return the EH descriptor index of the enclosing try, for the given region index. |
2114 | unsigned ehGetEnclosingTryIndex(unsigned regionIndex); |
2115 | |
2116 | // Return the EH descriptor index of the enclosing handler, for the given region index. |
2117 | unsigned ehGetEnclosingHndIndex(unsigned regionIndex); |
2118 | |
2119 | // Return the EH descriptor for the most nested 'try' region this BasicBlock is a member of (or nullptr if this |
2120 | // block is not in a 'try' region). |
2121 | EHblkDsc* ehGetBlockTryDsc(BasicBlock* block); |
2122 | |
2123 | // Return the EH descriptor for the most nested filter or handler region this BasicBlock is a member of (or nullptr |
2124 | // if this block is not in a filter or handler region). |
2125 | EHblkDsc* ehGetBlockHndDsc(BasicBlock* block); |
2126 | |
2127 | // Return the EH descriptor for the most nested region that may handle exceptions raised in this BasicBlock (or |
2128 | // nullptr if this block's exceptions propagate to caller). |
2129 | EHblkDsc* ehGetBlockExnFlowDsc(BasicBlock* block); |
2130 | |
2131 | EHblkDsc* ehIsBlockTryLast(BasicBlock* block); |
2132 | EHblkDsc* ehIsBlockHndLast(BasicBlock* block); |
2133 | bool ehIsBlockEHLast(BasicBlock* block); |
2134 | |
2135 | bool ehBlockHasExnFlowDsc(BasicBlock* block); |
2136 | |
2137 | // Return the region index of the most nested EH region this block is in. |
2138 | unsigned ehGetMostNestedRegionIndex(BasicBlock* block, bool* inTryRegion); |
2139 | |
2140 | // Find the true enclosing try index, ignoring 'mutual protect' try. Uses IL ranges to check. |
2141 | unsigned ehTrueEnclosingTryIndexIL(unsigned regionIndex); |
2142 | |
2143 | // Return the index of the most nested enclosing region for a particular EH region. Returns NO_ENCLOSING_INDEX |
2144 | // if there is no enclosing region. If the returned index is not NO_ENCLOSING_INDEX, then '*inTryRegion' |
2145 | // is set to 'true' if the enclosing region is a 'try', or 'false' if the enclosing region is a handler. |
2146 | // (It can never be a filter.) |
2147 | unsigned ehGetEnclosingRegionIndex(unsigned regionIndex, bool* inTryRegion); |
2148 | |
2149 | // A block has been deleted. Update the EH table appropriately. |
2150 | void ehUpdateForDeletedBlock(BasicBlock* block); |
2151 | |
2152 | // Determine whether a block can be deleted while preserving the EH normalization rules. |
2153 | bool ehCanDeleteEmptyBlock(BasicBlock* block); |
2154 | |
2155 | // Update the 'last' pointers in the EH table to reflect new or deleted blocks in an EH region. |
2156 | void ehUpdateLastBlocks(BasicBlock* oldLast, BasicBlock* newLast); |
2157 | |
2158 | // For a finally handler, find the region index that the BBJ_CALLFINALLY lives in that calls the handler, |
2159 | // or NO_ENCLOSING_INDEX if the BBJ_CALLFINALLY lives in the main function body. Normally, the index |
2160 | // is the same index as the handler (and the BBJ_CALLFINALLY lives in the 'try' region), but for AMD64 the |
2161 | // BBJ_CALLFINALLY lives in the enclosing try or handler region, whichever is more nested, or the main function |
2162 | // body. If the returned index is not NO_ENCLOSING_INDEX, then '*inTryRegion' is set to 'true' if the |
2163 | // BBJ_CALLFINALLY lives in the returned index's 'try' region, or 'false' if lives in the handler region. (It never |
2164 | // lives in a filter.) |
2165 | unsigned ehGetCallFinallyRegionIndex(unsigned finallyIndex, bool* inTryRegion); |
2166 | |
2167 | // Find the range of basic blocks in which all BBJ_CALLFINALLY will be found that target the 'finallyIndex' region's |
2168 | // handler. Set begBlk to the first block, and endBlk to the block after the last block of the range |
2169 | // (nullptr if the last block is the last block in the program). |
2170 | // Precondition: 'finallyIndex' is the EH region of a try/finally clause. |
2171 | void ehGetCallFinallyBlockRange(unsigned finallyIndex, BasicBlock** begBlk, BasicBlock** endBlk); |
2172 | |
2173 | #ifdef DEBUG |
2174 | // Given a BBJ_CALLFINALLY block and the EH region index of the finally it is calling, return |
2175 | // 'true' if the BBJ_CALLFINALLY is in the correct EH region. |
2176 | bool ehCallFinallyInCorrectRegion(BasicBlock* blockCallFinally, unsigned finallyIndex); |
2177 | #endif // DEBUG |
2178 | |
2179 | #if FEATURE_EH_FUNCLETS |
2180 | // Do we need a PSPSym in the main function? For codegen purposes, we only need one |
2181 | // if there is a filter that protects a region with a nested EH clause (such as a |
2182 | // try/catch nested in the 'try' body of a try/filter/filter-handler). See |
2183 | // genFuncletProlog() for more details. However, the VM seems to use it for more |
2184 | // purposes, maybe including debugging. Until we are sure otherwise, always create |
2185 | // a PSPSym for functions with any EH. |
2186 | bool ehNeedsPSPSym() const |
2187 | { |
2188 | #ifdef _TARGET_X86_ |
2189 | return false; |
2190 | #else // _TARGET_X86_ |
2191 | return compHndBBtabCount > 0; |
2192 | #endif // _TARGET_X86_ |
2193 | } |
2194 | |
2195 | bool ehAnyFunclets(); // Are there any funclets in this function? |
2196 | unsigned ehFuncletCount(); // Return the count of funclets in the function |
2197 | |
2198 | unsigned bbThrowIndex(BasicBlock* blk); // Get the index to use as the cache key for sharing throw blocks |
2199 | #else // !FEATURE_EH_FUNCLETS |
2200 | bool ehAnyFunclets() |
2201 | { |
2202 | return false; |
2203 | } |
2204 | unsigned ehFuncletCount() |
2205 | { |
2206 | return 0; |
2207 | } |
2208 | |
2209 | unsigned bbThrowIndex(BasicBlock* blk) |
2210 | { |
2211 | return blk->bbTryIndex; |
2212 | } // Get the index to use as the cache key for sharing throw blocks |
2213 | #endif // !FEATURE_EH_FUNCLETS |
2214 | |
2215 | // Returns a flowList representing the "EH predecessors" of "blk". These are the normal predecessors of |
2216 | // "blk", plus one special case: if "blk" is the first block of a handler, considers the predecessor(s) of the first |
2217 | // first block of the corresponding try region to be "EH predecessors". (If there is a single such predecessor, |
2218 | // for example, we want to consider that the immediate dominator of the catch clause start block, so it's |
2219 | // convenient to also consider it a predecessor.) |
2220 | flowList* BlockPredsWithEH(BasicBlock* blk); |
2221 | |
2222 | // This table is useful for memoization of the method above. |
2223 | typedef JitHashTable<BasicBlock*, JitPtrKeyFuncs<BasicBlock>, flowList*> BlockToFlowListMap; |
2224 | BlockToFlowListMap* m_blockToEHPreds; |
2225 | BlockToFlowListMap* GetBlockToEHPreds() |
2226 | { |
2227 | if (m_blockToEHPreds == nullptr) |
2228 | { |
2229 | m_blockToEHPreds = new (getAllocator()) BlockToFlowListMap(getAllocator()); |
2230 | } |
2231 | return m_blockToEHPreds; |
2232 | } |
2233 | |
2234 | void* ehEmitCookie(BasicBlock* block); |
2235 | UNATIVE_OFFSET ehCodeOffset(BasicBlock* block); |
2236 | |
2237 | EHblkDsc* ehInitHndRange(BasicBlock* src, IL_OFFSET* hndBeg, IL_OFFSET* hndEnd, bool* inFilter); |
2238 | |
2239 | EHblkDsc* ehInitTryRange(BasicBlock* src, IL_OFFSET* tryBeg, IL_OFFSET* tryEnd); |
2240 | |
2241 | EHblkDsc* ehInitHndBlockRange(BasicBlock* blk, BasicBlock** hndBeg, BasicBlock** hndLast, bool* inFilter); |
2242 | |
2243 | EHblkDsc* ehInitTryBlockRange(BasicBlock* blk, BasicBlock** tryBeg, BasicBlock** tryLast); |
2244 | |
2245 | void fgSetTryEnd(EHblkDsc* handlerTab, BasicBlock* newTryLast); |
2246 | |
2247 | void fgSetHndEnd(EHblkDsc* handlerTab, BasicBlock* newHndLast); |
2248 | |
2249 | void fgSkipRmvdBlocks(EHblkDsc* handlerTab); |
2250 | |
2251 | void fgAllocEHTable(); |
2252 | |
2253 | void fgRemoveEHTableEntry(unsigned XTnum); |
2254 | |
2255 | #if FEATURE_EH_FUNCLETS |
2256 | |
2257 | EHblkDsc* fgAddEHTableEntry(unsigned XTnum); |
2258 | |
2259 | #endif // FEATURE_EH_FUNCLETS |
2260 | |
2261 | #if !FEATURE_EH |
2262 | void fgRemoveEH(); |
2263 | #endif // !FEATURE_EH |
2264 | |
2265 | void fgSortEHTable(); |
2266 | |
2267 | // Causes the EH table to obey some well-formedness conditions, by inserting |
2268 | // empty BB's when necessary: |
2269 | // * No block is both the first block of a handler and the first block of a try. |
2270 | // * No block is the first block of multiple 'try' regions. |
2271 | // * No block is the last block of multiple EH regions. |
2272 | void fgNormalizeEH(); |
2273 | bool fgNormalizeEHCase1(); |
2274 | bool fgNormalizeEHCase2(); |
2275 | bool fgNormalizeEHCase3(); |
2276 | |
2277 | #ifdef DEBUG |
2278 | void dispIncomingEHClause(unsigned num, const CORINFO_EH_CLAUSE& clause); |
2279 | void dispOutgoingEHClause(unsigned num, const CORINFO_EH_CLAUSE& clause); |
2280 | void fgVerifyHandlerTab(); |
2281 | void fgDispHandlerTab(); |
2282 | #endif // DEBUG |
2283 | |
2284 | bool fgNeedToSortEHTable; |
2285 | |
2286 | void verInitEHTree(unsigned numEHClauses); |
2287 | void verInsertEhNode(CORINFO_EH_CLAUSE* clause, EHblkDsc* handlerTab); |
2288 | void verInsertEhNodeInTree(EHNodeDsc** ppRoot, EHNodeDsc* node); |
2289 | void verInsertEhNodeParent(EHNodeDsc** ppRoot, EHNodeDsc* node); |
2290 | void verCheckNestingLevel(EHNodeDsc* initRoot); |
2291 | |
2292 | /* |
2293 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
2294 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
2295 | XX XX |
2296 | XX GenTree and BasicBlock XX |
2297 | XX XX |
2298 | XX Functions to allocate and display the GenTrees and BasicBlocks XX |
2299 | XX XX |
2300 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
2301 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
2302 | */ |
2303 | |
2304 | // Functions to create nodes |
2305 | GenTreeStmt* gtNewStmt(GenTree* expr = nullptr, IL_OFFSETX offset = BAD_IL_OFFSET); |
2306 | |
2307 | // For unary opers. |
2308 | GenTree* gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1, bool doSimplifications = TRUE); |
2309 | |
2310 | // For binary opers. |
2311 | GenTree* gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1, GenTree* op2); |
2312 | |
2313 | GenTree* gtNewQmarkNode(var_types type, GenTree* cond, GenTree* colon); |
2314 | |
2315 | GenTree* gtNewLargeOperNode(genTreeOps oper, |
2316 | var_types type = TYP_I_IMPL, |
2317 | GenTree* op1 = nullptr, |
2318 | GenTree* op2 = nullptr); |
2319 | |
2320 | GenTreeIntCon* gtNewIconNode(ssize_t value, var_types type = TYP_INT); |
2321 | |
2322 | GenTree* gtNewPhysRegNode(regNumber reg, var_types type); |
2323 | |
2324 | GenTree* gtNewJmpTableNode(); |
2325 | |
2326 | GenTree* gtNewIndOfIconHandleNode(var_types indType, size_t value, unsigned iconFlags, bool isInvariant); |
2327 | |
2328 | GenTree* gtNewIconHandleNode(size_t value, unsigned flags, FieldSeqNode* fields = nullptr); |
2329 | |
2330 | unsigned gtTokenToIconFlags(unsigned token); |
2331 | |
2332 | GenTree* gtNewIconEmbHndNode(void* value, void* pValue, unsigned flags, void* compileTimeHandle); |
2333 | |
2334 | GenTree* gtNewIconEmbScpHndNode(CORINFO_MODULE_HANDLE scpHnd); |
2335 | GenTree* gtNewIconEmbClsHndNode(CORINFO_CLASS_HANDLE clsHnd); |
2336 | GenTree* gtNewIconEmbMethHndNode(CORINFO_METHOD_HANDLE methHnd); |
2337 | GenTree* gtNewIconEmbFldHndNode(CORINFO_FIELD_HANDLE fldHnd); |
2338 | |
2339 | GenTree* gtNewStringLiteralNode(InfoAccessType iat, void* pValue); |
2340 | |
2341 | GenTree* gtNewLconNode(__int64 value); |
2342 | |
2343 | GenTree* gtNewDconNode(double value); |
2344 | |
2345 | GenTree* gtNewSconNode(int CPX, CORINFO_MODULE_HANDLE scpHandle); |
2346 | |
2347 | GenTree* gtNewZeroConNode(var_types type); |
2348 | |
2349 | GenTree* gtNewOneConNode(var_types type); |
2350 | |
2351 | #ifdef FEATURE_SIMD |
2352 | GenTree* gtNewSIMDVectorZero(var_types simdType, var_types baseType, unsigned size); |
2353 | GenTree* gtNewSIMDVectorOne(var_types simdType, var_types baseType, unsigned size); |
2354 | #endif |
2355 | |
2356 | GenTree* gtNewBlkOpNode(GenTree* dst, GenTree* srcOrFillVal, unsigned size, bool isVolatile, bool isCopyBlock); |
2357 | |
2358 | GenTree* gtNewPutArgReg(var_types type, GenTree* arg, regNumber argReg); |
2359 | |
2360 | GenTree* gtNewBitCastNode(var_types type, GenTree* arg); |
2361 | |
2362 | protected: |
2363 | void gtBlockOpInit(GenTree* result, GenTree* dst, GenTree* srcOrFillVal, bool isVolatile); |
2364 | |
2365 | public: |
2366 | GenTree* gtNewObjNode(CORINFO_CLASS_HANDLE structHnd, GenTree* addr); |
2367 | void gtSetObjGcInfo(GenTreeObj* objNode); |
2368 | GenTree* gtNewStructVal(CORINFO_CLASS_HANDLE structHnd, GenTree* addr); |
2369 | GenTree* gtNewBlockVal(GenTree* addr, unsigned size); |
2370 | |
2371 | GenTree* gtNewCpObjNode(GenTree* dst, GenTree* src, CORINFO_CLASS_HANDLE structHnd, bool isVolatile); |
2372 | |
2373 | GenTreeArgList* gtNewListNode(GenTree* op1, GenTreeArgList* op2); |
2374 | |
2375 | GenTreeCall* gtNewCallNode(gtCallTypes callType, |
2376 | CORINFO_METHOD_HANDLE handle, |
2377 | var_types type, |
2378 | GenTreeArgList* args, |
2379 | IL_OFFSETX ilOffset = BAD_IL_OFFSET); |
2380 | |
2381 | GenTreeCall* gtNewIndCallNode(GenTree* addr, |
2382 | var_types type, |
2383 | GenTreeArgList* args, |
2384 | IL_OFFSETX ilOffset = BAD_IL_OFFSET); |
2385 | |
2386 | GenTreeCall* gtNewHelperCallNode(unsigned helper, var_types type, GenTreeArgList* args = nullptr); |
2387 | |
2388 | GenTree* gtNewLclvNode(unsigned lnum, var_types type, IL_OFFSETX ILoffs = BAD_IL_OFFSET); |
2389 | |
2390 | #ifdef FEATURE_SIMD |
2391 | GenTreeSIMD* gtNewSIMDNode( |
2392 | var_types type, GenTree* op1, SIMDIntrinsicID simdIntrinsicID, var_types baseType, unsigned size); |
2393 | GenTreeSIMD* gtNewSIMDNode( |
2394 | var_types type, GenTree* op1, GenTree* op2, SIMDIntrinsicID simdIntrinsicID, var_types baseType, unsigned size); |
2395 | void SetOpLclRelatedToSIMDIntrinsic(GenTree* op); |
2396 | #endif |
2397 | |
2398 | #ifdef FEATURE_HW_INTRINSICS |
2399 | GenTreeHWIntrinsic* gtNewSimdHWIntrinsicNode(var_types type, |
2400 | NamedIntrinsic hwIntrinsicID, |
2401 | var_types baseType, |
2402 | unsigned size); |
2403 | GenTreeHWIntrinsic* gtNewSimdHWIntrinsicNode( |
2404 | var_types type, GenTree* op1, NamedIntrinsic hwIntrinsicID, var_types baseType, unsigned size); |
2405 | GenTreeHWIntrinsic* gtNewSimdHWIntrinsicNode( |
2406 | var_types type, GenTree* op1, GenTree* op2, NamedIntrinsic hwIntrinsicID, var_types baseType, unsigned size); |
2407 | GenTreeHWIntrinsic* gtNewSimdHWIntrinsicNode(var_types type, |
2408 | GenTree* op1, |
2409 | GenTree* op2, |
2410 | GenTree* op3, |
2411 | NamedIntrinsic hwIntrinsicID, |
2412 | var_types baseType, |
2413 | unsigned size); |
2414 | GenTreeHWIntrinsic* gtNewSimdHWIntrinsicNode(var_types type, |
2415 | GenTree* op1, |
2416 | GenTree* op2, |
2417 | GenTree* op3, |
2418 | GenTree* op4, |
2419 | NamedIntrinsic hwIntrinsicID, |
2420 | var_types baseType, |
2421 | unsigned size); |
2422 | GenTreeHWIntrinsic* gtNewScalarHWIntrinsicNode(var_types type, GenTree* op1, NamedIntrinsic hwIntrinsicID); |
2423 | GenTreeHWIntrinsic* gtNewScalarHWIntrinsicNode(var_types type, |
2424 | GenTree* op1, |
2425 | GenTree* op2, |
2426 | NamedIntrinsic hwIntrinsicID); |
2427 | GenTreeHWIntrinsic* gtNewScalarHWIntrinsicNode( |
2428 | var_types type, GenTree* op1, GenTree* op2, GenTree* op3, NamedIntrinsic hwIntrinsicID); |
2429 | GenTree* gtNewMustThrowException(unsigned helper, var_types type, CORINFO_CLASS_HANDLE clsHnd); |
2430 | CORINFO_CLASS_HANDLE gtGetStructHandleForHWSIMD(var_types simdType, var_types simdBaseType); |
2431 | #endif // FEATURE_HW_INTRINSICS |
2432 | |
2433 | GenTree* gtNewLclLNode(unsigned lnum, var_types type, IL_OFFSETX ILoffs = BAD_IL_OFFSET); |
2434 | GenTreeLclFld* gtNewLclFldNode(unsigned lnum, var_types type, unsigned offset); |
2435 | GenTree* gtNewInlineCandidateReturnExpr(GenTree* inlineCandidate, var_types type); |
2436 | |
2437 | GenTree* gtNewCodeRef(BasicBlock* block); |
2438 | |
2439 | GenTree* gtNewFieldRef(var_types typ, CORINFO_FIELD_HANDLE fldHnd, GenTree* obj = nullptr, DWORD offset = 0); |
2440 | |
2441 | GenTree* gtNewIndexRef(var_types typ, GenTree* arrayOp, GenTree* indexOp); |
2442 | |
2443 | GenTreeArrLen* gtNewArrLen(var_types typ, GenTree* arrayOp, int lenOffset); |
2444 | |
2445 | GenTree* gtNewIndir(var_types typ, GenTree* addr); |
2446 | |
2447 | GenTreeArgList* gtNewArgList(GenTree* op); |
2448 | GenTreeArgList* gtNewArgList(GenTree* op1, GenTree* op2); |
2449 | GenTreeArgList* gtNewArgList(GenTree* op1, GenTree* op2, GenTree* op3); |
2450 | GenTreeArgList* gtNewArgList(GenTree* op1, GenTree* op2, GenTree* op3, GenTree* op4); |
2451 | |
2452 | static fgArgTabEntry* gtArgEntryByArgNum(GenTreeCall* call, unsigned argNum); |
2453 | static fgArgTabEntry* gtArgEntryByNode(GenTreeCall* call, GenTree* node); |
2454 | fgArgTabEntry* gtArgEntryByLateArgIndex(GenTreeCall* call, unsigned lateArgInx); |
2455 | static GenTree* gtArgNodeByLateArgInx(GenTreeCall* call, unsigned lateArgInx); |
2456 | bool gtArgIsThisPtr(fgArgTabEntry* argEntry); |
2457 | |
2458 | GenTree* gtNewAssignNode(GenTree* dst, GenTree* src); |
2459 | |
2460 | GenTree* gtNewTempAssign(unsigned tmp, |
2461 | GenTree* val, |
2462 | GenTree** pAfterStmt = nullptr, |
2463 | IL_OFFSETX ilOffset = BAD_IL_OFFSET, |
2464 | BasicBlock* block = nullptr); |
2465 | |
2466 | GenTree* gtNewRefCOMfield(GenTree* objPtr, |
2467 | CORINFO_RESOLVED_TOKEN* pResolvedToken, |
2468 | CORINFO_ACCESS_FLAGS access, |
2469 | CORINFO_FIELD_INFO* pFieldInfo, |
2470 | var_types lclTyp, |
2471 | CORINFO_CLASS_HANDLE structType, |
2472 | GenTree* assg); |
2473 | |
2474 | GenTree* gtNewNothingNode(); |
2475 | |
2476 | GenTree* gtNewArgPlaceHolderNode(var_types type, CORINFO_CLASS_HANDLE clsHnd); |
2477 | |
2478 | GenTree* gtUnusedValNode(GenTree* expr); |
2479 | |
2480 | GenTreeCast* gtNewCastNode(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType); |
2481 | |
2482 | GenTreeCast* gtNewCastNodeL(var_types typ, GenTree* op1, bool fromUnsigned, var_types castType); |
2483 | |
2484 | GenTreeAllocObj* gtNewAllocObjNode( |
2485 | unsigned int helper, bool helperHasSideEffects, CORINFO_CLASS_HANDLE clsHnd, var_types type, GenTree* op1); |
2486 | |
2487 | GenTreeAllocObj* gtNewAllocObjNode(CORINFO_RESOLVED_TOKEN* pResolvedToken, BOOL useParent); |
2488 | |
2489 | GenTree* gtNewRuntimeLookup(CORINFO_GENERIC_HANDLE hnd, CorInfoGenericHandleType hndTyp, GenTree* lookupTree); |
2490 | |
2491 | //------------------------------------------------------------------------ |
2492 | // Other GenTree functions |
2493 | |
2494 | GenTree* gtClone(GenTree* tree, bool complexOK = false); |
2495 | |
2496 | // If `tree` is a lclVar with lclNum `varNum`, return an IntCns with value `varVal`; otherwise, |
2497 | // create a copy of `tree`, adding specified flags, replacing uses of lclVar `deepVarNum` with |
2498 | // IntCnses with value `deepVarVal`. |
2499 | GenTree* gtCloneExpr( |
2500 | GenTree* tree, unsigned addFlags, unsigned varNum, int varVal, unsigned deepVarNum, int deepVarVal); |
2501 | |
2502 | // Create a copy of `tree`, optionally adding specifed flags, and optionally mapping uses of local |
2503 | // `varNum` to int constants with value `varVal`. |
2504 | GenTree* gtCloneExpr(GenTree* tree, unsigned addFlags = 0, unsigned varNum = BAD_VAR_NUM, int varVal = 0) |
2505 | { |
2506 | return gtCloneExpr(tree, addFlags, varNum, varVal, varNum, varVal); |
2507 | } |
2508 | |
2509 | // Internal helper for cloning a call |
2510 | GenTreeCall* gtCloneExprCallHelper(GenTreeCall* call, |
2511 | unsigned addFlags = 0, |
2512 | unsigned deepVarNum = BAD_VAR_NUM, |
2513 | int deepVarVal = 0); |
2514 | |
2515 | // Create copy of an inline or guarded devirtualization candidate tree. |
2516 | GenTreeCall* gtCloneCandidateCall(GenTreeCall* call); |
2517 | |
2518 | GenTree* gtReplaceTree(GenTree* stmt, GenTree* tree, GenTree* replacementTree); |
2519 | |
2520 | void gtUpdateSideEffects(GenTree* stmt, GenTree* tree); |
2521 | |
2522 | void (GenTree* tree); |
2523 | |
2524 | void gtUpdateStmtSideEffects(GenTree* stmt); |
2525 | |
2526 | void gtUpdateNodeSideEffects(GenTree* tree); |
2527 | |
2528 | void gtUpdateNodeOperSideEffects(GenTree* tree); |
2529 | |
2530 | // Returns "true" iff the complexity (not formally defined, but first interpretation |
2531 | // is #of nodes in subtree) of "tree" is greater than "limit". |
2532 | // (This is somewhat redundant with the "gtCostEx/gtCostSz" fields, but can be used |
2533 | // before they have been set.) |
2534 | bool gtComplexityExceeds(GenTree** tree, unsigned limit); |
2535 | |
2536 | bool gtCompareTree(GenTree* op1, GenTree* op2); |
2537 | |
2538 | GenTree* gtReverseCond(GenTree* tree); |
2539 | |
2540 | bool gtHasRef(GenTree* tree, ssize_t lclNum, bool defOnly); |
2541 | |
2542 | bool gtHasLocalsWithAddrOp(GenTree* tree); |
2543 | |
2544 | unsigned gtSetListOrder(GenTree* list, bool regs, bool isListCallArgs); |
2545 | |
2546 | void gtWalkOp(GenTree** op1, GenTree** op2, GenTree* base, bool constOnly); |
2547 | |
2548 | #ifdef DEBUG |
2549 | unsigned gtHashValue(GenTree* tree); |
2550 | |
2551 | GenTree* gtWalkOpEffectiveVal(GenTree* op); |
2552 | #endif |
2553 | |
2554 | void gtPrepareCost(GenTree* tree); |
2555 | bool gtIsLikelyRegVar(GenTree* tree); |
2556 | |
2557 | // Returns true iff the secondNode can be swapped with firstNode. |
2558 | bool gtCanSwapOrder(GenTree* firstNode, GenTree* secondNode); |
2559 | |
2560 | unsigned gtSetEvalOrder(GenTree* tree); |
2561 | |
2562 | void gtSetStmtInfo(GenTree* stmt); |
2563 | |
2564 | // Returns "true" iff "node" has any of the side effects in "flags". |
2565 | bool gtNodeHasSideEffects(GenTree* node, unsigned flags); |
2566 | |
2567 | // Returns "true" iff "tree" or its (transitive) children have any of the side effects in "flags". |
2568 | bool gtTreeHasSideEffects(GenTree* tree, unsigned flags); |
2569 | |
2570 | // Appends 'expr' in front of 'list' |
2571 | // 'list' will typically start off as 'nullptr' |
2572 | // when 'list' is non-null a GT_COMMA node is used to insert 'expr' |
2573 | GenTree* gtBuildCommaList(GenTree* list, GenTree* expr); |
2574 | |
2575 | void (GenTree* expr, |
2576 | GenTree** pList, |
2577 | unsigned flags = GTF_SIDE_EFFECT, |
2578 | bool ignoreRoot = false); |
2579 | |
2580 | GenTree* gtGetThisArg(GenTreeCall* call); |
2581 | |
2582 | // Static fields of struct types (and sometimes the types that those are reduced to) are represented by having the |
2583 | // static field contain an object pointer to the boxed struct. This simplifies the GC implementation...but |
2584 | // complicates the JIT somewhat. This predicate returns "true" iff a node with type "fieldNodeType", representing |
2585 | // the given "fldHnd", is such an object pointer. |
2586 | bool gtIsStaticFieldPtrToBoxedStruct(var_types fieldNodeType, CORINFO_FIELD_HANDLE fldHnd); |
2587 | |
2588 | // Return true if call is a recursive call; return false otherwise. |
2589 | // Note when inlining, this looks for calls back to the root method. |
2590 | bool gtIsRecursiveCall(GenTreeCall* call) |
2591 | { |
2592 | return gtIsRecursiveCall(call->gtCallMethHnd); |
2593 | } |
2594 | |
2595 | bool gtIsRecursiveCall(CORINFO_METHOD_HANDLE callMethodHandle) |
2596 | { |
2597 | return (callMethodHandle == impInlineRoot()->info.compMethodHnd); |
2598 | } |
2599 | |
2600 | //------------------------------------------------------------------------- |
2601 | |
2602 | GenTree* gtFoldExpr(GenTree* tree); |
2603 | GenTree* |
2604 | #ifdef __clang__ |
2605 | // TODO-Amd64-Unix: Remove this when the clang optimizer is fixed and/or the method implementation is |
2606 | // refactored in a simpler code. This is a workaround for a bug in the clang-3.5 optimizer. The issue is that in |
2607 | // release build the optimizer is mistyping (or just wrongly decides to use 32 bit operation for a corner case |
2608 | // of MIN_LONG) the args of the (ltemp / lval2) to int (it does a 32 bit div operation instead of 64 bit) - see |
2609 | // the implementation of the method in gentree.cpp. For the case of lval1 and lval2 equal to MIN_LONG |
2610 | // (0x8000000000000000) this results in raising a SIGFPE. The method implementation is rather complex. Disable |
2611 | // optimizations for now. |
2612 | __attribute__((optnone)) |
2613 | #endif // __clang__ |
2614 | gtFoldExprConst(GenTree* tree); |
2615 | GenTree* gtFoldExprSpecial(GenTree* tree); |
2616 | GenTree* gtFoldExprCompare(GenTree* tree); |
2617 | GenTree* gtCreateHandleCompare(genTreeOps oper, |
2618 | GenTree* op1, |
2619 | GenTree* op2, |
2620 | CorInfoInlineTypeCheck typeCheckInliningResult); |
2621 | GenTree* gtFoldExprCall(GenTreeCall* call); |
2622 | GenTree* gtFoldTypeCompare(GenTree* tree); |
2623 | GenTree* gtFoldTypeEqualityCall(CorInfoIntrinsics methodID, GenTree* op1, GenTree* op2); |
2624 | |
2625 | // Options to control behavior of gtTryRemoveBoxUpstreamEffects |
2626 | enum BoxRemovalOptions |
2627 | { |
2628 | BR_REMOVE_AND_NARROW, // remove effects, minimize remaining work, return possibly narrowed source tree |
2629 | BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE, // remove effects and minimize remaining work, return type handle tree |
2630 | BR_REMOVE_BUT_NOT_NARROW, // remove effects, return original source tree |
2631 | BR_DONT_REMOVE, // check if removal is possible, return copy source tree |
2632 | BR_DONT_REMOVE_WANT_TYPE_HANDLE, // check if removal is possible, return type handle tree |
2633 | BR_MAKE_LOCAL_COPY // revise box to copy to temp local and return local's address |
2634 | }; |
2635 | |
2636 | GenTree* gtTryRemoveBoxUpstreamEffects(GenTree* tree, BoxRemovalOptions options = BR_REMOVE_AND_NARROW); |
2637 | GenTree* gtOptimizeEnumHasFlag(GenTree* thisOp, GenTree* flagOp); |
2638 | |
2639 | //------------------------------------------------------------------------- |
2640 | // Get the handle, if any. |
2641 | CORINFO_CLASS_HANDLE gtGetStructHandleIfPresent(GenTree* tree); |
2642 | // Get the handle, and assert if not found. |
2643 | CORINFO_CLASS_HANDLE gtGetStructHandle(GenTree* tree); |
2644 | // Get the handle for a ref type. |
2645 | CORINFO_CLASS_HANDLE gtGetClassHandle(GenTree* tree, bool* pIsExact, bool* pIsNonNull); |
2646 | // Get the class handle for an helper call |
2647 | CORINFO_CLASS_HANDLE gtGetHelperCallClassHandle(GenTreeCall* call, bool* pIsExact, bool* pIsNonNull); |
2648 | // Get the element handle for an array of ref type. |
2649 | CORINFO_CLASS_HANDLE gtGetArrayElementClassHandle(GenTree* array); |
2650 | // Get a class handle from a helper call argument |
2651 | CORINFO_CLASS_HANDLE gtGetHelperArgClassHandle(GenTree* array, |
2652 | unsigned* runtimeLookupCount = nullptr, |
2653 | GenTree** handleTree = nullptr); |
2654 | // Get the class handle for a field |
2655 | CORINFO_CLASS_HANDLE gtGetFieldClassHandle(CORINFO_FIELD_HANDLE fieldHnd, bool* pIsExact, bool* pIsNonNull); |
2656 | // Check if this tree is a gc static base helper call |
2657 | bool gtIsStaticGCBaseHelperCall(GenTree* tree); |
2658 | |
2659 | //------------------------------------------------------------------------- |
2660 | // Functions to display the trees |
2661 | |
2662 | #ifdef DEBUG |
2663 | void gtDispNode(GenTree* tree, IndentStack* indentStack, __in_z const char* msg, bool isLIR); |
2664 | |
2665 | void gtDispVN(GenTree* tree); |
2666 | void gtDispConst(GenTree* tree); |
2667 | void gtDispLeaf(GenTree* tree, IndentStack* indentStack); |
2668 | void gtDispNodeName(GenTree* tree); |
2669 | void gtDispRegVal(GenTree* tree); |
2670 | |
2671 | enum IndentInfo |
2672 | { |
2673 | IINone, |
2674 | IIArc, |
2675 | IIArcTop, |
2676 | IIArcBottom, |
2677 | IIEmbedded, |
2678 | IIError, |
2679 | IndentInfoCount |
2680 | }; |
2681 | void gtDispChild(GenTree* child, |
2682 | IndentStack* indentStack, |
2683 | IndentInfo arcType, |
2684 | __in_opt const char* msg = nullptr, |
2685 | bool topOnly = false); |
2686 | void gtDispTree(GenTree* tree, |
2687 | IndentStack* indentStack = nullptr, |
2688 | __in_opt const char* msg = nullptr, |
2689 | bool topOnly = false, |
2690 | bool isLIR = false); |
2691 | void gtGetLclVarNameInfo(unsigned lclNum, const char** ilKindOut, const char** ilNameOut, unsigned* ilNumOut); |
2692 | int gtGetLclVarName(unsigned lclNum, char* buf, unsigned buf_remaining); |
2693 | char* gtGetLclVarName(unsigned lclNum); |
2694 | void gtDispLclVar(unsigned varNum, bool padForBiggestDisp = true); |
2695 | void gtDispTreeList(GenTree* tree, IndentStack* indentStack = nullptr); |
2696 | void gtGetArgMsg(GenTreeCall* call, GenTree* arg, unsigned argNum, int listCount, char* bufp, unsigned bufLength); |
2697 | void gtGetLateArgMsg(GenTreeCall* call, GenTree* arg, int argNum, int listCount, char* bufp, unsigned bufLength); |
2698 | void gtDispArgList(GenTreeCall* call, IndentStack* indentStack); |
2699 | void gtDispFieldSeq(FieldSeqNode* pfsn); |
2700 | |
2701 | void gtDispRange(LIR::ReadOnlyRange const& range); |
2702 | |
2703 | void gtDispTreeRange(LIR::Range& containingRange, GenTree* tree); |
2704 | |
2705 | void gtDispLIRNode(GenTree* node, const char* prefixMsg = nullptr); |
2706 | #endif |
2707 | |
2708 | // For tree walks |
2709 | |
2710 | enum fgWalkResult |
2711 | { |
2712 | WALK_CONTINUE, |
2713 | WALK_SKIP_SUBTREES, |
2714 | WALK_ABORT |
2715 | }; |
2716 | struct fgWalkData; |
2717 | typedef fgWalkResult(fgWalkPreFn)(GenTree** pTree, fgWalkData* data); |
2718 | typedef fgWalkResult(fgWalkPostFn)(GenTree** pTree, fgWalkData* data); |
2719 | |
2720 | #ifdef DEBUG |
2721 | static fgWalkPreFn gtAssertColonCond; |
2722 | #endif |
2723 | static fgWalkPreFn gtMarkColonCond; |
2724 | static fgWalkPreFn gtClearColonCond; |
2725 | |
2726 | GenTree** gtFindLink(GenTree* stmt, GenTree* node); |
2727 | bool gtHasCatchArg(GenTree* tree); |
2728 | |
2729 | typedef ArrayStack<GenTree*> GenTreeStack; |
2730 | |
2731 | static bool gtHasCallOnStack(GenTreeStack* parentStack); |
2732 | |
2733 | //========================================================================= |
2734 | // BasicBlock functions |
2735 | #ifdef DEBUG |
2736 | // This is a debug flag we will use to assert when creating block during codegen |
2737 | // as this interferes with procedure splitting. If you know what you're doing, set |
2738 | // it to true before creating the block. (DEBUG only) |
2739 | bool fgSafeBasicBlockCreation; |
2740 | #endif |
2741 | |
2742 | BasicBlock* bbNewBasicBlock(BBjumpKinds jumpKind); |
2743 | |
2744 | /* |
2745 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
2746 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
2747 | XX XX |
2748 | XX LclVarsInfo XX |
2749 | XX XX |
2750 | XX The variables to be used by the code generator. XX |
2751 | XX XX |
2752 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
2753 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
2754 | */ |
2755 | |
2756 | // |
2757 | // For both PROMOTION_TYPE_NONE and PROMOTION_TYPE_DEPENDENT the struct will |
2758 | // be placed in the stack frame and it's fields must be laid out sequentially. |
2759 | // |
2760 | // For PROMOTION_TYPE_INDEPENDENT each of the struct's fields is replaced by |
2761 | // a local variable that can be enregistered or placed in the stack frame. |
2762 | // The fields do not need to be laid out sequentially |
2763 | // |
2764 | enum lvaPromotionType |
2765 | { |
2766 | PROMOTION_TYPE_NONE, // The struct local is not promoted |
2767 | PROMOTION_TYPE_INDEPENDENT, // The struct local is promoted, |
2768 | // and its field locals are independent of its parent struct local. |
2769 | PROMOTION_TYPE_DEPENDENT // The struct local is promoted, |
2770 | // but its field locals depend on its parent struct local. |
2771 | }; |
2772 | |
2773 | static int __cdecl RefCntCmp(const void* op1, const void* op2); |
2774 | static int __cdecl WtdRefCntCmp(const void* op1, const void* op2); |
2775 | |
2776 | /*****************************************************************************/ |
2777 | |
2778 | enum FrameLayoutState |
2779 | { |
2780 | NO_FRAME_LAYOUT, |
2781 | INITIAL_FRAME_LAYOUT, |
2782 | PRE_REGALLOC_FRAME_LAYOUT, |
2783 | REGALLOC_FRAME_LAYOUT, |
2784 | TENTATIVE_FRAME_LAYOUT, |
2785 | FINAL_FRAME_LAYOUT |
2786 | }; |
2787 | |
2788 | public: |
2789 | RefCountState lvaRefCountState; // Current local ref count state |
2790 | |
2791 | bool lvaLocalVarRefCounted() const |
2792 | { |
2793 | return lvaRefCountState == RCS_NORMAL; |
2794 | } |
2795 | |
2796 | bool lvaTrackedFixed; // true: We cannot add new 'tracked' variable |
2797 | unsigned lvaCount; // total number of locals |
2798 | |
2799 | unsigned lvaRefCount; // total number of references to locals |
2800 | LclVarDsc* lvaTable; // variable descriptor table |
2801 | unsigned lvaTableCnt; // lvaTable size (>= lvaCount) |
2802 | |
2803 | LclVarDsc** lvaRefSorted; // table sorted by refcount |
2804 | |
2805 | unsigned short lvaTrackedCount; // actual # of locals being tracked |
2806 | unsigned lvaTrackedCountInSizeTUnits; // min # of size_t's sufficient to hold a bit for all the locals being tracked |
2807 | |
2808 | #ifdef DEBUG |
2809 | VARSET_TP lvaTrackedVars; // set of tracked variables |
2810 | #endif |
2811 | #ifndef _TARGET_64BIT_ |
2812 | VARSET_TP lvaLongVars; // set of long (64-bit) variables |
2813 | #endif |
2814 | VARSET_TP lvaFloatVars; // set of floating-point (32-bit and 64-bit) variables |
2815 | |
2816 | unsigned lvaCurEpoch; // VarSets are relative to a specific set of tracked var indices. |
2817 | // It that changes, this changes. VarSets from different epochs |
2818 | // cannot be meaningfully combined. |
2819 | |
2820 | unsigned GetCurLVEpoch() |
2821 | { |
2822 | return lvaCurEpoch; |
2823 | } |
2824 | |
2825 | // reverse map of tracked number to var number |
2826 | unsigned* lvaTrackedToVarNum; |
2827 | |
2828 | #if DOUBLE_ALIGN |
2829 | #ifdef DEBUG |
2830 | // # of procs compiled a with double-aligned stack |
2831 | static unsigned s_lvaDoubleAlignedProcsCount; |
2832 | #endif |
2833 | #endif |
2834 | |
2835 | // Getters and setters for address-exposed and do-not-enregister local var properties. |
2836 | bool lvaVarAddrExposed(unsigned varNum); |
2837 | void lvaSetVarAddrExposed(unsigned varNum); |
2838 | bool lvaVarDoNotEnregister(unsigned varNum); |
2839 | #ifdef DEBUG |
2840 | // Reasons why we can't enregister. Some of these correspond to debug properties of local vars. |
2841 | enum DoNotEnregisterReason |
2842 | { |
2843 | DNER_AddrExposed, |
2844 | DNER_IsStruct, |
2845 | DNER_LocalField, |
2846 | DNER_VMNeedsStackAddr, |
2847 | DNER_LiveInOutOfHandler, |
2848 | DNER_LiveAcrossUnmanagedCall, |
2849 | DNER_BlockOp, // Is read or written via a block operation that explicitly takes the address. |
2850 | DNER_IsStructArg, // Is a struct passed as an argument in a way that requires a stack location. |
2851 | DNER_DepField, // It is a field of a dependently promoted struct |
2852 | DNER_NoRegVars, // opts.compFlags & CLFLG_REGVAR is not set |
2853 | DNER_MinOptsGC, // It is a GC Ref and we are compiling MinOpts |
2854 | #if !defined(_TARGET_64BIT_) |
2855 | DNER_LongParamField, // It is a decomposed field of a long parameter. |
2856 | #endif |
2857 | #ifdef JIT32_GCENCODER |
2858 | DNER_PinningRef, |
2859 | #endif |
2860 | }; |
2861 | #endif |
2862 | void lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregisterReason reason)); |
2863 | |
2864 | unsigned lvaVarargsHandleArg; |
2865 | #ifdef _TARGET_X86_ |
2866 | unsigned lvaVarargsBaseOfStkArgs; // Pointer (computed based on incoming varargs handle) to the start of the stack |
2867 | // arguments |
2868 | #endif // _TARGET_X86_ |
2869 | |
2870 | unsigned lvaInlinedPInvokeFrameVar; // variable representing the InlinedCallFrame |
2871 | unsigned lvaReversePInvokeFrameVar; // variable representing the reverse PInvoke frame |
2872 | #if FEATURE_FIXED_OUT_ARGS |
2873 | unsigned lvaPInvokeFrameRegSaveVar; // variable representing the RegSave for PInvoke inlining. |
2874 | #endif |
2875 | unsigned lvaMonAcquired; // boolean variable introduced into in synchronized methods |
2876 | // that tracks whether the lock has been taken |
2877 | |
2878 | unsigned lvaArg0Var; // The lclNum of arg0. Normally this will be info.compThisArg. |
2879 | // However, if there is a "ldarga 0" or "starg 0" in the IL, |
2880 | // we will redirect all "ldarg(a) 0" and "starg 0" to this temp. |
2881 | |
2882 | unsigned lvaInlineeReturnSpillTemp; // The temp to spill the non-VOID return expression |
2883 | // in case there are multiple BBJ_RETURN blocks in the inlinee |
2884 | // or if the inlinee has GC ref locals. |
2885 | |
2886 | #if FEATURE_FIXED_OUT_ARGS |
2887 | unsigned lvaOutgoingArgSpaceVar; // dummy TYP_LCLBLK var for fixed outgoing argument space |
2888 | PhasedVar<unsigned> lvaOutgoingArgSpaceSize; // size of fixed outgoing argument space |
2889 | #endif // FEATURE_FIXED_OUT_ARGS |
2890 | |
2891 | #ifdef _TARGET_ARM_ |
2892 | // On architectures whose ABIs allow structs to be passed in registers, struct promotion will sometimes |
2893 | // require us to "rematerialize" a struct from it's separate constituent field variables. Packing several sub-word |
2894 | // field variables into an argument register is a hard problem. It's easier to reserve a word of memory into which |
2895 | // such field can be copied, after which the assembled memory word can be read into the register. We will allocate |
2896 | // this variable to be this scratch word whenever struct promotion occurs. |
2897 | unsigned lvaPromotedStructAssemblyScratchVar; |
2898 | #endif // _TARGET_ARM_ |
2899 | |
2900 | #if defined(DEBUG) && defined(_TARGET_XARCH_) |
2901 | |
2902 | unsigned lvaReturnSpCheck; // Stores SP to confirm it is not corrupted on return. |
2903 | |
2904 | #endif // defined(DEBUG) && defined(_TARGET_XARCH_) |
2905 | |
2906 | #if defined(DEBUG) && defined(_TARGET_X86_) |
2907 | |
2908 | unsigned lvaCallSpCheck; // Stores SP to confirm it is not corrupted after every call. |
2909 | |
2910 | #endif // defined(DEBUG) && defined(_TARGET_X86_) |
2911 | |
2912 | unsigned lvaGenericsContextUseCount; |
2913 | |
2914 | bool lvaKeepAliveAndReportThis(); // Synchronized instance method of a reference type, or |
2915 | // CORINFO_GENERICS_CTXT_FROM_THIS? |
2916 | bool lvaReportParamTypeArg(); // Exceptions and CORINFO_GENERICS_CTXT_FROM_PARAMTYPEARG? |
2917 | |
2918 | //------------------------------------------------------------------------- |
2919 | // All these frame offsets are inter-related and must be kept in sync |
2920 | |
2921 | #if !FEATURE_EH_FUNCLETS |
2922 | // This is used for the callable handlers |
2923 | unsigned lvaShadowSPslotsVar; // TYP_BLK variable for all the shadow SP slots |
2924 | #endif // FEATURE_EH_FUNCLETS |
2925 | |
2926 | int lvaCachedGenericContextArgOffs; |
2927 | int lvaCachedGenericContextArgOffset(); // For CORINFO_CALLCONV_PARAMTYPE and if generic context is passed as |
2928 | // THIS pointer |
2929 | |
2930 | #ifdef JIT32_GCENCODER |
2931 | |
2932 | unsigned lvaLocAllocSPvar; // variable which stores the value of ESP after the the last alloca/localloc |
2933 | |
2934 | #endif // JIT32_GCENCODER |
2935 | |
2936 | unsigned lvaNewObjArrayArgs; // variable with arguments for new MD array helper |
2937 | |
2938 | // TODO-Review: Prior to reg predict we reserve 24 bytes for Spill temps. |
2939 | // after the reg predict we will use a computed maxTmpSize |
2940 | // which is based upon the number of spill temps predicted by reg predict |
2941 | // All this is necessary because if we under-estimate the size of the spill |
2942 | // temps we could fail when encoding instructions that reference stack offsets for ARM. |
2943 | // |
2944 | // Pre codegen max spill temp size. |
2945 | static const unsigned MAX_SPILL_TEMP_SIZE = 24; |
2946 | |
2947 | //------------------------------------------------------------------------- |
2948 | |
2949 | unsigned lvaGetMaxSpillTempSize(); |
2950 | #ifdef _TARGET_ARM_ |
2951 | bool lvaIsPreSpilled(unsigned lclNum, regMaskTP preSpillMask); |
2952 | #endif // _TARGET_ARM_ |
2953 | void lvaAssignFrameOffsets(FrameLayoutState curState); |
2954 | void lvaFixVirtualFrameOffsets(); |
2955 | void lvaUpdateArgsWithInitialReg(); |
2956 | void lvaAssignVirtualFrameOffsetsToArgs(); |
2957 | #ifdef UNIX_AMD64_ABI |
2958 | int lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize, int argOffs, int* callerArgOffset); |
2959 | #else // !UNIX_AMD64_ABI |
2960 | int lvaAssignVirtualFrameOffsetToArg(unsigned lclNum, unsigned argSize, int argOffs); |
2961 | #endif // !UNIX_AMD64_ABI |
2962 | void lvaAssignVirtualFrameOffsetsToLocals(); |
2963 | int lvaAllocLocalAndSetVirtualOffset(unsigned lclNum, unsigned size, int stkOffs); |
2964 | #ifdef _TARGET_AMD64_ |
2965 | // Returns true if compCalleeRegsPushed (including RBP if used as frame pointer) is even. |
2966 | bool lvaIsCalleeSavedIntRegCountEven(); |
2967 | #endif |
2968 | void lvaAlignFrame(); |
2969 | void lvaAssignFrameOffsetsToPromotedStructs(); |
2970 | int lvaAllocateTemps(int stkOffs, bool mustDoubleAlign); |
2971 | |
2972 | #ifdef DEBUG |
2973 | void lvaDumpRegLocation(unsigned lclNum); |
2974 | void lvaDumpFrameLocation(unsigned lclNum); |
2975 | void lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t refCntWtdWidth = 6); |
2976 | void lvaTableDump(FrameLayoutState curState = NO_FRAME_LAYOUT); // NO_FRAME_LAYOUT means use the current frame |
2977 | // layout state defined by lvaDoneFrameLayout |
2978 | #endif |
2979 | |
2980 | // Limit frames size to 1GB. The maximum is 2GB in theory - make it intentionally smaller |
2981 | // to avoid bugs from borderline cases. |
2982 | #define MAX_FrameSize 0x3FFFFFFF |
2983 | void lvaIncrementFrameSize(unsigned size); |
2984 | |
2985 | unsigned lvaFrameSize(FrameLayoutState curState); |
2986 | |
2987 | // Returns the caller-SP-relative offset for the SP/FP relative offset determined by FP based. |
2988 | int lvaToCallerSPRelativeOffset(int offs, bool isFpBased); |
2989 | |
2990 | // Returns the caller-SP-relative offset for the local variable "varNum." |
2991 | int lvaGetCallerSPRelativeOffset(unsigned varNum); |
2992 | |
2993 | // Returns the SP-relative offset for the local variable "varNum". Illegal to ask this for functions with localloc. |
2994 | int lvaGetSPRelativeOffset(unsigned varNum); |
2995 | |
2996 | int lvaToInitialSPRelativeOffset(unsigned offset, bool isFpBased); |
2997 | int lvaGetInitialSPRelativeOffset(unsigned varNum); |
2998 | |
2999 | //------------------------ For splitting types ---------------------------- |
3000 | |
3001 | void lvaInitTypeRef(); |
3002 | |
3003 | void lvaInitArgs(InitVarDscInfo* varDscInfo); |
3004 | void lvaInitThisPtr(InitVarDscInfo* varDscInfo); |
3005 | void lvaInitRetBuffArg(InitVarDscInfo* varDscInfo); |
3006 | void lvaInitUserArgs(InitVarDscInfo* varDscInfo); |
3007 | void lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo); |
3008 | void lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo); |
3009 | |
3010 | void lvaInitVarDsc(LclVarDsc* varDsc, |
3011 | unsigned varNum, |
3012 | CorInfoType corInfoType, |
3013 | CORINFO_CLASS_HANDLE typeHnd, |
3014 | CORINFO_ARG_LIST_HANDLE varList, |
3015 | CORINFO_SIG_INFO* varSig); |
3016 | |
3017 | static unsigned lvaTypeRefMask(var_types type); |
3018 | |
3019 | var_types lvaGetActualType(unsigned lclNum); |
3020 | var_types lvaGetRealType(unsigned lclNum); |
3021 | |
3022 | //------------------------------------------------------------------------- |
3023 | |
3024 | void lvaInit(); |
3025 | |
3026 | LclVarDsc* lvaGetDesc(unsigned lclNum) |
3027 | { |
3028 | assert(lclNum < lvaCount); |
3029 | return &lvaTable[lclNum]; |
3030 | } |
3031 | |
3032 | LclVarDsc* lvaGetDesc(GenTreeLclVarCommon* lclVar) |
3033 | { |
3034 | assert(lclVar->GetLclNum() < lvaCount); |
3035 | return &lvaTable[lclVar->GetLclNum()]; |
3036 | } |
3037 | |
3038 | unsigned lvaLclSize(unsigned varNum); |
3039 | unsigned lvaLclExactSize(unsigned varNum); |
3040 | |
3041 | bool lvaHaveManyLocals() const; |
3042 | |
3043 | unsigned lvaGrabTemp(bool shortLifetime DEBUGARG(const char* reason)); |
3044 | unsigned lvaGrabTemps(unsigned cnt DEBUGARG(const char* reason)); |
3045 | unsigned lvaGrabTempWithImplicitUse(bool shortLifetime DEBUGARG(const char* reason)); |
3046 | |
3047 | void lvaSortOnly(); |
3048 | void lvaSortByRefCount(); |
3049 | void lvaDumpRefCounts(); |
3050 | |
3051 | void lvaMarkLocalVars(); // Local variable ref-counting |
3052 | void lvaComputeRefCounts(bool isRecompute, bool setSlotNumbers); |
3053 | void lvaMarkLocalVars(BasicBlock* block, bool isRecompute); |
3054 | |
3055 | void lvaAllocOutgoingArgSpaceVar(); // Set up lvaOutgoingArgSpaceVar |
3056 | |
3057 | VARSET_VALRET_TP lvaStmtLclMask(GenTree* stmt); |
3058 | |
3059 | #ifdef DEBUG |
3060 | struct lvaStressLclFldArgs |
3061 | { |
3062 | Compiler* m_pCompiler; |
3063 | bool m_bFirstPass; |
3064 | }; |
3065 | |
3066 | static fgWalkPreFn lvaStressLclFldCB; |
3067 | void lvaStressLclFld(); |
3068 | |
3069 | void lvaDispVarSet(VARSET_VALARG_TP set, VARSET_VALARG_TP allVars); |
3070 | void lvaDispVarSet(VARSET_VALARG_TP set); |
3071 | |
3072 | #endif |
3073 | |
3074 | #ifdef _TARGET_ARM_ |
3075 | int lvaFrameAddress(int varNum, bool mustBeFPBased, regNumber* pBaseReg, int addrModeOffset, bool isFloatUsage); |
3076 | #else |
3077 | int lvaFrameAddress(int varNum, bool* pFPbased); |
3078 | #endif |
3079 | |
3080 | bool lvaIsParameter(unsigned varNum); |
3081 | bool lvaIsRegArgument(unsigned varNum); |
3082 | BOOL lvaIsOriginalThisArg(unsigned varNum); // Is this varNum the original this argument? |
3083 | BOOL lvaIsOriginalThisReadOnly(); // return TRUE if there is no place in the code |
3084 | // that writes to arg0 |
3085 | |
3086 | // Struct parameters that are passed by reference are marked as both lvIsParam and lvIsTemp |
3087 | // (this is an overload of lvIsTemp because there are no temp parameters). |
3088 | // For x64 this is 3, 5, 6, 7, >8 byte structs that are passed by reference. |
3089 | // For ARM64, this is structs larger than 16 bytes that are passed by reference. |
3090 | bool lvaIsImplicitByRefLocal(unsigned varNum) |
3091 | { |
3092 | #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) |
3093 | LclVarDsc* varDsc = &(lvaTable[varNum]); |
3094 | if (varDsc->lvIsParam && varDsc->lvIsTemp) |
3095 | { |
3096 | assert(varTypeIsStruct(varDsc) || (varDsc->lvType == TYP_BYREF)); |
3097 | return true; |
3098 | } |
3099 | #endif // defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) |
3100 | return false; |
3101 | } |
3102 | |
3103 | // Returns true if this local var is a multireg struct |
3104 | bool lvaIsMultiregStruct(LclVarDsc* varDsc, bool isVararg); |
3105 | |
3106 | // If the local is a TYP_STRUCT, get/set a class handle describing it |
3107 | CORINFO_CLASS_HANDLE lvaGetStruct(unsigned varNum); |
3108 | void lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool unsafeValueClsCheck, bool setTypeInfo = true); |
3109 | void lvaSetStructUsedAsVarArg(unsigned varNum); |
3110 | |
3111 | // If the local is TYP_REF, set or update the associated class information. |
3112 | void lvaSetClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool isExact = false); |
3113 | void lvaSetClass(unsigned varNum, GenTree* tree, CORINFO_CLASS_HANDLE stackHandle = nullptr); |
3114 | void lvaUpdateClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool isExact = false); |
3115 | void lvaUpdateClass(unsigned varNum, GenTree* tree, CORINFO_CLASS_HANDLE stackHandle = nullptr); |
3116 | |
3117 | #define MAX_NumOfFieldsInPromotableStruct 4 // Maximum number of fields in promotable struct |
3118 | |
3119 | // Info about struct type fields. |
3120 | struct lvaStructFieldInfo |
3121 | { |
3122 | CORINFO_FIELD_HANDLE fldHnd; |
3123 | unsigned char fldOffset; |
3124 | unsigned char fldOrdinal; |
3125 | var_types fldType; |
3126 | unsigned fldSize; |
3127 | CORINFO_CLASS_HANDLE fldTypeHnd; |
3128 | |
3129 | lvaStructFieldInfo() |
3130 | : fldHnd(nullptr), fldOffset(0), fldOrdinal(0), fldType(TYP_UNDEF), fldSize(0), fldTypeHnd(nullptr) |
3131 | { |
3132 | } |
3133 | }; |
3134 | |
3135 | // Info about a struct type, instances of which may be candidates for promotion. |
3136 | struct lvaStructPromotionInfo |
3137 | { |
3138 | CORINFO_CLASS_HANDLE typeHnd; |
3139 | bool canPromote; |
3140 | bool containsHoles; |
3141 | bool customLayout; |
3142 | bool fieldsSorted; |
3143 | unsigned char fieldCnt; |
3144 | lvaStructFieldInfo fields[MAX_NumOfFieldsInPromotableStruct]; |
3145 | |
3146 | lvaStructPromotionInfo(CORINFO_CLASS_HANDLE typeHnd = nullptr) |
3147 | : typeHnd(typeHnd) |
3148 | , canPromote(false) |
3149 | , containsHoles(false) |
3150 | , customLayout(false) |
3151 | , fieldsSorted(false) |
3152 | , fieldCnt(0) |
3153 | { |
3154 | } |
3155 | }; |
3156 | |
3157 | static int __cdecl lvaFieldOffsetCmp(const void* field1, const void* field2); |
3158 | |
3159 | // This class is responsible for checking validity and profitability of struct promotion. |
3160 | // If it is both legal and profitable, then TryPromoteStructVar promotes the struct and initializes |
3161 | // nessesary information for fgMorphStructField to use. |
3162 | class StructPromotionHelper |
3163 | { |
3164 | public: |
3165 | StructPromotionHelper(Compiler* compiler); |
3166 | |
3167 | bool CanPromoteStructType(CORINFO_CLASS_HANDLE typeHnd); |
3168 | bool TryPromoteStructVar(unsigned lclNum); |
3169 | |
3170 | #ifdef DEBUG |
3171 | void CheckRetypedAsScalar(CORINFO_FIELD_HANDLE fieldHnd, var_types requestedType); |
3172 | #endif // DEBUG |
3173 | |
3174 | #ifdef _TARGET_ARM_ |
3175 | bool GetRequiresScratchVar(); |
3176 | #endif // _TARGET_ARM_ |
3177 | |
3178 | private: |
3179 | bool CanPromoteStructVar(unsigned lclNum); |
3180 | bool ShouldPromoteStructVar(unsigned lclNum); |
3181 | void PromoteStructVar(unsigned lclNum); |
3182 | void SortStructFields(); |
3183 | |
3184 | lvaStructFieldInfo GetFieldInfo(CORINFO_FIELD_HANDLE fieldHnd, BYTE ordinal); |
3185 | bool TryPromoteStructField(lvaStructFieldInfo& outerFieldInfo); |
3186 | |
3187 | private: |
3188 | Compiler* compiler; |
3189 | lvaStructPromotionInfo structPromotionInfo; |
3190 | |
3191 | #ifdef _TARGET_ARM_ |
3192 | bool requiresScratchVar; |
3193 | #endif // _TARGET_ARM_ |
3194 | |
3195 | #ifdef DEBUG |
3196 | typedef JitHashTable<CORINFO_FIELD_HANDLE, JitPtrKeyFuncs<CORINFO_FIELD_STRUCT_>, var_types> |
3197 | RetypedAsScalarFieldsMap; |
3198 | RetypedAsScalarFieldsMap retypedFieldsMap; |
3199 | #endif // DEBUG |
3200 | }; |
3201 | |
3202 | StructPromotionHelper* structPromotionHelper; |
3203 | |
3204 | #if !defined(_TARGET_64BIT_) |
3205 | void lvaPromoteLongVars(); |
3206 | #endif // !defined(_TARGET_64BIT_) |
3207 | unsigned lvaGetFieldLocal(const LclVarDsc* varDsc, unsigned int fldOffset); |
3208 | lvaPromotionType lvaGetPromotionType(const LclVarDsc* varDsc); |
3209 | lvaPromotionType lvaGetPromotionType(unsigned varNum); |
3210 | lvaPromotionType lvaGetParentPromotionType(const LclVarDsc* varDsc); |
3211 | lvaPromotionType lvaGetParentPromotionType(unsigned varNum); |
3212 | bool lvaIsFieldOfDependentlyPromotedStruct(const LclVarDsc* varDsc); |
3213 | bool lvaIsGCTracked(const LclVarDsc* varDsc); |
3214 | |
3215 | #if defined(FEATURE_SIMD) |
3216 | bool lvaMapSimd12ToSimd16(const LclVarDsc* varDsc) |
3217 | { |
3218 | assert(varDsc->lvType == TYP_SIMD12); |
3219 | assert(varDsc->lvExactSize == 12); |
3220 | |
3221 | #if defined(_TARGET_64BIT_) |
3222 | assert(varDsc->lvSize() == 16); |
3223 | #endif // defined(_TARGET_64BIT_) |
3224 | |
3225 | // We make local variable SIMD12 types 16 bytes instead of just 12. lvSize() |
3226 | // already does this calculation. However, we also need to prevent mapping types if the var is a |
3227 | // dependently promoted struct field, which must remain its exact size within its parent struct. |
3228 | // However, we don't know this until late, so we may have already pretended the field is bigger |
3229 | // before that. |
3230 | if ((varDsc->lvSize() == 16) && !lvaIsFieldOfDependentlyPromotedStruct(varDsc)) |
3231 | { |
3232 | return true; |
3233 | } |
3234 | else |
3235 | { |
3236 | return false; |
3237 | } |
3238 | } |
3239 | #endif // defined(FEATURE_SIMD) |
3240 | |
3241 | BYTE* lvaGetGcLayout(unsigned varNum); |
3242 | bool lvaTypeIsGC(unsigned varNum); |
3243 | unsigned lvaGSSecurityCookie; // LclVar number |
3244 | bool lvaTempsHaveLargerOffsetThanVars(); |
3245 | |
3246 | // Returns "true" iff local variable "lclNum" is in SSA form. |
3247 | bool lvaInSsa(unsigned lclNum) |
3248 | { |
3249 | assert(lclNum < lvaCount); |
3250 | return lvaTable[lclNum].lvInSsa; |
3251 | } |
3252 | |
3253 | unsigned lvaSecurityObject; // variable representing the security object on the stack |
3254 | unsigned lvaStubArgumentVar; // variable representing the secret stub argument coming in EAX |
3255 | |
3256 | #if FEATURE_EH_FUNCLETS |
3257 | unsigned lvaPSPSym; // variable representing the PSPSym |
3258 | #endif |
3259 | |
3260 | InlineInfo* impInlineInfo; |
3261 | InlineStrategy* m_inlineStrategy; |
3262 | |
3263 | // The Compiler* that is the root of the inlining tree of which "this" is a member. |
3264 | Compiler* impInlineRoot(); |
3265 | |
3266 | #if defined(DEBUG) || defined(INLINE_DATA) |
3267 | unsigned __int64 getInlineCycleCount() |
3268 | { |
3269 | return m_compCycles; |
3270 | } |
3271 | #endif // defined(DEBUG) || defined(INLINE_DATA) |
3272 | |
3273 | bool fgNoStructPromotion; // Set to TRUE to turn off struct promotion for this method. |
3274 | bool fgNoStructParamPromotion; // Set to TRUE to turn off struct promotion for parameters this method. |
3275 | |
3276 | //========================================================================= |
3277 | // PROTECTED |
3278 | //========================================================================= |
3279 | |
3280 | protected: |
3281 | //---------------- Local variable ref-counting ---------------------------- |
3282 | |
3283 | void lvaMarkLclRefs(GenTree* tree, BasicBlock* block, GenTreeStmt* stmt, bool isRecompute); |
3284 | bool IsDominatedByExceptionalEntry(BasicBlock* block); |
3285 | void SetVolatileHint(LclVarDsc* varDsc); |
3286 | |
3287 | // Keeps the mapping from SSA #'s to VN's for the implicit memory variables. |
3288 | SsaDefArray<SsaMemDef> ; |
3289 | |
3290 | public: |
3291 | // Returns the address of the per-Ssa data for memory at the given ssaNum (which is required |
3292 | // not to be the SsaConfig::RESERVED_SSA_NUM, which indicates that the variable is |
3293 | // not an SSA variable). |
3294 | SsaMemDef* (unsigned ssaNum) |
3295 | { |
3296 | return lvMemoryPerSsaData.GetSsaDef(ssaNum); |
3297 | } |
3298 | |
3299 | /* |
3300 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3301 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3302 | XX XX |
3303 | XX Importer XX |
3304 | XX XX |
3305 | XX Imports the given method and converts it to semantic trees XX |
3306 | XX XX |
3307 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3308 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3309 | */ |
3310 | |
3311 | public: |
3312 | void impInit(); |
3313 | |
3314 | void impImport(BasicBlock* method); |
3315 | |
3316 | CORINFO_CLASS_HANDLE impGetRefAnyClass(); |
3317 | CORINFO_CLASS_HANDLE impGetRuntimeArgumentHandle(); |
3318 | CORINFO_CLASS_HANDLE impGetTypeHandleClass(); |
3319 | CORINFO_CLASS_HANDLE impGetStringClass(); |
3320 | CORINFO_CLASS_HANDLE impGetObjectClass(); |
3321 | |
3322 | // Returns underlying type of handles returned by ldtoken instruction |
3323 | var_types GetRuntimeHandleUnderlyingType() |
3324 | { |
3325 | // RuntimeTypeHandle is backed by raw pointer on CoreRT and by object reference on other runtimes |
3326 | return IsTargetAbi(CORINFO_CORERT_ABI) ? TYP_I_IMPL : TYP_REF; |
3327 | } |
3328 | |
3329 | void impDevirtualizeCall(GenTreeCall* call, |
3330 | CORINFO_METHOD_HANDLE* method, |
3331 | unsigned* methodFlags, |
3332 | CORINFO_CONTEXT_HANDLE* contextHandle, |
3333 | CORINFO_CONTEXT_HANDLE* exactContextHandle, |
3334 | bool isLateDevirtualization); |
3335 | |
3336 | //========================================================================= |
3337 | // PROTECTED |
3338 | //========================================================================= |
3339 | |
3340 | protected: |
3341 | //-------------------- Stack manipulation --------------------------------- |
3342 | |
3343 | unsigned impStkSize; // Size of the full stack |
3344 | |
3345 | #define SMALL_STACK_SIZE 16 // number of elements in impSmallStack |
3346 | |
3347 | struct SavedStack // used to save/restore stack contents. |
3348 | { |
3349 | unsigned ssDepth; // number of values on stack |
3350 | StackEntry* ssTrees; // saved tree values |
3351 | }; |
3352 | |
3353 | bool impIsPrimitive(CorInfoType type); |
3354 | bool impILConsumesAddr(const BYTE* codeAddr, CORINFO_METHOD_HANDLE fncHandle, CORINFO_MODULE_HANDLE scpHandle); |
3355 | |
3356 | void impResolveToken(const BYTE* addr, CORINFO_RESOLVED_TOKEN* pResolvedToken, CorInfoTokenKind kind); |
3357 | |
3358 | void impPushOnStack(GenTree* tree, typeInfo ti); |
3359 | void impPushNullObjRefOnStack(); |
3360 | StackEntry impPopStack(); |
3361 | StackEntry& impStackTop(unsigned n = 0); |
3362 | unsigned impStackHeight(); |
3363 | |
3364 | void impSaveStackState(SavedStack* savePtr, bool copy); |
3365 | void impRestoreStackState(SavedStack* savePtr); |
3366 | |
3367 | GenTree* impImportLdvirtftn(GenTree* thisPtr, CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo); |
3368 | |
3369 | void impImportAndPushBox(CORINFO_RESOLVED_TOKEN* pResolvedToken); |
3370 | |
3371 | void impImportNewObjArray(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo); |
3372 | |
3373 | bool impCanPInvokeInline(); |
3374 | bool impCanPInvokeInlineCallSite(BasicBlock* block); |
3375 | void impCheckForPInvokeCall( |
3376 | GenTreeCall* call, CORINFO_METHOD_HANDLE methHnd, CORINFO_SIG_INFO* sig, unsigned mflags, BasicBlock* block); |
3377 | GenTreeCall* impImportIndirectCall(CORINFO_SIG_INFO* sig, IL_OFFSETX ilOffset = BAD_IL_OFFSET); |
3378 | void impPopArgsForUnmanagedCall(GenTree* call, CORINFO_SIG_INFO* sig); |
3379 | |
3380 | void impInsertHelperCall(CORINFO_HELPER_DESC* helperCall); |
3381 | void impHandleAccessAllowed(CorInfoIsAccessAllowedResult result, CORINFO_HELPER_DESC* helperCall); |
3382 | void impHandleAccessAllowedInternal(CorInfoIsAccessAllowedResult result, CORINFO_HELPER_DESC* helperCall); |
3383 | |
3384 | var_types impImportCall(OPCODE opcode, |
3385 | CORINFO_RESOLVED_TOKEN* pResolvedToken, |
3386 | CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, // Is this a "constrained." call on a |
3387 | // type parameter? |
3388 | GenTree* newobjThis, |
3389 | int prefixFlags, |
3390 | CORINFO_CALL_INFO* callInfo, |
3391 | IL_OFFSET rawILOffset); |
3392 | |
3393 | CORINFO_CLASS_HANDLE impGetSpecialIntrinsicExactReturnType(CORINFO_METHOD_HANDLE specialIntrinsicHandle); |
3394 | |
3395 | bool impMethodInfo_hasRetBuffArg(CORINFO_METHOD_INFO* methInfo); |
3396 | |
3397 | GenTree* impFixupCallStructReturn(GenTreeCall* call, CORINFO_CLASS_HANDLE retClsHnd); |
3398 | |
3399 | GenTree* impFixupStructReturnType(GenTree* op, CORINFO_CLASS_HANDLE retClsHnd); |
3400 | |
3401 | #ifdef DEBUG |
3402 | var_types impImportJitTestLabelMark(int numArgs); |
3403 | #endif // DEBUG |
3404 | |
3405 | GenTree* impInitClass(CORINFO_RESOLVED_TOKEN* pResolvedToken); |
3406 | |
3407 | GenTree* impImportStaticReadOnlyField(void* fldAddr, var_types lclTyp); |
3408 | |
3409 | GenTree* impImportStaticFieldAccess(CORINFO_RESOLVED_TOKEN* pResolvedToken, |
3410 | CORINFO_ACCESS_FLAGS access, |
3411 | CORINFO_FIELD_INFO* pFieldInfo, |
3412 | var_types lclTyp); |
3413 | |
3414 | static void impBashVarAddrsToI(GenTree* tree1, GenTree* tree2 = nullptr); |
3415 | |
3416 | GenTree* impImplicitIorI4Cast(GenTree* tree, var_types dstTyp); |
3417 | |
3418 | GenTree* impImplicitR4orR8Cast(GenTree* tree, var_types dstTyp); |
3419 | |
3420 | void impImportLeave(BasicBlock* block); |
3421 | void impResetLeaveBlock(BasicBlock* block, unsigned jmpAddr); |
3422 | GenTree* impIntrinsic(GenTree* newobjThis, |
3423 | CORINFO_CLASS_HANDLE clsHnd, |
3424 | CORINFO_METHOD_HANDLE method, |
3425 | CORINFO_SIG_INFO* sig, |
3426 | unsigned methodFlags, |
3427 | int memberRef, |
3428 | bool readonlyCall, |
3429 | bool tailCall, |
3430 | CORINFO_RESOLVED_TOKEN* pContstrainedResolvedToken, |
3431 | CORINFO_THIS_TRANSFORM constraintCallThisTransform, |
3432 | CorInfoIntrinsics* pIntrinsicID, |
3433 | bool* isSpecialIntrinsic = nullptr); |
3434 | GenTree* impMathIntrinsic(CORINFO_METHOD_HANDLE method, |
3435 | CORINFO_SIG_INFO* sig, |
3436 | var_types callType, |
3437 | CorInfoIntrinsics intrinsicID, |
3438 | bool tailCall); |
3439 | NamedIntrinsic lookupNamedIntrinsic(CORINFO_METHOD_HANDLE method); |
3440 | |
3441 | #ifdef FEATURE_HW_INTRINSICS |
3442 | GenTree* impBaseIntrinsic(NamedIntrinsic intrinsic, |
3443 | CORINFO_CLASS_HANDLE clsHnd, |
3444 | CORINFO_METHOD_HANDLE method, |
3445 | CORINFO_SIG_INFO* sig); |
3446 | GenTree* impHWIntrinsic(NamedIntrinsic intrinsic, |
3447 | CORINFO_METHOD_HANDLE method, |
3448 | CORINFO_SIG_INFO* sig, |
3449 | bool mustExpand); |
3450 | GenTree* impUnsupportedHWIntrinsic(unsigned helper, |
3451 | CORINFO_METHOD_HANDLE method, |
3452 | CORINFO_SIG_INFO* sig, |
3453 | bool mustExpand); |
3454 | |
3455 | protected: |
3456 | bool compSupportsHWIntrinsic(InstructionSet isa); |
3457 | |
3458 | #ifdef _TARGET_XARCH_ |
3459 | GenTree* impSSEIntrinsic(NamedIntrinsic intrinsic, |
3460 | CORINFO_METHOD_HANDLE method, |
3461 | CORINFO_SIG_INFO* sig, |
3462 | bool mustExpand); |
3463 | GenTree* impSSE2Intrinsic(NamedIntrinsic intrinsic, |
3464 | CORINFO_METHOD_HANDLE method, |
3465 | CORINFO_SIG_INFO* sig, |
3466 | bool mustExpand); |
3467 | GenTree* impSSE42Intrinsic(NamedIntrinsic intrinsic, |
3468 | CORINFO_METHOD_HANDLE method, |
3469 | CORINFO_SIG_INFO* sig, |
3470 | bool mustExpand); |
3471 | GenTree* impAvxOrAvx2Intrinsic(NamedIntrinsic intrinsic, |
3472 | CORINFO_METHOD_HANDLE method, |
3473 | CORINFO_SIG_INFO* sig, |
3474 | bool mustExpand); |
3475 | GenTree* impAESIntrinsic(NamedIntrinsic intrinsic, |
3476 | CORINFO_METHOD_HANDLE method, |
3477 | CORINFO_SIG_INFO* sig, |
3478 | bool mustExpand); |
3479 | GenTree* impBMI1OrBMI2Intrinsic(NamedIntrinsic intrinsic, |
3480 | CORINFO_METHOD_HANDLE method, |
3481 | CORINFO_SIG_INFO* sig, |
3482 | bool mustExpand); |
3483 | GenTree* impFMAIntrinsic(NamedIntrinsic intrinsic, |
3484 | CORINFO_METHOD_HANDLE method, |
3485 | CORINFO_SIG_INFO* sig, |
3486 | bool mustExpand); |
3487 | GenTree* impLZCNTIntrinsic(NamedIntrinsic intrinsic, |
3488 | CORINFO_METHOD_HANDLE method, |
3489 | CORINFO_SIG_INFO* sig, |
3490 | bool mustExpand); |
3491 | GenTree* impPCLMULQDQIntrinsic(NamedIntrinsic intrinsic, |
3492 | CORINFO_METHOD_HANDLE method, |
3493 | CORINFO_SIG_INFO* sig, |
3494 | bool mustExpand); |
3495 | GenTree* impPOPCNTIntrinsic(NamedIntrinsic intrinsic, |
3496 | CORINFO_METHOD_HANDLE method, |
3497 | CORINFO_SIG_INFO* sig, |
3498 | bool mustExpand); |
3499 | |
3500 | protected: |
3501 | GenTree* getArgForHWIntrinsic(var_types argType, CORINFO_CLASS_HANDLE argClass); |
3502 | GenTree* impNonConstFallback(NamedIntrinsic intrinsic, var_types simdType, var_types baseType); |
3503 | GenTree* addRangeCheckIfNeeded(NamedIntrinsic intrinsic, GenTree* lastOp, bool mustExpand); |
3504 | #endif // _TARGET_XARCH_ |
3505 | #ifdef _TARGET_ARM64_ |
3506 | InstructionSet lookupHWIntrinsicISA(const char* className); |
3507 | NamedIntrinsic lookupHWIntrinsic(const char* className, const char* methodName); |
3508 | bool impCheckImmediate(GenTree* immediateOp, unsigned int max); |
3509 | #endif // _TARGET_ARM64_ |
3510 | #endif // FEATURE_HW_INTRINSICS |
3511 | GenTree* impArrayAccessIntrinsic(CORINFO_CLASS_HANDLE clsHnd, |
3512 | CORINFO_SIG_INFO* sig, |
3513 | int memberRef, |
3514 | bool readonlyCall, |
3515 | CorInfoIntrinsics intrinsicID); |
3516 | GenTree* impInitializeArrayIntrinsic(CORINFO_SIG_INFO* sig); |
3517 | |
3518 | GenTree* impMethodPointer(CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_CALL_INFO* pCallInfo); |
3519 | |
3520 | GenTree* impTransformThis(GenTree* thisPtr, |
3521 | CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, |
3522 | CORINFO_THIS_TRANSFORM transform); |
3523 | |
3524 | //----------------- Manipulating the trees and stmts ---------------------- |
3525 | |
3526 | GenTree* impTreeList; // Trees for the BB being imported |
3527 | GenTree* impTreeLast; // The last tree for the current BB |
3528 | |
3529 | public: |
3530 | enum |
3531 | { |
3532 | CHECK_SPILL_ALL = -1, |
3533 | CHECK_SPILL_NONE = -2 |
3534 | }; |
3535 | |
3536 | void impBeginTreeList(); |
3537 | void impEndTreeList(BasicBlock* block, GenTree* firstStmt, GenTree* lastStmt); |
3538 | void impEndTreeList(BasicBlock* block); |
3539 | void impAppendStmtCheck(GenTree* stmt, unsigned chkLevel); |
3540 | void impAppendStmt(GenTree* stmt, unsigned chkLevel); |
3541 | void impInsertStmtBefore(GenTree* stmt, GenTree* stmtBefore); |
3542 | GenTree* impAppendTree(GenTree* tree, unsigned chkLevel, IL_OFFSETX offset); |
3543 | void impInsertTreeBefore(GenTree* tree, IL_OFFSETX offset, GenTree* stmtBefore); |
3544 | void impAssignTempGen(unsigned tmp, |
3545 | GenTree* val, |
3546 | unsigned curLevel, |
3547 | GenTree** pAfterStmt = nullptr, |
3548 | IL_OFFSETX ilOffset = BAD_IL_OFFSET, |
3549 | BasicBlock* block = nullptr); |
3550 | void impAssignTempGen(unsigned tmpNum, |
3551 | GenTree* val, |
3552 | CORINFO_CLASS_HANDLE structHnd, |
3553 | unsigned curLevel, |
3554 | GenTree** pAfterStmt = nullptr, |
3555 | IL_OFFSETX ilOffset = BAD_IL_OFFSET, |
3556 | BasicBlock* block = nullptr); |
3557 | GenTree* impCloneExpr(GenTree* tree, |
3558 | GenTree** clone, |
3559 | CORINFO_CLASS_HANDLE structHnd, |
3560 | unsigned curLevel, |
3561 | GenTree** pAfterStmt DEBUGARG(const char* reason)); |
3562 | GenTree* impAssignStruct(GenTree* dest, |
3563 | GenTree* src, |
3564 | CORINFO_CLASS_HANDLE structHnd, |
3565 | unsigned curLevel, |
3566 | GenTree** pAfterStmt = nullptr, |
3567 | IL_OFFSETX ilOffset = BAD_IL_OFFSET, |
3568 | BasicBlock* block = nullptr); |
3569 | GenTree* impAssignStructPtr(GenTree* dest, |
3570 | GenTree* src, |
3571 | CORINFO_CLASS_HANDLE structHnd, |
3572 | unsigned curLevel, |
3573 | GenTree** pAfterStmt = nullptr, |
3574 | IL_OFFSETX ilOffset = BAD_IL_OFFSET, |
3575 | BasicBlock* block = nullptr); |
3576 | |
3577 | GenTree* impGetStructAddr(GenTree* structVal, CORINFO_CLASS_HANDLE structHnd, unsigned curLevel, bool willDeref); |
3578 | |
3579 | var_types impNormStructType(CORINFO_CLASS_HANDLE structHnd, |
3580 | BYTE* gcLayout = nullptr, |
3581 | unsigned* numGCVars = nullptr, |
3582 | var_types* simdBaseType = nullptr); |
3583 | |
3584 | GenTree* impNormStructVal(GenTree* structVal, |
3585 | CORINFO_CLASS_HANDLE structHnd, |
3586 | unsigned curLevel, |
3587 | bool forceNormalization = false); |
3588 | |
3589 | GenTree* impTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken, |
3590 | BOOL* pRuntimeLookup = nullptr, |
3591 | BOOL mustRestoreHandle = FALSE, |
3592 | BOOL importParent = FALSE); |
3593 | |
3594 | GenTree* impParentClassTokenToHandle(CORINFO_RESOLVED_TOKEN* pResolvedToken, |
3595 | BOOL* pRuntimeLookup = nullptr, |
3596 | BOOL mustRestoreHandle = FALSE) |
3597 | { |
3598 | return impTokenToHandle(pResolvedToken, pRuntimeLookup, mustRestoreHandle, TRUE); |
3599 | } |
3600 | |
3601 | GenTree* impLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, |
3602 | CORINFO_LOOKUP* pLookup, |
3603 | unsigned flags, |
3604 | void* compileTimeHandle); |
3605 | |
3606 | GenTree* getRuntimeContextTree(CORINFO_RUNTIME_LOOKUP_KIND kind); |
3607 | |
3608 | GenTree* impRuntimeLookupToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, |
3609 | CORINFO_LOOKUP* pLookup, |
3610 | void* compileTimeHandle); |
3611 | |
3612 | GenTree* impReadyToRunLookupToTree(CORINFO_CONST_LOOKUP* pLookup, unsigned flags, void* compileTimeHandle); |
3613 | |
3614 | GenTreeCall* impReadyToRunHelperToTree(CORINFO_RESOLVED_TOKEN* pResolvedToken, |
3615 | CorInfoHelpFunc helper, |
3616 | var_types type, |
3617 | GenTreeArgList* arg = nullptr, |
3618 | CORINFO_LOOKUP_KIND* pGenericLookupKind = nullptr); |
3619 | |
3620 | GenTree* impCastClassOrIsInstToTree(GenTree* op1, |
3621 | GenTree* op2, |
3622 | CORINFO_RESOLVED_TOKEN* pResolvedToken, |
3623 | bool isCastClass); |
3624 | |
3625 | GenTree* impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool isCastClass); |
3626 | |
3627 | bool VarTypeIsMultiByteAndCanEnreg( |
3628 | var_types type, CORINFO_CLASS_HANDLE typeClass, unsigned* typeSize, bool forReturn, bool isVarArg); |
3629 | |
3630 | bool IsIntrinsicImplementedByUserCall(CorInfoIntrinsics intrinsicId); |
3631 | bool IsTargetIntrinsic(CorInfoIntrinsics intrinsicId); |
3632 | bool IsMathIntrinsic(CorInfoIntrinsics intrinsicId); |
3633 | bool IsMathIntrinsic(GenTree* tree); |
3634 | |
3635 | private: |
3636 | //----------------- Importing the method ---------------------------------- |
3637 | |
3638 | CORINFO_CONTEXT_HANDLE impTokenLookupContextHandle; // The context used for looking up tokens. |
3639 | |
3640 | #ifdef DEBUG |
3641 | unsigned impCurOpcOffs; |
3642 | const char* impCurOpcName; |
3643 | bool impNestedStackSpill; |
3644 | |
3645 | // For displaying instrs with generated native code (-n:B) |
3646 | GenTree* impLastILoffsStmt; // oldest stmt added for which we did not gtStmtLastILoffs |
3647 | void impNoteLastILoffs(); |
3648 | #endif |
3649 | |
3650 | /* IL offset of the stmt currently being imported. It gets set to |
3651 | BAD_IL_OFFSET after it has been set in the appended trees. Then it gets |
3652 | updated at IL offsets for which we have to report mapping info. |
3653 | It also includes flag bits, so use jitGetILoffs() |
3654 | to get the actual IL offset value. |
3655 | */ |
3656 | |
3657 | IL_OFFSETX impCurStmtOffs; |
3658 | void impCurStmtOffsSet(IL_OFFSET offs); |
3659 | |
3660 | void impNoteBranchOffs(); |
3661 | |
3662 | unsigned impInitBlockLineInfo(); |
3663 | |
3664 | GenTree* impCheckForNullPointer(GenTree* obj); |
3665 | bool impIsThis(GenTree* obj); |
3666 | bool impIsLDFTN_TOKEN(const BYTE* delegateCreateStart, const BYTE* newobjCodeAddr); |
3667 | bool impIsDUP_LDVIRTFTN_TOKEN(const BYTE* delegateCreateStart, const BYTE* newobjCodeAddr); |
3668 | bool impIsAnySTLOC(OPCODE opcode) |
3669 | { |
3670 | return ((opcode == CEE_STLOC) || (opcode == CEE_STLOC_S) || |
3671 | ((opcode >= CEE_STLOC_0) && (opcode <= CEE_STLOC_3))); |
3672 | } |
3673 | |
3674 | GenTreeArgList* impPopList(unsigned count, CORINFO_SIG_INFO* sig, GenTreeArgList* prefixTree = nullptr); |
3675 | |
3676 | GenTreeArgList* impPopRevList(unsigned count, CORINFO_SIG_INFO* sig, unsigned skipReverseCount = 0); |
3677 | |
3678 | /* |
3679 | * Get current IL offset with stack-empty info incoporated |
3680 | */ |
3681 | IL_OFFSETX impCurILOffset(IL_OFFSET offs, bool callInstruction = false); |
3682 | |
3683 | //---------------- Spilling the importer stack ---------------------------- |
3684 | |
3685 | // The maximum number of bytes of IL processed without clean stack state. |
3686 | // It allows to limit the maximum tree size and depth. |
3687 | static const unsigned MAX_TREE_SIZE = 200; |
3688 | bool impCanSpillNow(OPCODE prevOpcode); |
3689 | |
3690 | struct PendingDsc |
3691 | { |
3692 | PendingDsc* pdNext; |
3693 | BasicBlock* pdBB; |
3694 | SavedStack pdSavedStack; |
3695 | ThisInitState pdThisPtrInit; |
3696 | }; |
3697 | |
3698 | PendingDsc* impPendingList; // list of BBs currently waiting to be imported. |
3699 | PendingDsc* impPendingFree; // Freed up dscs that can be reused |
3700 | |
3701 | // We keep a byte-per-block map (dynamically extended) in the top-level Compiler object of a compilation. |
3702 | JitExpandArray<BYTE> impPendingBlockMembers; |
3703 | |
3704 | // Return the byte for "b" (allocating/extending impPendingBlockMembers if necessary.) |
3705 | // Operates on the map in the top-level ancestor. |
3706 | BYTE impGetPendingBlockMember(BasicBlock* blk) |
3707 | { |
3708 | return impInlineRoot()->impPendingBlockMembers.Get(blk->bbInd()); |
3709 | } |
3710 | |
3711 | // Set the byte for "b" to "val" (allocating/extending impPendingBlockMembers if necessary.) |
3712 | // Operates on the map in the top-level ancestor. |
3713 | void impSetPendingBlockMember(BasicBlock* blk, BYTE val) |
3714 | { |
3715 | impInlineRoot()->impPendingBlockMembers.Set(blk->bbInd(), val); |
3716 | } |
3717 | |
3718 | bool impCanReimport; |
3719 | |
3720 | bool impSpillStackEntry(unsigned level, |
3721 | unsigned varNum |
3722 | #ifdef DEBUG |
3723 | , |
3724 | bool bAssertOnRecursion, |
3725 | const char* reason |
3726 | #endif |
3727 | ); |
3728 | |
3729 | void impSpillStackEnsure(bool spillLeaves = false); |
3730 | void impEvalSideEffects(); |
3731 | void impSpillSpecialSideEff(); |
3732 | void impSpillSideEffects(bool spillGlobEffects, unsigned chkLevel DEBUGARG(const char* reason)); |
3733 | void impSpillValueClasses(); |
3734 | void impSpillEvalStack(); |
3735 | static fgWalkPreFn impFindValueClasses; |
3736 | void impSpillLclRefs(ssize_t lclNum); |
3737 | |
3738 | BasicBlock* impPushCatchArgOnStack(BasicBlock* hndBlk, CORINFO_CLASS_HANDLE clsHnd, bool isSingleBlockFilter); |
3739 | |
3740 | void impImportBlockCode(BasicBlock* block); |
3741 | |
3742 | void impReimportMarkBlock(BasicBlock* block); |
3743 | void impReimportMarkSuccessors(BasicBlock* block); |
3744 | |
3745 | void impVerifyEHBlock(BasicBlock* block, bool isTryStart); |
3746 | |
3747 | void impImportBlockPending(BasicBlock* block); |
3748 | |
3749 | // Similar to impImportBlockPending, but assumes that block has already been imported once and is being |
3750 | // reimported for some reason. It specifically does *not* look at verCurrentState to set the EntryState |
3751 | // for the block, but instead, just re-uses the block's existing EntryState. |
3752 | void impReimportBlockPending(BasicBlock* block); |
3753 | |
3754 | var_types impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTree** pOp1, GenTree** pOp2); |
3755 | |
3756 | void impImportBlock(BasicBlock* block); |
3757 | |
3758 | // Assumes that "block" is a basic block that completes with a non-empty stack. We will assign the values |
3759 | // on the stack to local variables (the "spill temp" variables). The successor blocks will assume that |
3760 | // its incoming stack contents are in those locals. This requires "block" and its successors to agree on |
3761 | // the variables that will be used -- and for all the predecessors of those successors, and the |
3762 | // successors of those predecessors, etc. Call such a set of blocks closed under alternating |
3763 | // successor/predecessor edges a "spill clique." A block is a "predecessor" or "successor" member of the |
3764 | // clique (or, conceivably, both). Each block has a specified sequence of incoming and outgoing spill |
3765 | // temps. If "block" already has its outgoing spill temps assigned (they are always a contiguous series |
3766 | // of local variable numbers, so we represent them with the base local variable number), returns that. |
3767 | // Otherwise, picks a set of spill temps, and propagates this choice to all blocks in the spill clique of |
3768 | // which "block" is a member (asserting, in debug mode, that no block in this clique had its spill temps |
3769 | // chosen already. More precisely, that the incoming or outgoing spill temps are not chosen, depending |
3770 | // on which kind of member of the clique the block is). |
3771 | unsigned impGetSpillTmpBase(BasicBlock* block); |
3772 | |
3773 | // Assumes that "block" is a basic block that completes with a non-empty stack. We have previously |
3774 | // assigned the values on the stack to local variables (the "spill temp" variables). The successor blocks |
3775 | // will assume that its incoming stack contents are in those locals. This requires "block" and its |
3776 | // successors to agree on the variables and their types that will be used. The CLI spec allows implicit |
3777 | // conversions between 'int' and 'native int' or 'float' and 'double' stack types. So one predecessor can |
3778 | // push an int and another can push a native int. For 64-bit we have chosen to implement this by typing |
3779 | // the "spill temp" as native int, and then importing (or re-importing as needed) so that all the |
3780 | // predecessors in the "spill clique" push a native int (sign-extending if needed), and all the |
3781 | // successors receive a native int. Similarly float and double are unified to double. |
3782 | // This routine is called after a type-mismatch is detected, and it will walk the spill clique to mark |
3783 | // blocks for re-importation as appropriate (both successors, so they get the right incoming type, and |
3784 | // predecessors, so they insert an upcast if needed). |
3785 | void impReimportSpillClique(BasicBlock* block); |
3786 | |
3787 | // When we compute a "spill clique" (see above) these byte-maps are allocated to have a byte per basic |
3788 | // block, and represent the predecessor and successor members of the clique currently being computed. |
3789 | // *** Access to these will need to be locked in a parallel compiler. |
3790 | JitExpandArray<BYTE> impSpillCliquePredMembers; |
3791 | JitExpandArray<BYTE> impSpillCliqueSuccMembers; |
3792 | |
3793 | enum SpillCliqueDir |
3794 | { |
3795 | SpillCliquePred, |
3796 | SpillCliqueSucc |
3797 | }; |
3798 | |
3799 | // Abstract class for receiving a callback while walking a spill clique |
3800 | class SpillCliqueWalker |
3801 | { |
3802 | public: |
3803 | virtual void Visit(SpillCliqueDir predOrSucc, BasicBlock* blk) = 0; |
3804 | }; |
3805 | |
3806 | // This class is used for setting the bbStkTempsIn and bbStkTempsOut on the blocks within a spill clique |
3807 | class SetSpillTempsBase : public SpillCliqueWalker |
3808 | { |
3809 | unsigned m_baseTmp; |
3810 | |
3811 | public: |
3812 | SetSpillTempsBase(unsigned baseTmp) : m_baseTmp(baseTmp) |
3813 | { |
3814 | } |
3815 | virtual void Visit(SpillCliqueDir predOrSucc, BasicBlock* blk); |
3816 | }; |
3817 | |
3818 | // This class is used for implementing impReimportSpillClique part on each block within the spill clique |
3819 | class ReimportSpillClique : public SpillCliqueWalker |
3820 | { |
3821 | Compiler* m_pComp; |
3822 | |
3823 | public: |
3824 | ReimportSpillClique(Compiler* pComp) : m_pComp(pComp) |
3825 | { |
3826 | } |
3827 | virtual void Visit(SpillCliqueDir predOrSucc, BasicBlock* blk); |
3828 | }; |
3829 | |
3830 | // This is the heart of the algorithm for walking spill cliques. It invokes callback->Visit for each |
3831 | // predecessor or successor within the spill clique |
3832 | void impWalkSpillCliqueFromPred(BasicBlock* pred, SpillCliqueWalker* callback); |
3833 | |
3834 | // For a BasicBlock that has already been imported, the EntryState has an array of GenTrees for the |
3835 | // incoming locals. This walks that list an resets the types of the GenTrees to match the types of |
3836 | // the VarDscs. They get out of sync when we have int/native int issues (see impReimportSpillClique). |
3837 | void impRetypeEntryStateTemps(BasicBlock* blk); |
3838 | |
3839 | BYTE impSpillCliqueGetMember(SpillCliqueDir predOrSucc, BasicBlock* blk); |
3840 | void impSpillCliqueSetMember(SpillCliqueDir predOrSucc, BasicBlock* blk, BYTE val); |
3841 | |
3842 | void impPushVar(GenTree* op, typeInfo tiRetVal); |
3843 | void impLoadVar(unsigned lclNum, IL_OFFSET offset, typeInfo tiRetVal); |
3844 | void impLoadVar(unsigned lclNum, IL_OFFSET offset) |
3845 | { |
3846 | impLoadVar(lclNum, offset, lvaTable[lclNum].lvVerTypeInfo); |
3847 | } |
3848 | void impLoadArg(unsigned ilArgNum, IL_OFFSET offset); |
3849 | void impLoadLoc(unsigned ilLclNum, IL_OFFSET offset); |
3850 | bool impReturnInstruction(BasicBlock* block, int prefixFlags, OPCODE& opcode); |
3851 | |
3852 | #ifdef _TARGET_ARM_ |
3853 | void impMarkLclDstNotPromotable(unsigned tmpNum, GenTree* op, CORINFO_CLASS_HANDLE hClass); |
3854 | #endif |
3855 | |
3856 | // A free list of linked list nodes used to represent to-do stacks of basic blocks. |
3857 | struct BlockListNode |
3858 | { |
3859 | BasicBlock* m_blk; |
3860 | BlockListNode* m_next; |
3861 | BlockListNode(BasicBlock* blk, BlockListNode* next = nullptr) : m_blk(blk), m_next(next) |
3862 | { |
3863 | } |
3864 | void* operator new(size_t sz, Compiler* comp); |
3865 | }; |
3866 | BlockListNode* impBlockListNodeFreeList; |
3867 | |
3868 | void FreeBlockListNode(BlockListNode* node); |
3869 | |
3870 | bool impIsValueType(typeInfo* pTypeInfo); |
3871 | var_types mangleVarArgsType(var_types type); |
3872 | |
3873 | #if FEATURE_VARARG |
3874 | regNumber getCallArgIntRegister(regNumber floatReg); |
3875 | regNumber getCallArgFloatRegister(regNumber intReg); |
3876 | #endif // FEATURE_VARARG |
3877 | |
3878 | #if defined(DEBUG) |
3879 | static unsigned jitTotalMethodCompiled; |
3880 | #endif |
3881 | |
3882 | #ifdef DEBUG |
3883 | static LONG jitNestingLevel; |
3884 | #endif // DEBUG |
3885 | |
3886 | static BOOL impIsAddressInLocal(GenTree* tree, GenTree** lclVarTreeOut); |
3887 | |
3888 | void impMakeDiscretionaryInlineObservations(InlineInfo* pInlineInfo, InlineResult* inlineResult); |
3889 | |
3890 | // STATIC inlining decision based on the IL code. |
3891 | void impCanInlineIL(CORINFO_METHOD_HANDLE fncHandle, |
3892 | CORINFO_METHOD_INFO* methInfo, |
3893 | bool forceInline, |
3894 | InlineResult* inlineResult); |
3895 | |
3896 | void impCheckCanInline(GenTreeCall* call, |
3897 | CORINFO_METHOD_HANDLE fncHandle, |
3898 | unsigned methAttr, |
3899 | CORINFO_CONTEXT_HANDLE exactContextHnd, |
3900 | InlineCandidateInfo** ppInlineCandidateInfo, |
3901 | InlineResult* inlineResult); |
3902 | |
3903 | void impInlineRecordArgInfo(InlineInfo* pInlineInfo, |
3904 | GenTree* curArgVal, |
3905 | unsigned argNum, |
3906 | InlineResult* inlineResult); |
3907 | |
3908 | void impInlineInitVars(InlineInfo* pInlineInfo); |
3909 | |
3910 | unsigned impInlineFetchLocal(unsigned lclNum DEBUGARG(const char* reason)); |
3911 | |
3912 | GenTree* impInlineFetchArg(unsigned lclNum, InlArgInfo* inlArgInfo, InlLclVarInfo* lclTypeInfo); |
3913 | |
3914 | BOOL impInlineIsThis(GenTree* tree, InlArgInfo* inlArgInfo); |
3915 | |
3916 | BOOL impInlineIsGuaranteedThisDerefBeforeAnySideEffects(GenTree* additionalTreesToBeEvaluatedBefore, |
3917 | GenTree* variableBeingDereferenced, |
3918 | InlArgInfo* inlArgInfo); |
3919 | |
3920 | void impMarkInlineCandidate(GenTree* call, |
3921 | CORINFO_CONTEXT_HANDLE exactContextHnd, |
3922 | bool exactContextNeedsRuntimeLookup, |
3923 | CORINFO_CALL_INFO* callInfo); |
3924 | |
3925 | void impMarkInlineCandidateHelper(GenTreeCall* call, |
3926 | CORINFO_CONTEXT_HANDLE exactContextHnd, |
3927 | bool exactContextNeedsRuntimeLookup, |
3928 | CORINFO_CALL_INFO* callInfo); |
3929 | |
3930 | bool impTailCallRetTypeCompatible(var_types callerRetType, |
3931 | CORINFO_CLASS_HANDLE callerRetTypeClass, |
3932 | var_types calleeRetType, |
3933 | CORINFO_CLASS_HANDLE calleeRetTypeClass); |
3934 | |
3935 | bool impIsTailCallILPattern(bool tailPrefixed, |
3936 | OPCODE curOpcode, |
3937 | const BYTE* codeAddrOfNextOpcode, |
3938 | const BYTE* codeEnd, |
3939 | bool isRecursive, |
3940 | bool* IsCallPopRet = nullptr); |
3941 | |
3942 | bool impIsImplicitTailCallCandidate( |
3943 | OPCODE curOpcode, const BYTE* codeAddrOfNextOpcode, const BYTE* codeEnd, int prefixFlags, bool isRecursive); |
3944 | |
3945 | CORINFO_RESOLVED_TOKEN* impAllocateToken(CORINFO_RESOLVED_TOKEN token); |
3946 | |
3947 | /* |
3948 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3949 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3950 | XX XX |
3951 | XX FlowGraph XX |
3952 | XX XX |
3953 | XX Info about the basic-blocks, their contents and the flow analysis XX |
3954 | XX XX |
3955 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3956 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3957 | */ |
3958 | |
3959 | public: |
3960 | BasicBlock* fgFirstBB; // Beginning of the basic block list |
3961 | BasicBlock* fgLastBB; // End of the basic block list |
3962 | BasicBlock* fgFirstColdBlock; // First block to be placed in the cold section |
3963 | #if FEATURE_EH_FUNCLETS |
3964 | BasicBlock* fgFirstFuncletBB; // First block of outlined funclets (to allow block insertion before the funclets) |
3965 | #endif |
3966 | BasicBlock* fgFirstBBScratch; // Block inserted for initialization stuff. Is nullptr if no such block has been |
3967 | // created. |
3968 | BasicBlockList* fgReturnBlocks; // list of BBJ_RETURN blocks |
3969 | unsigned fgEdgeCount; // # of control flow edges between the BBs |
3970 | unsigned fgBBcount; // # of BBs in the method |
3971 | #ifdef DEBUG |
3972 | unsigned fgBBcountAtCodegen; // # of BBs in the method at the start of codegen |
3973 | #endif |
3974 | unsigned fgBBNumMax; // The max bbNum that has been assigned to basic blocks |
3975 | unsigned fgDomBBcount; // # of BBs for which we have dominator and reachability information |
3976 | BasicBlock** fgBBInvPostOrder; // The flow graph stored in an array sorted in topological order, needed to compute |
3977 | // dominance. Indexed by block number. Size: fgBBNumMax + 1. |
3978 | |
3979 | // After the dominance tree is computed, we cache a DFS preorder number and DFS postorder number to compute |
3980 | // dominance queries in O(1). fgDomTreePreOrder and fgDomTreePostOrder are arrays giving the block's preorder and |
3981 | // postorder number, respectively. The arrays are indexed by basic block number. (Note that blocks are numbered |
3982 | // starting from one. Thus, we always waste element zero. This makes debugging easier and makes the code less likely |
3983 | // to suffer from bugs stemming from forgetting to add or subtract one from the block number to form an array |
3984 | // index). The arrays are of size fgBBNumMax + 1. |
3985 | unsigned* fgDomTreePreOrder; |
3986 | unsigned* fgDomTreePostOrder; |
3987 | |
3988 | bool fgBBVarSetsInited; |
3989 | |
3990 | // Allocate array like T* a = new T[fgBBNumMax + 1]; |
3991 | // Using helper so we don't keep forgetting +1. |
3992 | template <typename T> |
3993 | T* fgAllocateTypeForEachBlk(CompMemKind cmk = CMK_Unknown) |
3994 | { |
3995 | return getAllocator(cmk).allocate<T>(fgBBNumMax + 1); |
3996 | } |
3997 | |
3998 | // BlockSets are relative to a specific set of BasicBlock numbers. If that changes |
3999 | // (if the blocks are renumbered), this changes. BlockSets from different epochs |
4000 | // cannot be meaningfully combined. Note that new blocks can be created with higher |
4001 | // block numbers without changing the basic block epoch. These blocks *cannot* |
4002 | // participate in a block set until the blocks are all renumbered, causing the epoch |
4003 | // to change. This is useful if continuing to use previous block sets is valuable. |
4004 | // If the epoch is zero, then it is uninitialized, and block sets can't be used. |
4005 | unsigned fgCurBBEpoch; |
4006 | |
4007 | unsigned GetCurBasicBlockEpoch() |
4008 | { |
4009 | return fgCurBBEpoch; |
4010 | } |
4011 | |
4012 | // The number of basic blocks in the current epoch. When the blocks are renumbered, |
4013 | // this is fgBBcount. As blocks are added, fgBBcount increases, fgCurBBEpochSize remains |
4014 | // the same, until a new BasicBlock epoch is created, such as when the blocks are all renumbered. |
4015 | unsigned fgCurBBEpochSize; |
4016 | |
4017 | // The number of "size_t" elements required to hold a bitset large enough for fgCurBBEpochSize |
4018 | // bits. This is precomputed to avoid doing math every time BasicBlockBitSetTraits::GetArrSize() is called. |
4019 | unsigned fgBBSetCountInSizeTUnits; |
4020 | |
4021 | void NewBasicBlockEpoch() |
4022 | { |
4023 | INDEBUG(unsigned oldEpochArrSize = fgBBSetCountInSizeTUnits); |
4024 | |
4025 | // We have a new epoch. Compute and cache the size needed for new BlockSets. |
4026 | fgCurBBEpoch++; |
4027 | fgCurBBEpochSize = fgBBNumMax + 1; |
4028 | fgBBSetCountInSizeTUnits = |
4029 | roundUp(fgCurBBEpochSize, (unsigned)(sizeof(size_t) * 8)) / unsigned(sizeof(size_t) * 8); |
4030 | |
4031 | #ifdef DEBUG |
4032 | // All BlockSet objects are now invalid! |
4033 | fgReachabilitySetsValid = false; // the bbReach sets are now invalid! |
4034 | fgEnterBlksSetValid = false; // the fgEnterBlks set is now invalid! |
4035 | |
4036 | if (verbose) |
4037 | { |
4038 | unsigned epochArrSize = BasicBlockBitSetTraits::GetArrSize(this, sizeof(size_t)); |
4039 | printf("\nNew BlockSet epoch %d, # of blocks (including unused BB00): %u, bitset array size: %u (%s)" , |
4040 | fgCurBBEpoch, fgCurBBEpochSize, epochArrSize, (epochArrSize <= 1) ? "short" : "long" ); |
4041 | if ((fgCurBBEpoch != 1) && ((oldEpochArrSize <= 1) != (epochArrSize <= 1))) |
4042 | { |
4043 | // If we're not just establishing the first epoch, and the epoch array size has changed such that we're |
4044 | // going to change our bitset representation from short (just a size_t bitset) to long (a pointer to an |
4045 | // array of size_t bitsets), then print that out. |
4046 | printf("; NOTE: BlockSet size was previously %s!" , (oldEpochArrSize <= 1) ? "short" : "long" ); |
4047 | } |
4048 | printf("\n" ); |
4049 | } |
4050 | #endif // DEBUG |
4051 | } |
4052 | |
4053 | void EnsureBasicBlockEpoch() |
4054 | { |
4055 | if (fgCurBBEpochSize != fgBBNumMax + 1) |
4056 | { |
4057 | NewBasicBlockEpoch(); |
4058 | } |
4059 | } |
4060 | |
4061 | BasicBlock* fgNewBasicBlock(BBjumpKinds jumpKind); |
4062 | void fgEnsureFirstBBisScratch(); |
4063 | bool fgFirstBBisScratch(); |
4064 | bool fgBBisScratch(BasicBlock* block); |
4065 | |
4066 | void fgExtendEHRegionBefore(BasicBlock* block); |
4067 | void fgExtendEHRegionAfter(BasicBlock* block); |
4068 | |
4069 | BasicBlock* fgNewBBbefore(BBjumpKinds jumpKind, BasicBlock* block, bool extendRegion); |
4070 | |
4071 | BasicBlock* fgNewBBafter(BBjumpKinds jumpKind, BasicBlock* block, bool extendRegion); |
4072 | |
4073 | BasicBlock* fgNewBBinRegion(BBjumpKinds jumpKind, |
4074 | unsigned tryIndex, |
4075 | unsigned hndIndex, |
4076 | BasicBlock* nearBlk, |
4077 | bool putInFilter = false, |
4078 | bool runRarely = false, |
4079 | bool insertAtEnd = false); |
4080 | |
4081 | BasicBlock* fgNewBBinRegion(BBjumpKinds jumpKind, |
4082 | BasicBlock* srcBlk, |
4083 | bool runRarely = false, |
4084 | bool insertAtEnd = false); |
4085 | |
4086 | BasicBlock* fgNewBBinRegion(BBjumpKinds jumpKind); |
4087 | |
4088 | BasicBlock* fgNewBBinRegionWorker(BBjumpKinds jumpKind, |
4089 | BasicBlock* afterBlk, |
4090 | unsigned xcptnIndex, |
4091 | bool putInTryRegion); |
4092 | |
4093 | void fgInsertBBbefore(BasicBlock* insertBeforeBlk, BasicBlock* newBlk); |
4094 | void fgInsertBBafter(BasicBlock* insertAfterBlk, BasicBlock* newBlk); |
4095 | void fgUnlinkBlock(BasicBlock* block); |
4096 | |
4097 | unsigned fgMeasureIR(); |
4098 | |
4099 | bool fgModified; // True if the flow graph has been modified recently |
4100 | bool fgComputePredsDone; // Have we computed the bbPreds list |
4101 | bool fgCheapPredsValid; // Is the bbCheapPreds list valid? |
4102 | bool fgDomsComputed; // Have we computed the dominator sets? |
4103 | bool fgOptimizedFinally; // Did we optimize any try-finallys? |
4104 | |
4105 | bool fgHasSwitch; // any BBJ_SWITCH jumps? |
4106 | |
4107 | BlockSet fgEnterBlks; // Set of blocks which have a special transfer of control; the "entry" blocks plus EH handler |
4108 | // begin blocks. |
4109 | |
4110 | #ifdef DEBUG |
4111 | bool fgReachabilitySetsValid; // Are the bbReach sets valid? |
4112 | bool fgEnterBlksSetValid; // Is the fgEnterBlks set valid? |
4113 | #endif // DEBUG |
4114 | |
4115 | bool fgRemoveRestOfBlock; // true if we know that we will throw |
4116 | bool fgStmtRemoved; // true if we remove statements -> need new DFA |
4117 | |
4118 | // There are two modes for ordering of the trees. |
4119 | // - In FGOrderTree, the dominant ordering is the tree order, and the nodes contained in |
4120 | // each tree and sub-tree are contiguous, and can be traversed (in gtNext/gtPrev order) |
4121 | // by traversing the tree according to the order of the operands. |
4122 | // - In FGOrderLinear, the dominant ordering is the linear order. |
4123 | |
4124 | enum FlowGraphOrder |
4125 | { |
4126 | FGOrderTree, |
4127 | FGOrderLinear |
4128 | }; |
4129 | FlowGraphOrder fgOrder; |
4130 | |
4131 | // The following are boolean flags that keep track of the state of internal data structures |
4132 | |
4133 | bool fgStmtListThreaded; // true if the node list is now threaded |
4134 | bool fgCanRelocateEHRegions; // true if we are allowed to relocate the EH regions |
4135 | bool fgEdgeWeightsComputed; // true after we have called fgComputeEdgeWeights |
4136 | bool fgHaveValidEdgeWeights; // true if we were successful in computing all of the edge weights |
4137 | bool fgSlopUsedInEdgeWeights; // true if their was some slop used when computing the edge weights |
4138 | bool fgRangeUsedInEdgeWeights; // true if some of the edgeWeight are expressed in Min..Max form |
4139 | bool fgNeedsUpdateFlowGraph; // true if we need to run fgUpdateFlowGraph |
4140 | BasicBlock::weight_t fgCalledCount; // count of the number of times this method was called |
4141 | // This is derived from the profile data |
4142 | // or is BB_UNITY_WEIGHT when we don't have profile data |
4143 | |
4144 | #if FEATURE_EH_FUNCLETS |
4145 | bool fgFuncletsCreated; // true if the funclet creation phase has been run |
4146 | #endif // FEATURE_EH_FUNCLETS |
4147 | |
4148 | bool fgGlobalMorph; // indicates if we are during the global morphing phase |
4149 | // since fgMorphTree can be called from several places |
4150 | |
4151 | bool impBoxTempInUse; // the temp below is valid and available |
4152 | unsigned impBoxTemp; // a temporary that is used for boxing |
4153 | |
4154 | #ifdef DEBUG |
4155 | bool jitFallbackCompile; // Are we doing a fallback compile? That is, have we executed a NO_WAY assert, |
4156 | // and we are trying to compile again in a "safer", minopts mode? |
4157 | #endif |
4158 | |
4159 | #if defined(DEBUG) |
4160 | unsigned impInlinedCodeSize; |
4161 | #endif |
4162 | |
4163 | //------------------------------------------------------------------------- |
4164 | |
4165 | void fgInit(); |
4166 | |
4167 | void fgImport(); |
4168 | |
4169 | void fgTransformIndirectCalls(); |
4170 | |
4171 | void fgInline(); |
4172 | |
4173 | void fgRemoveEmptyTry(); |
4174 | |
4175 | void fgRemoveEmptyFinally(); |
4176 | |
4177 | void fgMergeFinallyChains(); |
4178 | |
4179 | void fgCloneFinally(); |
4180 | |
4181 | void fgCleanupContinuation(BasicBlock* continuation); |
4182 | |
4183 | void fgUpdateFinallyTargetFlags(); |
4184 | |
4185 | void fgClearAllFinallyTargetBits(); |
4186 | |
4187 | void fgAddFinallyTargetFlags(); |
4188 | |
4189 | #if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_) |
4190 | // Sometimes we need to defer updating the BBF_FINALLY_TARGET bit. fgNeedToAddFinallyTargetBits signals |
4191 | // when this is necessary. |
4192 | bool fgNeedToAddFinallyTargetBits; |
4193 | #endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_) |
4194 | |
4195 | bool fgRetargetBranchesToCanonicalCallFinally(BasicBlock* block, |
4196 | BasicBlock* handler, |
4197 | BlockToBlockMap& continuationMap); |
4198 | |
4199 | GenTree* fgGetCritSectOfStaticMethod(); |
4200 | |
4201 | #if FEATURE_EH_FUNCLETS |
4202 | |
4203 | void fgAddSyncMethodEnterExit(); |
4204 | |
4205 | GenTree* fgCreateMonitorTree(unsigned lvaMonitorBool, unsigned lvaThisVar, BasicBlock* block, bool enter); |
4206 | |
4207 | void fgConvertSyncReturnToLeave(BasicBlock* block); |
4208 | |
4209 | #endif // FEATURE_EH_FUNCLETS |
4210 | |
4211 | void fgAddReversePInvokeEnterExit(); |
4212 | |
4213 | bool fgMoreThanOneReturnBlock(); |
4214 | |
4215 | // The number of separate return points in the method. |
4216 | unsigned fgReturnCount; |
4217 | |
4218 | void fgAddInternal(); |
4219 | |
4220 | bool fgFoldConditional(BasicBlock* block); |
4221 | |
4222 | void fgMorphStmts(BasicBlock* block, bool* lnot, bool* loadw); |
4223 | void fgMorphBlocks(); |
4224 | |
4225 | bool fgMorphBlockStmt(BasicBlock* block, GenTreeStmt* stmt DEBUGARG(const char* msg)); |
4226 | |
4227 | void fgSetOptions(); |
4228 | |
4229 | #ifdef DEBUG |
4230 | static fgWalkPreFn fgAssertNoQmark; |
4231 | void fgPreExpandQmarkChecks(GenTree* expr); |
4232 | void fgPostExpandQmarkChecks(); |
4233 | static void fgCheckQmarkAllowedForm(GenTree* tree); |
4234 | #endif |
4235 | |
4236 | IL_OFFSET fgFindBlockILOffset(BasicBlock* block); |
4237 | |
4238 | BasicBlock* fgSplitBlockAtBeginning(BasicBlock* curr); |
4239 | BasicBlock* fgSplitBlockAtEnd(BasicBlock* curr); |
4240 | BasicBlock* fgSplitBlockAfterStatement(BasicBlock* curr, GenTree* stmt); |
4241 | BasicBlock* fgSplitBlockAfterNode(BasicBlock* curr, GenTree* node); // for LIR |
4242 | BasicBlock* fgSplitEdge(BasicBlock* curr, BasicBlock* succ); |
4243 | |
4244 | GenTreeStmt* fgNewStmtFromTree(GenTree* tree, BasicBlock* block, IL_OFFSETX offs); |
4245 | GenTreeStmt* fgNewStmtFromTree(GenTree* tree); |
4246 | GenTreeStmt* fgNewStmtFromTree(GenTree* tree, BasicBlock* block); |
4247 | GenTreeStmt* fgNewStmtFromTree(GenTree* tree, IL_OFFSETX offs); |
4248 | |
4249 | GenTree* fgGetTopLevelQmark(GenTree* expr, GenTree** ppDst = nullptr); |
4250 | void fgExpandQmarkForCastInstOf(BasicBlock* block, GenTree* stmt); |
4251 | void fgExpandQmarkStmt(BasicBlock* block, GenTree* expr); |
4252 | void fgExpandQmarkNodes(); |
4253 | |
4254 | void fgMorph(); |
4255 | |
4256 | // Do "simple lowering." This functionality is (conceptually) part of "general" |
4257 | // lowering that is distributed between fgMorph and the lowering phase of LSRA. |
4258 | void fgSimpleLowering(); |
4259 | |
4260 | GenTree* fgInitThisClass(); |
4261 | |
4262 | GenTreeCall* fgGetStaticsCCtorHelper(CORINFO_CLASS_HANDLE cls, CorInfoHelpFunc helper); |
4263 | |
4264 | GenTreeCall* fgGetSharedCCtor(CORINFO_CLASS_HANDLE cls); |
4265 | |
4266 | bool backendRequiresLocalVarLifetimes() |
4267 | { |
4268 | return !opts.MinOpts() || m_pLinearScan->willEnregisterLocalVars(); |
4269 | } |
4270 | |
4271 | void fgLocalVarLiveness(); |
4272 | |
4273 | void fgLocalVarLivenessInit(); |
4274 | |
4275 | void fgPerNodeLocalVarLiveness(GenTree* node); |
4276 | void fgPerBlockLocalVarLiveness(); |
4277 | |
4278 | VARSET_VALRET_TP fgGetHandlerLiveVars(BasicBlock* block); |
4279 | |
4280 | void fgLiveVarAnalysis(bool updateInternalOnly = false); |
4281 | |
4282 | void fgComputeLifeCall(VARSET_TP& life, GenTreeCall* call); |
4283 | |
4284 | void fgComputeLifeTrackedLocalUse(VARSET_TP& life, LclVarDsc& varDsc, GenTreeLclVarCommon* node); |
4285 | bool fgComputeLifeTrackedLocalDef(VARSET_TP& life, |
4286 | VARSET_VALARG_TP keepAliveVars, |
4287 | LclVarDsc& varDsc, |
4288 | GenTreeLclVarCommon* node); |
4289 | void fgComputeLifeUntrackedLocal(VARSET_TP& life, |
4290 | VARSET_VALARG_TP keepAliveVars, |
4291 | LclVarDsc& varDsc, |
4292 | GenTreeLclVarCommon* lclVarNode); |
4293 | bool fgComputeLifeLocal(VARSET_TP& life, VARSET_VALARG_TP keepAliveVars, GenTree* lclVarNode); |
4294 | |
4295 | void fgComputeLife(VARSET_TP& life, |
4296 | GenTree* startNode, |
4297 | GenTree* endNode, |
4298 | VARSET_VALARG_TP volatileVars, |
4299 | bool* pStmtInfoDirty DEBUGARG(bool* treeModf)); |
4300 | |
4301 | void fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALARG_TP volatileVars); |
4302 | |
4303 | bool fgRemoveDeadStore(GenTree** pTree, |
4304 | LclVarDsc* varDsc, |
4305 | VARSET_VALARG_TP life, |
4306 | bool* doAgain, |
4307 | bool* pStmtInfoDirty DEBUGARG(bool* treeModf)); |
4308 | |
4309 | // For updating liveset during traversal AFTER fgComputeLife has completed |
4310 | VARSET_VALRET_TP fgGetVarBits(GenTree* tree); |
4311 | VARSET_VALRET_TP fgUpdateLiveSet(VARSET_VALARG_TP liveSet, GenTree* tree); |
4312 | |
4313 | // Returns the set of live variables after endTree, |
4314 | // assuming that liveSet is the set of live variables BEFORE tree. |
4315 | // Requires that fgComputeLife has completed, and that tree is in the same |
4316 | // statement as endTree, and that it comes before endTree in execution order |
4317 | |
4318 | VARSET_VALRET_TP fgUpdateLiveSet(VARSET_VALARG_TP liveSet, GenTree* tree, GenTree* endTree) |
4319 | { |
4320 | VARSET_TP newLiveSet(VarSetOps::MakeCopy(this, liveSet)); |
4321 | while (tree != nullptr && tree != endTree->gtNext) |
4322 | { |
4323 | VarSetOps::AssignNoCopy(this, newLiveSet, fgUpdateLiveSet(newLiveSet, tree)); |
4324 | tree = tree->gtNext; |
4325 | } |
4326 | assert(tree == endTree->gtNext); |
4327 | return newLiveSet; |
4328 | } |
4329 | |
4330 | void fgInterBlockLocalVarLiveness(); |
4331 | |
4332 | // The presence of a partial definition presents some difficulties for SSA: this is both a use of some SSA name |
4333 | // of "x", and a def of a new SSA name for "x". The tree only has one local variable for "x", so it has to choose |
4334 | // whether to treat that as the use or def. It chooses the "use", and thus the old SSA name. This map allows us |
4335 | // to record/recover the "def" SSA number, given the lcl var node for "x" in such a tree. |
4336 | typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, unsigned> NodeToUnsignedMap; |
4337 | NodeToUnsignedMap* m_opAsgnVarDefSsaNums; |
4338 | NodeToUnsignedMap* GetOpAsgnVarDefSsaNums() |
4339 | { |
4340 | if (m_opAsgnVarDefSsaNums == nullptr) |
4341 | { |
4342 | m_opAsgnVarDefSsaNums = new (getAllocator()) NodeToUnsignedMap(getAllocator()); |
4343 | } |
4344 | return m_opAsgnVarDefSsaNums; |
4345 | } |
4346 | |
4347 | // Requires value numbering phase to have completed. Returns the value number ("gtVN") of the |
4348 | // "tree," EXCEPT in the case of GTF_VAR_USEASG, because the tree node's gtVN member is the |
4349 | // "use" VN. Performs a lookup into the map of (use asg tree -> def VN.) to return the "def's" |
4350 | // VN. |
4351 | inline ValueNum GetUseAsgDefVNOrTreeVN(GenTree* tree); |
4352 | |
4353 | // Requires that "lcl" has the GTF_VAR_DEF flag set. Returns the SSA number of "lcl". |
4354 | // Except: assumes that lcl is a def, and if it is |
4355 | // a partial def (GTF_VAR_USEASG), looks up and returns the SSA number for the "def", |
4356 | // rather than the "use" SSA number recorded in the tree "lcl". |
4357 | inline unsigned GetSsaNumForLocalVarDef(GenTree* lcl); |
4358 | |
4359 | // Performs SSA conversion. |
4360 | void fgSsaBuild(); |
4361 | |
4362 | // Reset any data structures to the state expected by "fgSsaBuild", so it can be run again. |
4363 | void (); |
4364 | |
4365 | unsigned fgSsaPassesCompleted; // Number of times fgSsaBuild has been run. |
4366 | |
4367 | // Returns "true" if a struct temp of the given type requires needs zero init in this block |
4368 | inline bool fgStructTempNeedsExplicitZeroInit(LclVarDsc* varDsc, BasicBlock* block); |
4369 | |
4370 | // The value numbers for this compilation. |
4371 | ValueNumStore* vnStore; |
4372 | |
4373 | public: |
4374 | ValueNumStore* GetValueNumStore() |
4375 | { |
4376 | return vnStore; |
4377 | } |
4378 | |
4379 | // Do value numbering (assign a value number to each |
4380 | // tree node). |
4381 | void fgValueNumber(); |
4382 | |
4383 | // Computes new GcHeap VN via the assignment H[elemTypeEq][arrVN][inx][fldSeq] = rhsVN. |
4384 | // Assumes that "elemTypeEq" is the (equivalence class rep) of the array element type. |
4385 | // The 'indType' is the indirection type of the lhs of the assignment and will typically |
4386 | // match the element type of the array or fldSeq. When this type doesn't match |
4387 | // or if the fldSeq is 'NotAField' we invalidate the array contents H[elemTypeEq][arrVN] |
4388 | // |
4389 | ValueNum fgValueNumberArrIndexAssign(CORINFO_CLASS_HANDLE elemTypeEq, |
4390 | ValueNum arrVN, |
4391 | ValueNum inxVN, |
4392 | FieldSeqNode* fldSeq, |
4393 | ValueNum rhsVN, |
4394 | var_types indType); |
4395 | |
4396 | // Requires that "tree" is a GT_IND marked as an array index, and that its address argument |
4397 | // has been parsed to yield the other input arguments. If evaluation of the address |
4398 | // can raise exceptions, those should be captured in the exception set "excVN." |
4399 | // Assumes that "elemTypeEq" is the (equivalence class rep) of the array element type. |
4400 | // Marks "tree" with the VN for H[elemTypeEq][arrVN][inx][fldSeq] (for the liberal VN; a new unique |
4401 | // VN for the conservative VN.) Also marks the tree's argument as the address of an array element. |
4402 | // The type tree->TypeGet() will typically match the element type of the array or fldSeq. |
4403 | // When this type doesn't match or if the fldSeq is 'NotAField' we return a new unique VN |
4404 | // |
4405 | ValueNum fgValueNumberArrIndexVal(GenTree* tree, |
4406 | CORINFO_CLASS_HANDLE elemTypeEq, |
4407 | ValueNum arrVN, |
4408 | ValueNum inxVN, |
4409 | ValueNum excVN, |
4410 | FieldSeqNode* fldSeq); |
4411 | |
4412 | // Requires "funcApp" to be a VNF_PtrToArrElem, and "addrXvn" to represent the exception set thrown |
4413 | // by evaluating the array index expression "tree". Returns the value number resulting from |
4414 | // dereferencing the array in the current GcHeap state. If "tree" is non-null, it must be the |
4415 | // "GT_IND" that does the dereference, and it is given the returned value number. |
4416 | ValueNum fgValueNumberArrIndexVal(GenTree* tree, struct VNFuncApp* funcApp, ValueNum addrXvn); |
4417 | |
4418 | // Compute the value number for a byref-exposed load of the given type via the given pointerVN. |
4419 | ValueNum fgValueNumberByrefExposedLoad(var_types type, ValueNum pointerVN); |
4420 | |
4421 | unsigned fgVNPassesCompleted; // Number of times fgValueNumber has been run. |
4422 | |
4423 | // Utility functions for fgValueNumber. |
4424 | |
4425 | // Perform value-numbering for the trees in "blk". |
4426 | void fgValueNumberBlock(BasicBlock* blk); |
4427 | |
4428 | // Requires that "entryBlock" is the entry block of loop "loopNum", and that "loopNum" is the |
4429 | // innermost loop of which "entryBlock" is the entry. Returns the value number that should be |
4430 | // assumed for the memoryKind at the start "entryBlk". |
4431 | ValueNum fgMemoryVNForLoopSideEffects(MemoryKind memoryKind, BasicBlock* entryBlock, unsigned loopNum); |
4432 | |
4433 | // Called when an operation (performed by "tree", described by "msg") may cause the GcHeap to be mutated. |
4434 | // As GcHeap is a subset of ByrefExposed, this will also annotate the ByrefExposed mutation. |
4435 | void fgMutateGcHeap(GenTree* tree DEBUGARG(const char* msg)); |
4436 | |
4437 | // Called when an operation (performed by "tree", described by "msg") may cause an address-exposed local to be |
4438 | // mutated. |
4439 | void fgMutateAddressExposedLocal(GenTree* tree DEBUGARG(const char* msg)); |
4440 | |
4441 | // For a GC heap store at curTree, record the new curMemoryVN's and update curTree's MemorySsaMap. |
4442 | // As GcHeap is a subset of ByrefExposed, this will also record the ByrefExposed store. |
4443 | void recordGcHeapStore(GenTree* curTree, ValueNum gcHeapVN DEBUGARG(const char* msg)); |
4444 | |
4445 | // For a store to an address-exposed local at curTree, record the new curMemoryVN and update curTree's MemorySsaMap. |
4446 | void recordAddressExposedLocalStore(GenTree* curTree, ValueNum memoryVN DEBUGARG(const char* msg)); |
4447 | |
4448 | // Tree caused an update in the current memory VN. If "tree" has an associated heap SSA #, record that |
4449 | // value in that SSA #. |
4450 | void fgValueNumberRecordMemorySsa(MemoryKind memoryKind, GenTree* tree); |
4451 | |
4452 | // The input 'tree' is a leaf node that is a constant |
4453 | // Assign the proper value number to the tree |
4454 | void fgValueNumberTreeConst(GenTree* tree); |
4455 | |
4456 | // Assumes that all inputs to "tree" have had value numbers assigned; assigns a VN to tree. |
4457 | // (With some exceptions: the VN of the lhs of an assignment is assigned as part of the |
4458 | // assignment.) |
4459 | void fgValueNumberTree(GenTree* tree); |
4460 | |
4461 | // Does value-numbering for a block assignment. |
4462 | void fgValueNumberBlockAssignment(GenTree* tree); |
4463 | |
4464 | // Does value-numbering for a cast tree. |
4465 | void fgValueNumberCastTree(GenTree* tree); |
4466 | |
4467 | // Does value-numbering for an intrinsic tree. |
4468 | void fgValueNumberIntrinsic(GenTree* tree); |
4469 | |
4470 | // Does value-numbering for a call. We interpret some helper calls. |
4471 | void fgValueNumberCall(GenTreeCall* call); |
4472 | |
4473 | // The VN of some nodes in "args" may have changed -- reassign VNs to the arg list nodes. |
4474 | void fgUpdateArgListVNs(GenTreeArgList* args); |
4475 | |
4476 | // Does value-numbering for a helper "call" that has a VN function symbol "vnf". |
4477 | void fgValueNumberHelperCallFunc(GenTreeCall* call, VNFunc vnf, ValueNumPair vnpExc); |
4478 | |
4479 | // Requires "helpCall" to be a helper call. Assigns it a value number; |
4480 | // we understand the semantics of some of the calls. Returns "true" if |
4481 | // the call may modify the heap (we assume arbitrary memory side effects if so). |
4482 | bool fgValueNumberHelperCall(GenTreeCall* helpCall); |
4483 | |
4484 | // Requires that "helpFunc" is one of the pure Jit Helper methods. |
4485 | // Returns the corresponding VNFunc to use for value numbering |
4486 | VNFunc fgValueNumberJitHelperMethodVNFunc(CorInfoHelpFunc helpFunc); |
4487 | |
4488 | // Adds the exception set for the current tree node which has a memory indirection operation |
4489 | void fgValueNumberAddExceptionSetForIndirection(GenTree* tree, GenTree* baseAddr); |
4490 | |
4491 | // Adds the exception sets for the current tree node which is performing a division or modulus operation |
4492 | void fgValueNumberAddExceptionSetForDivision(GenTree* tree); |
4493 | |
4494 | // Adds the exception set for the current tree node which is performing a overflow checking operation |
4495 | void fgValueNumberAddExceptionSetForOverflow(GenTree* tree); |
4496 | |
4497 | // Adds the exception set for the current tree node which is performing a ckfinite operation |
4498 | void fgValueNumberAddExceptionSetForCkFinite(GenTree* tree); |
4499 | |
4500 | // Adds the exception sets for the current tree node |
4501 | void fgValueNumberAddExceptionSet(GenTree* tree); |
4502 | |
4503 | // These are the current value number for the memory implicit variables while |
4504 | // doing value numbering. These are the value numbers under the "liberal" interpretation |
4505 | // of memory values; the "conservative" interpretation needs no VN, since every access of |
4506 | // memory yields an unknown value. |
4507 | ValueNum fgCurMemoryVN[MemoryKindCount]; |
4508 | |
4509 | // Return a "pseudo"-class handle for an array element type. If "elemType" is TYP_STRUCT, |
4510 | // requires "elemStructType" to be non-null (and to have a low-order zero). Otherwise, low order bit |
4511 | // is 1, and the rest is an encoding of "elemTyp". |
4512 | static CORINFO_CLASS_HANDLE EncodeElemType(var_types elemTyp, CORINFO_CLASS_HANDLE elemStructType) |
4513 | { |
4514 | if (elemStructType != nullptr) |
4515 | { |
4516 | assert(varTypeIsStruct(elemTyp) || elemTyp == TYP_REF || elemTyp == TYP_BYREF || |
4517 | varTypeIsIntegral(elemTyp)); |
4518 | assert((size_t(elemStructType) & 0x1) == 0x0); // Make sure the encoding below is valid. |
4519 | return elemStructType; |
4520 | } |
4521 | else |
4522 | { |
4523 | assert(elemTyp != TYP_STRUCT); |
4524 | elemTyp = varTypeUnsignedToSigned(elemTyp); |
4525 | return CORINFO_CLASS_HANDLE(size_t(elemTyp) << 1 | 0x1); |
4526 | } |
4527 | } |
4528 | // If "clsHnd" is the result of an "EncodePrim" call, returns true and sets "*pPrimType" to the |
4529 | // var_types it represents. Otherwise, returns TYP_STRUCT (on the assumption that "clsHnd" is |
4530 | // the struct type of the element). |
4531 | static var_types DecodeElemType(CORINFO_CLASS_HANDLE clsHnd) |
4532 | { |
4533 | size_t clsHndVal = size_t(clsHnd); |
4534 | if (clsHndVal & 0x1) |
4535 | { |
4536 | return var_types(clsHndVal >> 1); |
4537 | } |
4538 | else |
4539 | { |
4540 | return TYP_STRUCT; |
4541 | } |
4542 | } |
4543 | |
4544 | // Convert a BYTE which represents the VM's CorInfoGCtype to the JIT's var_types |
4545 | var_types getJitGCType(BYTE gcType); |
4546 | |
4547 | enum structPassingKind |
4548 | { |
4549 | SPK_Unknown, // Invalid value, never returned |
4550 | SPK_PrimitiveType, // The struct is passed/returned using a primitive type. |
4551 | SPK_EnclosingType, // Like SPK_Primitive type, but used for return types that |
4552 | // require a primitive type temp that is larger than the struct size. |
4553 | // Currently used for structs of size 3, 5, 6, or 7 bytes. |
4554 | SPK_ByValue, // The struct is passed/returned by value (using the ABI rules) |
4555 | // for ARM64 and UNIX_X64 in multiple registers. (when all of the |
4556 | // parameters registers are used, then the stack will be used) |
4557 | // for X86 passed on the stack, for ARM32 passed in registers |
4558 | // or the stack or split between registers and the stack. |
4559 | SPK_ByValueAsHfa, // The struct is passed/returned as an HFA in multiple registers. |
4560 | SPK_ByReference |
4561 | }; // The struct is passed/returned by reference to a copy/buffer. |
4562 | |
4563 | // Get the "primitive" type that is is used when we are given a struct of size 'structSize'. |
4564 | // For pointer sized structs the 'clsHnd' is used to determine if the struct contains GC ref. |
4565 | // A "primitive" type is one of the scalar types: byte, short, int, long, ref, float, double |
4566 | // If we can't or shouldn't use a "primitive" type then TYP_UNKNOWN is returned. |
4567 | // |
4568 | // isVarArg is passed for use on Windows Arm64 to change the decision returned regarding |
4569 | // hfa types. |
4570 | // |
4571 | var_types getPrimitiveTypeForStruct(unsigned structSize, CORINFO_CLASS_HANDLE clsHnd, bool isVarArg); |
4572 | |
4573 | // Get the type that is used to pass values of the given struct type. |
4574 | // isVarArg is passed for use on Windows Arm64 to change the decision returned regarding |
4575 | // hfa types. |
4576 | // |
4577 | var_types getArgTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, |
4578 | structPassingKind* wbPassStruct, |
4579 | bool isVarArg, |
4580 | unsigned structSize); |
4581 | |
4582 | // Get the type that is used to return values of the given struct type. |
4583 | // If the size is unknown, pass 0 and it will be determined from 'clsHnd'. |
4584 | var_types getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd, |
4585 | structPassingKind* wbPassStruct = nullptr, |
4586 | unsigned structSize = 0); |
4587 | |
4588 | #ifdef DEBUG |
4589 | // Print a representation of "vnp" or "vn" on standard output. |
4590 | // If "level" is non-zero, we also print out a partial expansion of the value. |
4591 | void vnpPrint(ValueNumPair vnp, unsigned level); |
4592 | void vnPrint(ValueNum vn, unsigned level); |
4593 | #endif |
4594 | |
4595 | bool fgDominate(BasicBlock* b1, BasicBlock* b2); // Return true if b1 dominates b2 |
4596 | |
4597 | // Dominator computation member functions |
4598 | // Not exposed outside Compiler |
4599 | protected: |
4600 | bool fgReachable(BasicBlock* b1, BasicBlock* b2); // Returns true if block b1 can reach block b2 |
4601 | |
4602 | void fgComputeDoms(); // Computes the immediate dominators for each basic block in the |
4603 | // flow graph. We first assume the fields bbIDom on each |
4604 | // basic block are invalid. This computation is needed later |
4605 | // by fgBuildDomTree to build the dominance tree structure. |
4606 | // Based on: A Simple, Fast Dominance Algorithm |
4607 | // by Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy |
4608 | |
4609 | void fgCompDominatedByExceptionalEntryBlocks(); |
4610 | |
4611 | BlockSet_ValRet_T fgGetDominatorSet(BasicBlock* block); // Returns a set of blocks that dominate the given block. |
4612 | // Note: this is relatively slow compared to calling fgDominate(), |
4613 | // especially if dealing with a single block versus block check. |
4614 | |
4615 | void fgComputeReachabilitySets(); // Compute bbReach sets. (Also sets BBF_GC_SAFE_POINT flag on blocks.) |
4616 | |
4617 | void fgComputeEnterBlocksSet(); // Compute the set of entry blocks, 'fgEnterBlks'. |
4618 | |
4619 | bool fgRemoveUnreachableBlocks(); // Remove blocks determined to be unreachable by the bbReach sets. |
4620 | |
4621 | void fgComputeReachability(); // Perform flow graph node reachability analysis. |
4622 | |
4623 | BasicBlock* fgIntersectDom(BasicBlock* a, BasicBlock* b); // Intersect two immediate dominator sets. |
4624 | |
4625 | void fgDfsInvPostOrder(); // In order to compute dominance using fgIntersectDom, the flow graph nodes must be |
4626 | // processed in topological sort, this function takes care of that. |
4627 | |
4628 | void fgDfsInvPostOrderHelper(BasicBlock* block, BlockSet& visited, unsigned* count); |
4629 | |
4630 | BlockSet_ValRet_T fgDomFindStartNodes(); // Computes which basic blocks don't have incoming edges in the flow graph. |
4631 | // Returns this as a set. |
4632 | |
4633 | BlockSet_ValRet_T fgDomTreeEntryNodes(BasicBlockList** domTree); // Computes which nodes in the dominance forest are |
4634 | // root nodes. Returns this as a set. |
4635 | |
4636 | #ifdef DEBUG |
4637 | void fgDispDomTree(BasicBlockList** domTree); // Helper that prints out the Dominator Tree in debug builds. |
4638 | #endif // DEBUG |
4639 | |
4640 | void fgBuildDomTree(); // Once we compute all the immediate dominator sets for each node in the flow graph |
4641 | // (performed by fgComputeDoms), this procedure builds the dominance tree represented |
4642 | // adjacency lists. |
4643 | |
4644 | // In order to speed up the queries of the form 'Does A dominates B', we can perform a DFS preorder and postorder |
4645 | // traversal of the dominance tree and the dominance query will become A dominates B iif preOrder(A) <= preOrder(B) |
4646 | // && postOrder(A) >= postOrder(B) making the computation O(1). |
4647 | void fgTraverseDomTree(unsigned bbNum, BasicBlockList** domTree, unsigned* preNum, unsigned* postNum); |
4648 | |
4649 | // When the flow graph changes, we need to update the block numbers, predecessor lists, reachability sets, and |
4650 | // dominators. |
4651 | void fgUpdateChangedFlowGraph(); |
4652 | |
4653 | public: |
4654 | // Compute the predecessors of the blocks in the control flow graph. |
4655 | void fgComputePreds(); |
4656 | |
4657 | // Remove all predecessor information. |
4658 | void fgRemovePreds(); |
4659 | |
4660 | // Compute the cheap flow graph predecessors lists. This is used in some early phases |
4661 | // before the full predecessors lists are computed. |
4662 | void fgComputeCheapPreds(); |
4663 | |
4664 | private: |
4665 | void fgAddCheapPred(BasicBlock* block, BasicBlock* blockPred); |
4666 | |
4667 | void fgRemoveCheapPred(BasicBlock* block, BasicBlock* blockPred); |
4668 | |
4669 | public: |
4670 | enum GCPollType |
4671 | { |
4672 | GCPOLL_NONE, |
4673 | GCPOLL_CALL, |
4674 | GCPOLL_INLINE |
4675 | }; |
4676 | |
4677 | // Initialize the per-block variable sets (used for liveness analysis). |
4678 | void fgInitBlockVarSets(); |
4679 | |
4680 | // true if we've gone through and created GC Poll calls. |
4681 | bool fgGCPollsCreated; |
4682 | void fgMarkGCPollBlocks(); |
4683 | void fgCreateGCPolls(); |
4684 | bool fgCreateGCPoll(GCPollType pollType, BasicBlock* block); |
4685 | |
4686 | // Requires that "block" is a block that returns from |
4687 | // a finally. Returns the number of successors (jump targets of |
4688 | // of blocks in the covered "try" that did a "LEAVE".) |
4689 | unsigned fgNSuccsOfFinallyRet(BasicBlock* block); |
4690 | |
4691 | // Requires that "block" is a block that returns (in the sense of BBJ_EHFINALLYRET) from |
4692 | // a finally. Returns its "i"th successor (jump targets of |
4693 | // of blocks in the covered "try" that did a "LEAVE".) |
4694 | // Requires that "i" < fgNSuccsOfFinallyRet(block). |
4695 | BasicBlock* fgSuccOfFinallyRet(BasicBlock* block, unsigned i); |
4696 | |
4697 | private: |
4698 | // Factor out common portions of the impls of the methods above. |
4699 | void fgSuccOfFinallyRetWork(BasicBlock* block, unsigned i, BasicBlock** bres, unsigned* nres); |
4700 | |
4701 | public: |
4702 | // For many purposes, it is desirable to be able to enumerate the *distinct* targets of a switch statement, |
4703 | // skipping duplicate targets. (E.g., in flow analyses that are only interested in the set of possible targets.) |
4704 | // SwitchUniqueSuccSet contains the non-duplicated switch targets. |
4705 | // (Code that modifies the jump table of a switch has an obligation to call Compiler::UpdateSwitchTableTarget, |
4706 | // which in turn will call the "UpdateTarget" method of this type if a SwitchUniqueSuccSet has already |
4707 | // been computed for the switch block. If a switch block is deleted or is transformed into a non-switch, |
4708 | // we leave the entry associated with the block, but it will no longer be accessed.) |
4709 | struct SwitchUniqueSuccSet |
4710 | { |
4711 | unsigned numDistinctSuccs; // Number of distinct targets of the switch. |
4712 | BasicBlock** nonDuplicates; // Array of "numDistinctSuccs", containing all the distinct switch target |
4713 | // successors. |
4714 | |
4715 | // The switch block "switchBlk" just had an entry with value "from" modified to the value "to". |
4716 | // Update "this" as necessary: if "from" is no longer an element of the jump table of "switchBlk", |
4717 | // remove it from "this", and ensure that "to" is a member. Use "alloc" to do any required allocation. |
4718 | void UpdateTarget(CompAllocator alloc, BasicBlock* switchBlk, BasicBlock* from, BasicBlock* to); |
4719 | }; |
4720 | |
4721 | typedef JitHashTable<BasicBlock*, JitPtrKeyFuncs<BasicBlock>, SwitchUniqueSuccSet> BlockToSwitchDescMap; |
4722 | |
4723 | private: |
4724 | // Maps BasicBlock*'s that end in switch statements to SwitchUniqueSuccSets that allow |
4725 | // iteration over only the distinct successors. |
4726 | BlockToSwitchDescMap* m_switchDescMap; |
4727 | |
4728 | public: |
4729 | BlockToSwitchDescMap* GetSwitchDescMap(bool createIfNull = true) |
4730 | { |
4731 | if ((m_switchDescMap == nullptr) && createIfNull) |
4732 | { |
4733 | m_switchDescMap = new (getAllocator()) BlockToSwitchDescMap(getAllocator()); |
4734 | } |
4735 | return m_switchDescMap; |
4736 | } |
4737 | |
4738 | // Invalidate the map of unique switch block successors. For example, since the hash key of the map |
4739 | // depends on block numbers, we must invalidate the map when the blocks are renumbered, to ensure that |
4740 | // we don't accidentally look up and return the wrong switch data. |
4741 | void InvalidateUniqueSwitchSuccMap() |
4742 | { |
4743 | m_switchDescMap = nullptr; |
4744 | } |
4745 | |
4746 | // Requires "switchBlock" to be a block that ends in a switch. Returns |
4747 | // the corresponding SwitchUniqueSuccSet. |
4748 | SwitchUniqueSuccSet GetDescriptorForSwitch(BasicBlock* switchBlk); |
4749 | |
4750 | // The switch block "switchBlk" just had an entry with value "from" modified to the value "to". |
4751 | // Update "this" as necessary: if "from" is no longer an element of the jump table of "switchBlk", |
4752 | // remove it from "this", and ensure that "to" is a member. |
4753 | void UpdateSwitchTableTarget(BasicBlock* switchBlk, BasicBlock* from, BasicBlock* to); |
4754 | |
4755 | // Remove the "SwitchUniqueSuccSet" of "switchBlk" in the BlockToSwitchDescMap. |
4756 | void fgInvalidateSwitchDescMapEntry(BasicBlock* switchBlk); |
4757 | |
4758 | BasicBlock* fgFirstBlockOfHandler(BasicBlock* block); |
4759 | |
4760 | flowList* fgGetPredForBlock(BasicBlock* block, BasicBlock* blockPred); |
4761 | |
4762 | flowList* fgGetPredForBlock(BasicBlock* block, BasicBlock* blockPred, flowList*** ptrToPred); |
4763 | |
4764 | flowList* fgSpliceOutPred(BasicBlock* block, BasicBlock* blockPred); |
4765 | |
4766 | flowList* fgRemoveRefPred(BasicBlock* block, BasicBlock* blockPred); |
4767 | |
4768 | flowList* fgRemoveAllRefPreds(BasicBlock* block, BasicBlock* blockPred); |
4769 | |
4770 | flowList* fgRemoveAllRefPreds(BasicBlock* block, flowList** ptrToPred); |
4771 | |
4772 | void fgRemoveBlockAsPred(BasicBlock* block); |
4773 | |
4774 | void fgChangeSwitchBlock(BasicBlock* oldSwitchBlock, BasicBlock* newSwitchBlock); |
4775 | |
4776 | void fgReplaceSwitchJumpTarget(BasicBlock* blockSwitch, BasicBlock* newTarget, BasicBlock* oldTarget); |
4777 | |
4778 | void fgReplaceJumpTarget(BasicBlock* block, BasicBlock* newTarget, BasicBlock* oldTarget); |
4779 | |
4780 | void fgReplacePred(BasicBlock* block, BasicBlock* oldPred, BasicBlock* newPred); |
4781 | |
4782 | flowList* fgAddRefPred(BasicBlock* block, |
4783 | BasicBlock* blockPred, |
4784 | flowList* oldEdge = nullptr, |
4785 | bool initializingPreds = false); // Only set to 'true' when we are computing preds in |
4786 | // fgComputePreds() |
4787 | |
4788 | void fgFindBasicBlocks(); |
4789 | |
4790 | bool fgIsBetterFallThrough(BasicBlock* bCur, BasicBlock* bAlt); |
4791 | |
4792 | bool fgCheckEHCanInsertAfterBlock(BasicBlock* blk, unsigned regionIndex, bool putInTryRegion); |
4793 | |
4794 | BasicBlock* fgFindInsertPoint(unsigned regionIndex, |
4795 | bool putInTryRegion, |
4796 | BasicBlock* startBlk, |
4797 | BasicBlock* endBlk, |
4798 | BasicBlock* nearBlk, |
4799 | BasicBlock* jumpBlk, |
4800 | bool runRarely); |
4801 | |
4802 | unsigned fgGetNestingLevel(BasicBlock* block, unsigned* pFinallyNesting = nullptr); |
4803 | |
4804 | void fgRemoveEmptyBlocks(); |
4805 | |
4806 | void fgRemoveStmt(BasicBlock* block, GenTree* stmt); |
4807 | |
4808 | bool fgCheckRemoveStmt(BasicBlock* block, GenTree* stmt); |
4809 | |
4810 | void (unsigned lnum); |
4811 | |
4812 | void fgUnreachableBlock(BasicBlock* block); |
4813 | |
4814 | void fgRemoveConditionalJump(BasicBlock* block); |
4815 | |
4816 | BasicBlock* fgLastBBInMainFunction(); |
4817 | |
4818 | BasicBlock* fgEndBBAfterMainFunction(); |
4819 | |
4820 | void fgUnlinkRange(BasicBlock* bBeg, BasicBlock* bEnd); |
4821 | |
4822 | void fgRemoveBlock(BasicBlock* block, bool unreachable); |
4823 | |
4824 | bool fgCanCompactBlocks(BasicBlock* block, BasicBlock* bNext); |
4825 | |
4826 | void fgCompactBlocks(BasicBlock* block, BasicBlock* bNext); |
4827 | |
4828 | void fgUpdateLoopsAfterCompacting(BasicBlock* block, BasicBlock* bNext); |
4829 | |
4830 | BasicBlock* fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst); |
4831 | |
4832 | bool fgRenumberBlocks(); |
4833 | |
4834 | bool fgExpandRarelyRunBlocks(); |
4835 | |
4836 | bool fgEhAllowsMoveBlock(BasicBlock* bBefore, BasicBlock* bAfter); |
4837 | |
4838 | void fgMoveBlocksAfter(BasicBlock* bStart, BasicBlock* bEnd, BasicBlock* insertAfterBlk); |
4839 | |
4840 | enum FG_RELOCATE_TYPE |
4841 | { |
4842 | FG_RELOCATE_TRY, // relocate the 'try' region |
4843 | FG_RELOCATE_HANDLER // relocate the handler region (including the filter if necessary) |
4844 | }; |
4845 | BasicBlock* fgRelocateEHRange(unsigned regionIndex, FG_RELOCATE_TYPE relocateType); |
4846 | |
4847 | #if FEATURE_EH_FUNCLETS |
4848 | #if defined(_TARGET_ARM_) |
4849 | void fgClearFinallyTargetBit(BasicBlock* block); |
4850 | #endif // defined(_TARGET_ARM_) |
4851 | bool fgIsIntraHandlerPred(BasicBlock* predBlock, BasicBlock* block); |
4852 | bool fgAnyIntraHandlerPreds(BasicBlock* block); |
4853 | void fgInsertFuncletPrologBlock(BasicBlock* block); |
4854 | void fgCreateFuncletPrologBlocks(); |
4855 | void fgCreateFunclets(); |
4856 | #else // !FEATURE_EH_FUNCLETS |
4857 | bool fgRelocateEHRegions(); |
4858 | #endif // !FEATURE_EH_FUNCLETS |
4859 | |
4860 | bool fgOptimizeUncondBranchToSimpleCond(BasicBlock* block, BasicBlock* target); |
4861 | |
4862 | bool fgBlockEndFavorsTailDuplication(BasicBlock* block); |
4863 | |
4864 | bool fgBlockIsGoodTailDuplicationCandidate(BasicBlock* block); |
4865 | |
4866 | bool fgOptimizeEmptyBlock(BasicBlock* block); |
4867 | |
4868 | bool fgOptimizeBranchToEmptyUnconditional(BasicBlock* block, BasicBlock* bDest); |
4869 | |
4870 | bool fgOptimizeBranch(BasicBlock* bJump); |
4871 | |
4872 | bool fgOptimizeSwitchBranches(BasicBlock* block); |
4873 | |
4874 | bool fgOptimizeBranchToNext(BasicBlock* block, BasicBlock* bNext, BasicBlock* bPrev); |
4875 | |
4876 | bool fgOptimizeSwitchJumps(); |
4877 | #ifdef DEBUG |
4878 | void fgPrintEdgeWeights(); |
4879 | #endif |
4880 | void fgComputeBlockAndEdgeWeights(); |
4881 | BasicBlock::weight_t fgComputeMissingBlockWeights(); |
4882 | void fgComputeCalledCount(BasicBlock::weight_t returnWeight); |
4883 | void fgComputeEdgeWeights(); |
4884 | |
4885 | void fgReorderBlocks(); |
4886 | |
4887 | void fgDetermineFirstColdBlock(); |
4888 | |
4889 | bool fgIsForwardBranch(BasicBlock* bJump, BasicBlock* bSrc = nullptr); |
4890 | |
4891 | bool fgUpdateFlowGraph(bool doTailDup = false); |
4892 | |
4893 | void fgFindOperOrder(); |
4894 | |
4895 | // method that returns if you should split here |
4896 | typedef bool(fgSplitPredicate)(GenTree* tree, GenTree* parent, fgWalkData* data); |
4897 | |
4898 | void fgSetBlockOrder(); |
4899 | |
4900 | void fgRemoveReturnBlock(BasicBlock* block); |
4901 | |
4902 | /* Helper code that has been factored out */ |
4903 | inline void fgConvertBBToThrowBB(BasicBlock* block); |
4904 | |
4905 | bool fgCastNeeded(GenTree* tree, var_types toType); |
4906 | GenTree* fgDoNormalizeOnStore(GenTree* tree); |
4907 | GenTree* fgMakeTmpArgNode(fgArgTabEntry* curArgTabEntry); |
4908 | |
4909 | // The following check for loops that don't execute calls |
4910 | bool fgLoopCallMarked; |
4911 | |
4912 | void fgLoopCallTest(BasicBlock* srcBB, BasicBlock* dstBB); |
4913 | void fgLoopCallMark(); |
4914 | |
4915 | void fgMarkLoopHead(BasicBlock* block); |
4916 | |
4917 | unsigned fgGetCodeEstimate(BasicBlock* block); |
4918 | |
4919 | #if DUMP_FLOWGRAPHS |
4920 | const char* fgProcessEscapes(const char* nameIn, escapeMapping_t* map); |
4921 | FILE* fgOpenFlowGraphFile(bool* wbDontClose, Phases phase, LPCWSTR type); |
4922 | bool fgDumpFlowGraph(Phases phase); |
4923 | |
4924 | #endif // DUMP_FLOWGRAPHS |
4925 | |
4926 | #ifdef DEBUG |
4927 | void fgDispDoms(); |
4928 | void fgDispReach(); |
4929 | void fgDispBBLiveness(BasicBlock* block); |
4930 | void fgDispBBLiveness(); |
4931 | void fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth = 0); |
4932 | void fgDispBasicBlocks(BasicBlock* firstBlock, BasicBlock* lastBlock, bool dumpTrees); |
4933 | void fgDispBasicBlocks(bool dumpTrees = false); |
4934 | void fgDumpStmtTree(GenTree* stmt, unsigned bbNum); |
4935 | void fgDumpBlock(BasicBlock* block); |
4936 | void fgDumpTrees(BasicBlock* firstBlock, BasicBlock* lastBlock); |
4937 | |
4938 | static fgWalkPreFn fgStress64RsltMulCB; |
4939 | void fgStress64RsltMul(); |
4940 | void fgDebugCheckUpdate(); |
4941 | void fgDebugCheckBBlist(bool checkBBNum = false, bool checkBBRefs = true); |
4942 | void fgDebugCheckBlockLinks(); |
4943 | void fgDebugCheckLinks(bool morphTrees = false); |
4944 | void fgDebugCheckStmtsList(BasicBlock* block, bool morphTrees); |
4945 | void fgDebugCheckNodeLinks(BasicBlock* block, GenTree* stmt); |
4946 | void fgDebugCheckNodesUniqueness(); |
4947 | |
4948 | void fgDebugCheckFlags(GenTree* tree); |
4949 | void fgDebugCheckFlagsHelper(GenTree* tree, unsigned treeFlags, unsigned chkFlags); |
4950 | void fgDebugCheckTryFinallyExits(); |
4951 | #endif |
4952 | |
4953 | static GenTree* fgGetFirstNode(GenTree* tree); |
4954 | |
4955 | //--------------------- Walking the trees in the IR ----------------------- |
4956 | |
4957 | struct fgWalkData |
4958 | { |
4959 | Compiler* compiler; |
4960 | fgWalkPreFn* wtprVisitorFn; |
4961 | fgWalkPostFn* wtpoVisitorFn; |
4962 | void* pCallbackData; // user-provided data |
4963 | bool wtprLclsOnly; // whether to only visit lclvar nodes |
4964 | GenTree* parent; // parent of current node, provided to callback |
4965 | GenTreeStack* parentStack; // stack of parent nodes, if asked for |
4966 | #ifdef DEBUG |
4967 | bool printModified; // callback can use this |
4968 | #endif |
4969 | }; |
4970 | |
4971 | fgWalkResult fgWalkTreePre(GenTree** pTree, |
4972 | fgWalkPreFn* visitor, |
4973 | void* pCallBackData = nullptr, |
4974 | bool lclVarsOnly = false, |
4975 | bool computeStack = false); |
4976 | |
4977 | fgWalkResult fgWalkTree(GenTree** pTree, |
4978 | fgWalkPreFn* preVisitor, |
4979 | fgWalkPostFn* postVisitor, |
4980 | void* pCallBackData = nullptr); |
4981 | |
4982 | void fgWalkAllTreesPre(fgWalkPreFn* visitor, void* pCallBackData); |
4983 | |
4984 | //----- Postorder |
4985 | |
4986 | fgWalkResult fgWalkTreePost(GenTree** pTree, |
4987 | fgWalkPostFn* visitor, |
4988 | void* pCallBackData = nullptr, |
4989 | bool computeStack = false); |
4990 | |
4991 | // An fgWalkPreFn that looks for expressions that have inline throws in |
4992 | // minopts mode. Basically it looks for tress with gtOverflowEx() or |
4993 | // GTF_IND_RNGCHK. It returns WALK_ABORT if one is found. It |
4994 | // returns WALK_SKIP_SUBTREES if GTF_EXCEPT is not set (assumes flags |
4995 | // properly propagated to parent trees). It returns WALK_CONTINUE |
4996 | // otherwise. |
4997 | static fgWalkResult fgChkThrowCB(GenTree** pTree, Compiler::fgWalkData* data); |
4998 | static fgWalkResult fgChkLocAllocCB(GenTree** pTree, Compiler::fgWalkData* data); |
4999 | static fgWalkResult fgChkQmarkCB(GenTree** pTree, Compiler::fgWalkData* data); |
5000 | |
5001 | /************************************************************************** |
5002 | * PROTECTED |
5003 | *************************************************************************/ |
5004 | |
5005 | protected: |
5006 | friend class SsaBuilder; |
5007 | friend struct ValueNumberState; |
5008 | |
5009 | //--------------------- Detect the basic blocks --------------------------- |
5010 | |
5011 | BasicBlock** fgBBs; // Table of pointers to the BBs |
5012 | |
5013 | void fgInitBBLookup(); |
5014 | BasicBlock* fgLookupBB(unsigned addr); |
5015 | |
5016 | void fgFindJumpTargets(const BYTE* codeAddr, IL_OFFSET codeSize, FixedBitVect* jumpTarget); |
5017 | |
5018 | void fgMarkBackwardJump(BasicBlock* startBlock, BasicBlock* endBlock); |
5019 | |
5020 | void fgLinkBasicBlocks(); |
5021 | |
5022 | unsigned fgMakeBasicBlocks(const BYTE* codeAddr, IL_OFFSET codeSize, FixedBitVect* jumpTarget); |
5023 | |
5024 | void fgCheckBasicBlockControlFlow(); |
5025 | |
5026 | void fgControlFlowPermitted(BasicBlock* blkSrc, |
5027 | BasicBlock* blkDest, |
5028 | BOOL IsLeave = false /* is the src a leave block */); |
5029 | |
5030 | bool fgFlowToFirstBlockOfInnerTry(BasicBlock* blkSrc, BasicBlock* blkDest, bool sibling); |
5031 | |
5032 | void fgObserveInlineConstants(OPCODE opcode, const FgStack& stack, bool isInlining); |
5033 | |
5034 | void fgAdjustForAddressExposedOrWrittenThis(); |
5035 | |
5036 | bool fgProfileData_ILSizeMismatch; |
5037 | ICorJitInfo::ProfileBuffer* fgProfileBuffer; |
5038 | ULONG fgProfileBufferCount; |
5039 | ULONG fgNumProfileRuns; |
5040 | |
5041 | unsigned fgStressBBProf() |
5042 | { |
5043 | #ifdef DEBUG |
5044 | unsigned result = JitConfig.JitStressBBProf(); |
5045 | if (result == 0) |
5046 | { |
5047 | if (compStressCompile(STRESS_BB_PROFILE, 15)) |
5048 | { |
5049 | result = 1; |
5050 | } |
5051 | } |
5052 | return result; |
5053 | #else |
5054 | return 0; |
5055 | #endif |
5056 | } |
5057 | |
5058 | bool fgHaveProfileData(); |
5059 | bool fgGetProfileWeightForBasicBlock(IL_OFFSET offset, unsigned* weight); |
5060 | void fgInstrumentMethod(); |
5061 | |
5062 | public: |
5063 | // fgIsUsingProfileWeights - returns true if we have real profile data for this method |
5064 | // or if we have some fake profile data for the stress mode |
5065 | bool fgIsUsingProfileWeights() |
5066 | { |
5067 | return (fgHaveProfileData() || fgStressBBProf()); |
5068 | } |
5069 | |
5070 | // fgProfileRunsCount - returns total number of scenario runs for the profile data |
5071 | // or BB_UNITY_WEIGHT when we aren't using profile data. |
5072 | unsigned fgProfileRunsCount() |
5073 | { |
5074 | return fgIsUsingProfileWeights() ? fgNumProfileRuns : BB_UNITY_WEIGHT; |
5075 | } |
5076 | |
5077 | //-------- Insert a statement at the start or end of a basic block -------- |
5078 | |
5079 | #ifdef DEBUG |
5080 | public: |
5081 | static bool fgBlockContainsStatementBounded(BasicBlock* block, GenTree* stmt, bool answerOnBoundExceeded = true); |
5082 | #endif |
5083 | |
5084 | public: |
5085 | GenTreeStmt* fgInsertStmtAtEnd(BasicBlock* block, GenTree* node); |
5086 | |
5087 | public: // Used by linear scan register allocation |
5088 | GenTreeStmt* fgInsertStmtNearEnd(BasicBlock* block, GenTree* node); |
5089 | |
5090 | private: |
5091 | GenTree* fgInsertStmtAtBeg(BasicBlock* block, GenTree* stmt); |
5092 | GenTree* fgInsertStmtAfter(BasicBlock* block, GenTree* insertionPoint, GenTree* stmt); |
5093 | |
5094 | public: // Used by linear scan register allocation |
5095 | GenTree* fgInsertStmtBefore(BasicBlock* block, GenTree* insertionPoint, GenTree* stmt); |
5096 | |
5097 | private: |
5098 | GenTree* fgInsertStmtListAfter(BasicBlock* block, GenTree* stmtAfter, GenTree* stmtList); |
5099 | |
5100 | // Create a new temporary variable to hold the result of *ppTree, |
5101 | // and transform the graph accordingly. |
5102 | GenTree* fgInsertCommaFormTemp(GenTree** ppTree, CORINFO_CLASS_HANDLE structType = nullptr); |
5103 | GenTree* fgMakeMultiUse(GenTree** ppTree); |
5104 | |
5105 | private: |
5106 | // Recognize a bitwise rotation pattern and convert into a GT_ROL or a GT_ROR node. |
5107 | GenTree* fgRecognizeAndMorphBitwiseRotation(GenTree* tree); |
5108 | bool fgOperIsBitwiseRotationRoot(genTreeOps oper); |
5109 | |
5110 | //-------- Determine the order in which the trees will be evaluated ------- |
5111 | |
5112 | unsigned fgTreeSeqNum; |
5113 | GenTree* fgTreeSeqLst; |
5114 | GenTree* fgTreeSeqBeg; |
5115 | |
5116 | GenTree* fgSetTreeSeq(GenTree* tree, GenTree* prev = nullptr, bool isLIR = false); |
5117 | void fgSetTreeSeqHelper(GenTree* tree, bool isLIR); |
5118 | void fgSetTreeSeqFinish(GenTree* tree, bool isLIR); |
5119 | void fgSetStmtSeq(GenTree* tree); |
5120 | void fgSetBlockOrder(BasicBlock* block); |
5121 | |
5122 | //------------------------- Morphing -------------------------------------- |
5123 | |
5124 | unsigned fgPtrArgCntMax; |
5125 | |
5126 | public: |
5127 | //------------------------------------------------------------------------ |
5128 | // fgGetPtrArgCntMax: Return the maximum number of pointer-sized stack arguments that calls inside this method |
5129 | // can push on the stack. This value is calculated during morph. |
5130 | // |
5131 | // Return Value: |
5132 | // Returns fgPtrArgCntMax, that is a private field. |
5133 | // |
5134 | unsigned fgGetPtrArgCntMax() const |
5135 | { |
5136 | return fgPtrArgCntMax; |
5137 | } |
5138 | |
5139 | //------------------------------------------------------------------------ |
5140 | // fgSetPtrArgCntMax: Set the maximum number of pointer-sized stack arguments that calls inside this method |
5141 | // can push on the stack. This function is used during StackLevelSetter to fix incorrect morph calculations. |
5142 | // |
5143 | void fgSetPtrArgCntMax(unsigned argCntMax) |
5144 | { |
5145 | fgPtrArgCntMax = argCntMax; |
5146 | } |
5147 | |
5148 | bool compCanEncodePtrArgCntMax(); |
5149 | |
5150 | private: |
5151 | hashBv* fgOutgoingArgTemps; |
5152 | hashBv* fgCurrentlyInUseArgTemps; |
5153 | |
5154 | void fgSetRngChkTarget(GenTree* tree, bool delay = true); |
5155 | |
5156 | BasicBlock* fgSetRngChkTargetInner(SpecialCodeKind kind, bool delay); |
5157 | |
5158 | #if REARRANGE_ADDS |
5159 | void fgMoveOpsLeft(GenTree* tree); |
5160 | #endif |
5161 | |
5162 | bool fgIsCommaThrow(GenTree* tree, bool forFolding = false); |
5163 | |
5164 | bool fgIsThrow(GenTree* tree); |
5165 | |
5166 | bool fgInDifferentRegions(BasicBlock* blk1, BasicBlock* blk2); |
5167 | bool fgIsBlockCold(BasicBlock* block); |
5168 | |
5169 | GenTree* fgMorphCastIntoHelper(GenTree* tree, int helper, GenTree* oper); |
5170 | |
5171 | GenTree* fgMorphIntoHelperCall(GenTree* tree, int helper, GenTreeArgList* args, bool morphArgs = true); |
5172 | |
5173 | GenTree* fgMorphStackArgForVarArgs(unsigned lclNum, var_types varType, unsigned lclOffs); |
5174 | |
5175 | // A "MorphAddrContext" carries information from the surrounding context. If we are evaluating a byref address, |
5176 | // it is useful to know whether the address will be immediately dereferenced, or whether the address value will |
5177 | // be used, perhaps by passing it as an argument to a called method. This affects how null checking is done: |
5178 | // for sufficiently small offsets, we can rely on OS page protection to implicitly null-check addresses that we |
5179 | // know will be dereferenced. To know that reliance on implicit null checking is sound, we must further know that |
5180 | // all offsets between the top-level indirection and the bottom are constant, and that their sum is sufficiently |
5181 | // small; hence the other fields of MorphAddrContext. |
5182 | enum MorphAddrContextKind |
5183 | { |
5184 | MACK_Ind, |
5185 | MACK_Addr, |
5186 | }; |
5187 | struct MorphAddrContext |
5188 | { |
5189 | MorphAddrContextKind m_kind; |
5190 | bool m_allConstantOffsets; // Valid only for "m_kind == MACK_Ind". True iff all offsets between |
5191 | // top-level indirection and here have been constants. |
5192 | size_t m_totalOffset; // Valid only for "m_kind == MACK_Ind", and if "m_allConstantOffsets" is true. |
5193 | // In that case, is the sum of those constant offsets. |
5194 | |
5195 | MorphAddrContext(MorphAddrContextKind kind) : m_kind(kind), m_allConstantOffsets(true), m_totalOffset(0) |
5196 | { |
5197 | } |
5198 | }; |
5199 | |
5200 | // A MACK_CopyBlock context is immutable, so we can just make one of these and share it. |
5201 | static MorphAddrContext s_CopyBlockMAC; |
5202 | |
5203 | #ifdef FEATURE_SIMD |
5204 | GenTree* getSIMDStructFromField(GenTree* tree, |
5205 | var_types* baseTypeOut, |
5206 | unsigned* indexOut, |
5207 | unsigned* simdSizeOut, |
5208 | bool ignoreUsedInSIMDIntrinsic = false); |
5209 | GenTree* fgMorphFieldAssignToSIMDIntrinsicSet(GenTree* tree); |
5210 | GenTree* fgMorphFieldToSIMDIntrinsicGet(GenTree* tree); |
5211 | bool fgMorphCombineSIMDFieldAssignments(BasicBlock* block, GenTree* stmt); |
5212 | void impMarkContiguousSIMDFieldAssignments(GenTree* stmt); |
5213 | |
5214 | // fgPreviousCandidateSIMDFieldAsgStmt is only used for tracking previous simd field assignment |
5215 | // in function: Complier::impMarkContiguousSIMDFieldAssignments. |
5216 | GenTree* fgPreviousCandidateSIMDFieldAsgStmt; |
5217 | |
5218 | #endif // FEATURE_SIMD |
5219 | GenTree* fgMorphArrayIndex(GenTree* tree); |
5220 | GenTree* fgMorphCast(GenTree* tree); |
5221 | GenTree* fgUnwrapProxy(GenTree* objRef); |
5222 | GenTreeFieldList* fgMorphLclArgToFieldlist(GenTreeLclVarCommon* lcl); |
5223 | void fgInitArgInfo(GenTreeCall* call); |
5224 | GenTreeCall* fgMorphArgs(GenTreeCall* call); |
5225 | GenTreeArgList* fgMorphArgList(GenTreeArgList* args, MorphAddrContext* mac); |
5226 | |
5227 | void fgMakeOutgoingStructArgCopy(GenTreeCall* call, |
5228 | GenTree* args, |
5229 | unsigned argIndex, |
5230 | CORINFO_CLASS_HANDLE copyBlkClass); |
5231 | |
5232 | void fgFixupStructReturn(GenTree* call); |
5233 | GenTree* fgMorphLocalVar(GenTree* tree, bool forceRemorph); |
5234 | |
5235 | public: |
5236 | bool fgAddrCouldBeNull(GenTree* addr); |
5237 | |
5238 | private: |
5239 | GenTree* fgMorphField(GenTree* tree, MorphAddrContext* mac); |
5240 | bool fgCanFastTailCall(GenTreeCall* call); |
5241 | bool fgCheckStmtAfterTailCall(); |
5242 | void fgMorphTailCall(GenTreeCall* call, void* pfnCopyArgs); |
5243 | GenTree* fgGetStubAddrArg(GenTreeCall* call); |
5244 | void fgMorphRecursiveFastTailCallIntoLoop(BasicBlock* block, GenTreeCall* recursiveTailCall); |
5245 | GenTree* fgAssignRecursiveCallArgToCallerParam(GenTree* arg, |
5246 | fgArgTabEntry* argTabEntry, |
5247 | BasicBlock* block, |
5248 | IL_OFFSETX callILOffset, |
5249 | GenTree* tmpAssignmentInsertionPoint, |
5250 | GenTree* paramAssignmentInsertionPoint); |
5251 | static int fgEstimateCallStackSize(GenTreeCall* call); |
5252 | GenTree* fgMorphCall(GenTreeCall* call); |
5253 | void fgMorphCallInline(GenTreeCall* call, InlineResult* result); |
5254 | void fgMorphCallInlineHelper(GenTreeCall* call, InlineResult* result); |
5255 | #if DEBUG |
5256 | void fgNoteNonInlineCandidate(GenTreeStmt* stmt, GenTreeCall* call); |
5257 | static fgWalkPreFn fgFindNonInlineCandidate; |
5258 | #endif |
5259 | GenTree* fgOptimizeDelegateConstructor(GenTreeCall* call, |
5260 | CORINFO_CONTEXT_HANDLE* ExactContextHnd, |
5261 | CORINFO_RESOLVED_TOKEN* ldftnToken); |
5262 | GenTree* fgMorphLeaf(GenTree* tree); |
5263 | void fgAssignSetVarDef(GenTree* tree); |
5264 | GenTree* fgMorphOneAsgBlockOp(GenTree* tree); |
5265 | GenTree* fgMorphInitBlock(GenTree* tree); |
5266 | GenTree* fgMorphBlkToInd(GenTreeBlk* tree, var_types type); |
5267 | GenTree* fgMorphGetStructAddr(GenTree** pTree, CORINFO_CLASS_HANDLE clsHnd, bool isRValue = false); |
5268 | GenTree* fgMorphBlkNode(GenTree* tree, bool isDest); |
5269 | GenTree* fgMorphBlockOperand(GenTree* tree, var_types asgType, unsigned blockWidth, bool isDest); |
5270 | void fgMorphUnsafeBlk(GenTreeObj* obj); |
5271 | GenTree* fgMorphCopyBlock(GenTree* tree); |
5272 | GenTree* fgMorphForRegisterFP(GenTree* tree); |
5273 | GenTree* fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac = nullptr); |
5274 | GenTree* fgMorphModToSubMulDiv(GenTreeOp* tree); |
5275 | GenTree* fgMorphSmpOpOptional(GenTreeOp* tree); |
5276 | GenTree* fgMorphRecognizeBoxNullable(GenTree* compare); |
5277 | |
5278 | GenTree* fgMorphToEmulatedFP(GenTree* tree); |
5279 | GenTree* fgMorphConst(GenTree* tree); |
5280 | |
5281 | public: |
5282 | GenTree* fgMorphTree(GenTree* tree, MorphAddrContext* mac = nullptr); |
5283 | |
5284 | private: |
5285 | #if LOCAL_ASSERTION_PROP |
5286 | void fgKillDependentAssertionsSingle(unsigned lclNum DEBUGARG(GenTree* tree)); |
5287 | void fgKillDependentAssertions(unsigned lclNum DEBUGARG(GenTree* tree)); |
5288 | #endif |
5289 | void fgMorphTreeDone(GenTree* tree, GenTree* oldTree = nullptr DEBUGARG(int morphNum = 0)); |
5290 | |
5291 | GenTreeStmt* fgMorphStmt; |
5292 | |
5293 | unsigned fgGetBigOffsetMorphingTemp(var_types type); // We cache one temp per type to be |
5294 | // used when morphing big offset. |
5295 | |
5296 | //----------------------- Liveness analysis ------------------------------- |
5297 | |
5298 | VARSET_TP fgCurUseSet; // vars used by block (before an assignment) |
5299 | VARSET_TP fgCurDefSet; // vars assigned by block (before a use) |
5300 | |
5301 | MemoryKindSet fgCurMemoryUse; // True iff the current basic block uses memory. |
5302 | MemoryKindSet fgCurMemoryDef; // True iff the current basic block modifies memory. |
5303 | MemoryKindSet fgCurMemoryHavoc; // True if the current basic block is known to set memory to a "havoc" value. |
5304 | |
5305 | bool byrefStatesMatchGcHeapStates; // True iff GcHeap and ByrefExposed memory have all the same def points. |
5306 | |
5307 | void fgMarkUseDef(GenTreeLclVarCommon* tree); |
5308 | |
5309 | void fgBeginScopeLife(VARSET_TP* inScope, VarScopeDsc* var); |
5310 | void fgEndScopeLife(VARSET_TP* inScope, VarScopeDsc* var); |
5311 | |
5312 | void fgMarkInScope(BasicBlock* block, VARSET_VALARG_TP inScope); |
5313 | void fgUnmarkInScope(BasicBlock* block, VARSET_VALARG_TP unmarkScope); |
5314 | |
5315 | void fgExtendDbgScopes(); |
5316 | void fgExtendDbgLifetimes(); |
5317 | |
5318 | #ifdef DEBUG |
5319 | void fgDispDebugScopes(); |
5320 | #endif // DEBUG |
5321 | |
5322 | //------------------------------------------------------------------------- |
5323 | // |
5324 | // The following keeps track of any code we've added for things like array |
5325 | // range checking or explicit calls to enable GC, and so on. |
5326 | // |
5327 | public: |
5328 | struct AddCodeDsc |
5329 | { |
5330 | AddCodeDsc* acdNext; |
5331 | BasicBlock* acdDstBlk; // block to which we jump |
5332 | unsigned acdData; |
5333 | SpecialCodeKind acdKind; // what kind of a special block is this? |
5334 | #if !FEATURE_FIXED_OUT_ARGS |
5335 | bool acdStkLvlInit; // has acdStkLvl value been already set? |
5336 | unsigned acdStkLvl; |
5337 | #endif // !FEATURE_FIXED_OUT_ARGS |
5338 | }; |
5339 | |
5340 | private: |
5341 | static unsigned acdHelper(SpecialCodeKind codeKind); |
5342 | |
5343 | AddCodeDsc* fgAddCodeList; |
5344 | bool fgAddCodeModf; |
5345 | bool fgRngChkThrowAdded; |
5346 | AddCodeDsc* fgExcptnTargetCache[SCK_COUNT]; |
5347 | |
5348 | BasicBlock* fgRngChkTarget(BasicBlock* block, SpecialCodeKind kind); |
5349 | |
5350 | BasicBlock* fgAddCodeRef(BasicBlock* srcBlk, unsigned refData, SpecialCodeKind kind); |
5351 | |
5352 | public: |
5353 | AddCodeDsc* fgFindExcptnTarget(SpecialCodeKind kind, unsigned refData); |
5354 | |
5355 | bool fgUseThrowHelperBlocks(); |
5356 | |
5357 | AddCodeDsc* fgGetAdditionalCodeDescriptors() |
5358 | { |
5359 | return fgAddCodeList; |
5360 | } |
5361 | |
5362 | private: |
5363 | bool fgIsCodeAdded(); |
5364 | |
5365 | bool fgIsThrowHlpBlk(BasicBlock* block); |
5366 | |
5367 | #if !FEATURE_FIXED_OUT_ARGS |
5368 | unsigned fgThrowHlpBlkStkLevel(BasicBlock* block); |
5369 | #endif // !FEATURE_FIXED_OUT_ARGS |
5370 | |
5371 | unsigned fgBigOffsetMorphingTemps[TYP_COUNT]; |
5372 | |
5373 | unsigned fgCheckInlineDepthAndRecursion(InlineInfo* inlineInfo); |
5374 | void fgInvokeInlineeCompiler(GenTreeCall* call, InlineResult* result); |
5375 | void fgInsertInlineeBlocks(InlineInfo* pInlineInfo); |
5376 | GenTree* fgInlinePrependStatements(InlineInfo* inlineInfo); |
5377 | void fgInlineAppendStatements(InlineInfo* inlineInfo, BasicBlock* block, GenTree* stmt); |
5378 | |
5379 | #if FEATURE_MULTIREG_RET |
5380 | GenTree* fgGetStructAsStructPtr(GenTree* tree); |
5381 | GenTree* fgAssignStructInlineeToVar(GenTree* child, CORINFO_CLASS_HANDLE retClsHnd); |
5382 | void fgAttachStructInlineeToAsg(GenTree* tree, GenTree* child, CORINFO_CLASS_HANDLE retClsHnd); |
5383 | #endif // FEATURE_MULTIREG_RET |
5384 | |
5385 | static fgWalkPreFn fgUpdateInlineReturnExpressionPlaceHolder; |
5386 | static fgWalkPostFn fgLateDevirtualization; |
5387 | |
5388 | #ifdef DEBUG |
5389 | static fgWalkPreFn fgDebugCheckInlineCandidates; |
5390 | |
5391 | void CheckNoTransformableIndirectCallsRemain(); |
5392 | static fgWalkPreFn fgDebugCheckForTransformableIndirectCalls; |
5393 | #endif |
5394 | |
5395 | void fgPromoteStructs(); |
5396 | void fgMorphStructField(GenTree* tree, GenTree* parent); |
5397 | void fgMorphLocalField(GenTree* tree, GenTree* parent); |
5398 | |
5399 | // Identify which parameters are implicit byrefs, and flag their LclVarDscs. |
5400 | void fgMarkImplicitByRefArgs(); |
5401 | |
5402 | // Change implicit byrefs' types from struct to pointer, and for any that were |
5403 | // promoted, create new promoted struct temps. |
5404 | void fgRetypeImplicitByRefArgs(); |
5405 | |
5406 | // Rewrite appearances of implicit byrefs (manifest the implied additional level of indirection). |
5407 | bool fgMorphImplicitByRefArgs(GenTree* tree); |
5408 | GenTree* fgMorphImplicitByRefArgs(GenTree* tree, bool isAddr); |
5409 | |
5410 | // Clear up annotations for any struct promotion temps created for implicit byrefs. |
5411 | void fgMarkDemotedImplicitByRefArgs(); |
5412 | |
5413 | void fgMarkAddressExposedLocals(); |
5414 | |
5415 | static fgWalkPreFn fgUpdateSideEffectsPre; |
5416 | static fgWalkPostFn fgUpdateSideEffectsPost; |
5417 | |
5418 | // The given local variable, required to be a struct variable, is being assigned via |
5419 | // a "lclField", to make it masquerade as an integral type in the ABI. Make sure that |
5420 | // the variable is not enregistered, and is therefore not promoted independently. |
5421 | void fgLclFldAssign(unsigned lclNum); |
5422 | |
5423 | static fgWalkPreFn gtHasLocalsWithAddrOpCB; |
5424 | |
5425 | enum TypeProducerKind |
5426 | { |
5427 | TPK_Unknown = 0, // May not be a RuntimeType |
5428 | TPK_Handle = 1, // RuntimeType via handle |
5429 | TPK_GetType = 2, // RuntimeType via Object.get_Type() |
5430 | TPK_Null = 3, // Tree value is null |
5431 | TPK_Other = 4 // RuntimeType via other means |
5432 | }; |
5433 | |
5434 | TypeProducerKind gtGetTypeProducerKind(GenTree* tree); |
5435 | bool gtIsTypeHandleToRuntimeTypeHelper(GenTreeCall* call); |
5436 | bool gtIsTypeHandleToRuntimeTypeHandleHelper(GenTreeCall* call, CorInfoHelpFunc* pHelper = nullptr); |
5437 | bool gtIsActiveCSE_Candidate(GenTree* tree); |
5438 | |
5439 | #ifdef DEBUG |
5440 | bool fgPrintInlinedMethods; |
5441 | #endif |
5442 | |
5443 | bool fgIsBigOffset(size_t offset); |
5444 | |
5445 | bool fgNeedReturnSpillTemp(); |
5446 | |
5447 | /* |
5448 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
5449 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
5450 | XX XX |
5451 | XX Optimizer XX |
5452 | XX XX |
5453 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
5454 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
5455 | */ |
5456 | |
5457 | public: |
5458 | void optInit(); |
5459 | |
5460 | void optRemoveRangeCheck(GenTree* tree, GenTree* stmt); |
5461 | bool optIsRangeCheckRemovable(GenTree* tree); |
5462 | |
5463 | protected: |
5464 | static fgWalkPreFn optValidRangeCheckIndex; |
5465 | static fgWalkPreFn optRemoveTreeVisitor; // Helper passed to Compiler::fgWalkAllTreesPre() to decrement the LclVar |
5466 | // usage counts |
5467 | |
5468 | void optRemoveTree(GenTree* deadTree, GenTree* keepList); |
5469 | |
5470 | /************************************************************************** |
5471 | * |
5472 | *************************************************************************/ |
5473 | |
5474 | protected: |
5475 | // Do hoisting for all loops. |
5476 | void optHoistLoopCode(); |
5477 | |
5478 | // To represent sets of VN's that have already been hoisted in outer loops. |
5479 | typedef JitHashTable<ValueNum, JitSmallPrimitiveKeyFuncs<ValueNum>, bool> VNToBoolMap; |
5480 | typedef VNToBoolMap VNSet; |
5481 | |
5482 | struct LoopHoistContext |
5483 | { |
5484 | private: |
5485 | // The set of variables hoisted in the current loop (or nullptr if there are none). |
5486 | VNSet* m_pHoistedInCurLoop; |
5487 | |
5488 | public: |
5489 | // Value numbers of expressions that have been hoisted in parent loops in the loop nest. |
5490 | VNSet m_hoistedInParentLoops; |
5491 | // Value numbers of expressions that have been hoisted in the current (or most recent) loop in the nest. |
5492 | // Previous decisions on loop-invariance of value numbers in the current loop. |
5493 | VNToBoolMap m_curLoopVnInvariantCache; |
5494 | |
5495 | VNSet* GetHoistedInCurLoop(Compiler* comp) |
5496 | { |
5497 | if (m_pHoistedInCurLoop == nullptr) |
5498 | { |
5499 | m_pHoistedInCurLoop = new (comp->getAllocatorLoopHoist()) VNSet(comp->getAllocatorLoopHoist()); |
5500 | } |
5501 | return m_pHoistedInCurLoop; |
5502 | } |
5503 | |
5504 | VNSet* () |
5505 | { |
5506 | VNSet* res = m_pHoistedInCurLoop; |
5507 | m_pHoistedInCurLoop = nullptr; |
5508 | return res; |
5509 | } |
5510 | |
5511 | LoopHoistContext(Compiler* comp) |
5512 | : m_pHoistedInCurLoop(nullptr) |
5513 | , m_hoistedInParentLoops(comp->getAllocatorLoopHoist()) |
5514 | , m_curLoopVnInvariantCache(comp->getAllocatorLoopHoist()) |
5515 | { |
5516 | } |
5517 | }; |
5518 | |
5519 | // Do hoisting for loop "lnum" (an index into the optLoopTable), and all loops nested within it. |
5520 | // Tracks the expressions that have been hoisted by containing loops by temporary recording their |
5521 | // value numbers in "m_hoistedInParentLoops". This set is not modified by the call. |
5522 | void optHoistLoopNest(unsigned lnum, LoopHoistContext* hoistCtxt); |
5523 | |
5524 | // Do hoisting for a particular loop ("lnum" is an index into the optLoopTable.) |
5525 | // Assumes that expressions have been hoisted in containing loops if their value numbers are in |
5526 | // "m_hoistedInParentLoops". |
5527 | // |
5528 | void optHoistThisLoop(unsigned lnum, LoopHoistContext* hoistCtxt); |
5529 | |
5530 | // Hoist all expressions in "blk" that are invariant in loop "lnum" (an index into the optLoopTable) |
5531 | // outside of that loop. Exempt expressions whose value number is in "m_hoistedInParentLoops"; add VN's of hoisted |
5532 | // expressions to "hoistInLoop". |
5533 | void optHoistLoopExprsForBlock(BasicBlock* blk, unsigned lnum, LoopHoistContext* hoistCtxt); |
5534 | |
5535 | // Return true if the tree looks profitable to hoist out of loop 'lnum'. |
5536 | bool optIsProfitableToHoistableTree(GenTree* tree, unsigned lnum); |
5537 | |
5538 | // Hoist all proper sub-expressions of "tree" (which occurs in "stmt", which occurs in "blk") |
5539 | // that are invariant in loop "lnum" (an index into the optLoopTable) |
5540 | // outside of that loop. Exempt expressions whose value number is in "hoistedInParents"; add VN's of hoisted |
5541 | // expressions to "hoistInLoop". |
5542 | // Returns "true" iff "tree" is loop-invariant (wrt "lnum"). |
5543 | // Assumes that the value of "*firstBlockAndBeforeSideEffect" indicates that we're in the first block, and before |
5544 | // any possible globally visible side effects. Assume is called in evaluation order, and updates this. |
5545 | bool optHoistLoopExprsForTree(GenTree* tree, |
5546 | unsigned lnum, |
5547 | LoopHoistContext* hoistCtxt, |
5548 | bool* firstBlockAndBeforeSideEffect, |
5549 | bool* pHoistable, |
5550 | bool* pCctorDependent); |
5551 | |
5552 | // Performs the hoisting 'tree' into the PreHeader for loop 'lnum' |
5553 | void optHoistCandidate(GenTree* tree, unsigned lnum, LoopHoistContext* hoistCtxt); |
5554 | |
5555 | // Returns true iff the ValueNum "vn" represents a value that is loop-invariant in "lnum". |
5556 | // Constants and init values are always loop invariant. |
5557 | // VNPhi's connect VN's to the SSA definition, so we can know if the SSA def occurs in the loop. |
5558 | bool optVNIsLoopInvariant(ValueNum vn, unsigned lnum, VNToBoolMap* recordedVNs); |
5559 | |
5560 | // Returns "true" iff "tree" is valid at the head of loop "lnum", in the context of the hoist substitution |
5561 | // "subst". If "tree" is a local SSA var, it is valid if its SSA definition occurs outside of the loop, or |
5562 | // if it is in the domain of "subst" (meaning that it's definition has been previously hoisted, with a "standin" |
5563 | // local.) If tree is a constant, it is valid. Otherwise, if it is an operator, it is valid iff its children are. |
5564 | bool optTreeIsValidAtLoopHead(GenTree* tree, unsigned lnum); |
5565 | |
5566 | // If "blk" is the entry block of a natural loop, returns true and sets "*pLnum" to the index of the loop |
5567 | // in the loop table. |
5568 | bool optBlockIsLoopEntry(BasicBlock* blk, unsigned* pLnum); |
5569 | |
5570 | // Records the set of "side effects" of all loops: fields (object instance and static) |
5571 | // written to, and SZ-array element type equivalence classes updated. |
5572 | void optComputeLoopSideEffects(); |
5573 | |
5574 | private: |
5575 | // Requires "lnum" to be the index of an outermost loop in the loop table. Traverses the body of that loop, |
5576 | // including all nested loops, and records the set of "side effects" of the loop: fields (object instance and |
5577 | // static) written to, and SZ-array element type equivalence classes updated. |
5578 | void optComputeLoopNestSideEffects(unsigned lnum); |
5579 | |
5580 | // Add the side effects of "blk" (which is required to be within a loop) to all loops of which it is a part. |
5581 | void optComputeLoopSideEffectsOfBlock(BasicBlock* blk); |
5582 | |
5583 | // Hoist the expression "expr" out of loop "lnum". |
5584 | void optPerformHoistExpr(GenTree* expr, unsigned lnum); |
5585 | |
5586 | public: |
5587 | void optOptimizeBools(); |
5588 | |
5589 | private: |
5590 | GenTree* optIsBoolCond(GenTree* condBranch, GenTree** compPtr, bool* boolPtr); |
5591 | #ifdef DEBUG |
5592 | void optOptimizeBoolsGcStress(BasicBlock* condBlock); |
5593 | #endif |
5594 | public: |
5595 | void optOptimizeLayout(); // Optimize the BasicBlock layout of the method |
5596 | |
5597 | void optOptimizeLoops(); // for "while-do" loops duplicates simple loop conditions and transforms |
5598 | // the loop into a "do-while" loop |
5599 | // Also finds all natural loops and records them in the loop table |
5600 | |
5601 | // Optionally clone loops in the loop table. |
5602 | void optCloneLoops(); |
5603 | |
5604 | // Clone loop "loopInd" in the loop table. |
5605 | void optCloneLoop(unsigned loopInd, LoopCloneContext* context); |
5606 | |
5607 | // Ensure that loop "loopInd" has a unique head block. (If the existing entry has |
5608 | // non-loop predecessors other than the head entry, create a new, empty block that goes (only) to the entry, |
5609 | // and redirects the preds of the entry to this new block.) Sets the weight of the newly created block to |
5610 | // "ambientWeight". |
5611 | void optEnsureUniqueHead(unsigned loopInd, unsigned ambientWeight); |
5612 | |
5613 | void optUnrollLoops(); // Unrolls loops (needs to have cost info) |
5614 | |
5615 | protected: |
5616 | // This enumeration describes what is killed by a call. |
5617 | |
5618 | enum callInterf |
5619 | { |
5620 | CALLINT_NONE, // no interference (most helpers) |
5621 | CALLINT_REF_INDIRS, // kills GC ref indirections (SETFIELD OBJ) |
5622 | CALLINT_SCL_INDIRS, // kills non GC ref indirections (SETFIELD non-OBJ) |
5623 | CALLINT_ALL_INDIRS, // kills both GC ref and non GC ref indirections (SETFIELD STRUCT) |
5624 | CALLINT_ALL, // kills everything (normal method call) |
5625 | }; |
5626 | |
5627 | public: |
5628 | // A "LoopDsc" describes a ("natural") loop. We (currently) require the body of a loop to be a contiguous (in |
5629 | // bbNext order) sequence of basic blocks. (At times, we may require the blocks in a loop to be "properly numbered" |
5630 | // in bbNext order; we use comparisons on the bbNum to decide order.) |
5631 | // The blocks that define the body are |
5632 | // first <= top <= entry <= bottom . |
5633 | // The "head" of the loop is a block outside the loop that has "entry" as a successor. We only support loops with a |
5634 | // single 'head' block. The meanings of these blocks are given in the definitions below. Also see the picture at |
5635 | // Compiler::optFindNaturalLoops(). |
5636 | struct LoopDsc |
5637 | { |
5638 | BasicBlock* lpHead; // HEAD of the loop (not part of the looping of the loop) -- has ENTRY as a successor. |
5639 | BasicBlock* lpFirst; // FIRST block (in bbNext order) reachable within this loop. (May be part of a nested |
5640 | // loop, but not the outer loop.) |
5641 | BasicBlock* lpTop; // loop TOP (the back edge from lpBottom reaches here) (in most cases FIRST and TOP are the |
5642 | // same) |
5643 | BasicBlock* lpEntry; // the ENTRY in the loop (in most cases TOP or BOTTOM) |
5644 | BasicBlock* lpBottom; // loop BOTTOM (from here we have a back edge to the TOP) |
5645 | BasicBlock* lpExit; // if a single exit loop this is the EXIT (in most cases BOTTOM) |
5646 | |
5647 | callInterf lpAsgCall; // "callInterf" for calls in the loop |
5648 | ALLVARSET_TP lpAsgVars; // set of vars assigned within the loop (all vars, not just tracked) |
5649 | varRefKinds lpAsgInds : 8; // set of inds modified within the loop |
5650 | |
5651 | unsigned short lpFlags; // Mask of the LPFLG_* constants |
5652 | |
5653 | unsigned char lpExitCnt; // number of exits from the loop |
5654 | |
5655 | unsigned char lpParent; // The index of the most-nested loop that completely contains this one, |
5656 | // or else BasicBlock::NOT_IN_LOOP if no such loop exists. |
5657 | unsigned char lpChild; // The index of a nested loop, or else BasicBlock::NOT_IN_LOOP if no child exists. |
5658 | // (Actually, an "immediately" nested loop -- |
5659 | // no other child of this loop is a parent of lpChild.) |
5660 | unsigned char lpSibling; // The index of another loop that is an immediate child of lpParent, |
5661 | // or else BasicBlock::NOT_IN_LOOP. One can enumerate all the children of a loop |
5662 | // by following "lpChild" then "lpSibling" links. |
5663 | |
5664 | #define LPFLG_DO_WHILE 0x0001 // it's a do-while loop (i.e ENTRY is at the TOP) |
5665 | #define LPFLG_ONE_EXIT 0x0002 // the loop has only one exit |
5666 | |
5667 | #define LPFLG_ITER 0x0004 // for (i = icon or lclVar; test_condition(); i++) |
5668 | #define LPFLG_HOISTABLE 0x0008 // the loop is in a form that is suitable for hoisting expressions |
5669 | #define LPFLG_CONST 0x0010 // for (i=icon;i<icon;i++){ ... } - constant loop |
5670 | |
5671 | #define LPFLG_VAR_INIT 0x0020 // iterator is initialized with a local var (var # found in lpVarInit) |
5672 | #define LPFLG_CONST_INIT 0x0040 // iterator is initialized with a constant (found in lpConstInit) |
5673 | |
5674 | #define LPFLG_VAR_LIMIT 0x0100 // iterator is compared with a local var (var # found in lpVarLimit) |
5675 | #define LPFLG_CONST_LIMIT 0x0200 // iterator is compared with a constant (found in lpConstLimit) |
5676 | #define LPFLG_ARRLEN_LIMIT 0x0400 // iterator is compared with a.len or a[i].len (found in lpArrLenLimit) |
5677 | #define LPFLG_SIMD_LIMIT 0x0080 // iterator is compared with Vector<T>.Count (found in lpConstLimit) |
5678 | |
5679 | #define LPFLG_HAS_PREHEAD 0x0800 // lpHead is known to be a preHead for this loop |
5680 | #define LPFLG_REMOVED 0x1000 // has been removed from the loop table (unrolled or optimized away) |
5681 | #define LPFLG_DONT_UNROLL 0x2000 // do not unroll this loop |
5682 | |
5683 | #define LPFLG_ASGVARS_YES 0x4000 // "lpAsgVars" has been computed |
5684 | #define LPFLG_ASGVARS_INC 0x8000 // "lpAsgVars" is incomplete -- vars beyond those representable in an AllVarSet |
5685 | // type are assigned to. |
5686 | |
5687 | bool lpLoopHasMemoryHavoc[MemoryKindCount]; // The loop contains an operation that we assume has arbitrary |
5688 | // memory side effects. If this is set, the fields below |
5689 | // may not be accurate (since they become irrelevant.) |
5690 | bool lpContainsCall; // True if executing the loop body *may* execute a call |
5691 | |
5692 | VARSET_TP lpVarInOut; // The set of variables that are IN or OUT during the execution of this loop |
5693 | VARSET_TP lpVarUseDef; // The set of variables that are USE or DEF during the execution of this loop |
5694 | |
5695 | int lpHoistedExprCount; // The register count for the non-FP expressions from inside this loop that have been |
5696 | // hoisted |
5697 | int lpLoopVarCount; // The register count for the non-FP LclVars that are read/written inside this loop |
5698 | int lpVarInOutCount; // The register count for the non-FP LclVars that are alive inside or accross this loop |
5699 | |
5700 | int lpHoistedFPExprCount; // The register count for the FP expressions from inside this loop that have been |
5701 | // hoisted |
5702 | int lpLoopVarFPCount; // The register count for the FP LclVars that are read/written inside this loop |
5703 | int lpVarInOutFPCount; // The register count for the FP LclVars that are alive inside or accross this loop |
5704 | |
5705 | typedef JitHashTable<CORINFO_FIELD_HANDLE, JitPtrKeyFuncs<struct CORINFO_FIELD_STRUCT_>, bool> FieldHandleSet; |
5706 | FieldHandleSet* lpFieldsModified; // This has entries (mappings to "true") for all static field and object |
5707 | // instance fields modified |
5708 | // in the loop. |
5709 | |
5710 | typedef JitHashTable<CORINFO_CLASS_HANDLE, JitPtrKeyFuncs<struct CORINFO_CLASS_STRUCT_>, bool> ClassHandleSet; |
5711 | ClassHandleSet* lpArrayElemTypesModified; // Bits set indicate the set of sz array element types such that |
5712 | // arrays of that type are modified |
5713 | // in the loop. |
5714 | |
5715 | // Adds the variable liveness information for 'blk' to 'this' LoopDsc |
5716 | void AddVariableLiveness(Compiler* comp, BasicBlock* blk); |
5717 | |
5718 | inline void AddModifiedField(Compiler* comp, CORINFO_FIELD_HANDLE fldHnd); |
5719 | // This doesn't *always* take a class handle -- it can also take primitive types, encoded as class handles |
5720 | // (shifted left, with a low-order bit set to distinguish.) |
5721 | // Use the {Encode/Decode}ElemType methods to construct/destruct these. |
5722 | inline void AddModifiedElemType(Compiler* comp, CORINFO_CLASS_HANDLE structHnd); |
5723 | |
5724 | /* The following values are set only for iterator loops, i.e. has the flag LPFLG_ITER set */ |
5725 | |
5726 | GenTree* lpIterTree; // The "i = i <op> const" tree |
5727 | unsigned lpIterVar(); // iterator variable # |
5728 | int lpIterConst(); // the constant with which the iterator is incremented |
5729 | genTreeOps lpIterOper(); // the type of the operation on the iterator (ASG_ADD, ASG_SUB, etc.) |
5730 | void VERIFY_lpIterTree(); |
5731 | |
5732 | var_types lpIterOperType(); // For overflow instructions |
5733 | |
5734 | union { |
5735 | int lpConstInit; // initial constant value of iterator : Valid if LPFLG_CONST_INIT |
5736 | unsigned lpVarInit; // initial local var number to which we initialize the iterator : Valid if |
5737 | // LPFLG_VAR_INIT |
5738 | }; |
5739 | |
5740 | /* The following is for LPFLG_ITER loops only (i.e. the loop condition is "i RELOP const or var" */ |
5741 | |
5742 | GenTree* lpTestTree; // pointer to the node containing the loop test |
5743 | genTreeOps lpTestOper(); // the type of the comparison between the iterator and the limit (GT_LE, GT_GE, etc.) |
5744 | void VERIFY_lpTestTree(); |
5745 | |
5746 | bool lpIsReversed(); // true if the iterator node is the second operand in the loop condition |
5747 | GenTree* lpIterator(); // the iterator node in the loop test |
5748 | GenTree* lpLimit(); // the limit node in the loop test |
5749 | |
5750 | int lpConstLimit(); // limit constant value of iterator - loop condition is "i RELOP const" : Valid if |
5751 | // LPFLG_CONST_LIMIT |
5752 | unsigned lpVarLimit(); // the lclVar # in the loop condition ( "i RELOP lclVar" ) : Valid if |
5753 | // LPFLG_VAR_LIMIT |
5754 | bool lpArrLenLimit(Compiler* comp, ArrIndex* index); // The array length in the loop condition ( "i RELOP |
5755 | // arr.len" or "i RELOP arr[i][j].len" ) : Valid if |
5756 | // LPFLG_ARRLEN_LIMIT |
5757 | |
5758 | // Returns "true" iff "*this" contains the blk. |
5759 | bool lpContains(BasicBlock* blk) |
5760 | { |
5761 | return lpFirst->bbNum <= blk->bbNum && blk->bbNum <= lpBottom->bbNum; |
5762 | } |
5763 | // Returns "true" iff "*this" (properly) contains the range [first, bottom] (allowing firsts |
5764 | // to be equal, but requiring bottoms to be different.) |
5765 | bool lpContains(BasicBlock* first, BasicBlock* bottom) |
5766 | { |
5767 | return lpFirst->bbNum <= first->bbNum && bottom->bbNum < lpBottom->bbNum; |
5768 | } |
5769 | |
5770 | // Returns "true" iff "*this" (properly) contains "lp2" (allowing firsts to be equal, but requiring |
5771 | // bottoms to be different.) |
5772 | bool lpContains(const LoopDsc& lp2) |
5773 | { |
5774 | return lpContains(lp2.lpFirst, lp2.lpBottom); |
5775 | } |
5776 | |
5777 | // Returns "true" iff "*this" is (properly) contained by the range [first, bottom] |
5778 | // (allowing firsts to be equal, but requiring bottoms to be different.) |
5779 | bool lpContainedBy(BasicBlock* first, BasicBlock* bottom) |
5780 | { |
5781 | return first->bbNum <= lpFirst->bbNum && lpBottom->bbNum < bottom->bbNum; |
5782 | } |
5783 | |
5784 | // Returns "true" iff "*this" is (properly) contained by "lp2" |
5785 | // (allowing firsts to be equal, but requiring bottoms to be different.) |
5786 | bool lpContainedBy(const LoopDsc& lp2) |
5787 | { |
5788 | return lpContains(lp2.lpFirst, lp2.lpBottom); |
5789 | } |
5790 | |
5791 | // Returns "true" iff "*this" is disjoint from the range [top, bottom]. |
5792 | bool lpDisjoint(BasicBlock* first, BasicBlock* bottom) |
5793 | { |
5794 | return bottom->bbNum < lpFirst->bbNum || lpBottom->bbNum < first->bbNum; |
5795 | } |
5796 | // Returns "true" iff "*this" is disjoint from "lp2". |
5797 | bool lpDisjoint(const LoopDsc& lp2) |
5798 | { |
5799 | return lpDisjoint(lp2.lpFirst, lp2.lpBottom); |
5800 | } |
5801 | // Returns "true" iff the loop is well-formed (see code for defn). |
5802 | bool lpWellFormed() |
5803 | { |
5804 | return lpFirst->bbNum <= lpTop->bbNum && lpTop->bbNum <= lpEntry->bbNum && |
5805 | lpEntry->bbNum <= lpBottom->bbNum && |
5806 | (lpHead->bbNum < lpTop->bbNum || lpHead->bbNum > lpBottom->bbNum); |
5807 | } |
5808 | }; |
5809 | |
5810 | protected: |
5811 | bool fgMightHaveLoop(); // returns true if there are any backedges |
5812 | bool fgHasLoops; // True if this method has any loops, set in fgComputeReachability |
5813 | |
5814 | public: |
5815 | LoopDsc* optLoopTable; // loop descriptor table |
5816 | unsigned char optLoopCount; // number of tracked loops |
5817 | |
5818 | bool optRecordLoop(BasicBlock* head, |
5819 | BasicBlock* first, |
5820 | BasicBlock* top, |
5821 | BasicBlock* entry, |
5822 | BasicBlock* bottom, |
5823 | BasicBlock* exit, |
5824 | unsigned char exitCnt); |
5825 | |
5826 | protected: |
5827 | unsigned optCallCount; // number of calls made in the method |
5828 | unsigned optIndirectCallCount; // number of virtual, interface and indirect calls made in the method |
5829 | unsigned optNativeCallCount; // number of Pinvoke/Native calls made in the method |
5830 | unsigned optLoopsCloned; // number of loops cloned in the current method. |
5831 | |
5832 | #ifdef DEBUG |
5833 | unsigned optFindLoopNumberFromBeginBlock(BasicBlock* begBlk); |
5834 | void optPrintLoopInfo(unsigned loopNum, |
5835 | BasicBlock* lpHead, |
5836 | BasicBlock* lpFirst, |
5837 | BasicBlock* lpTop, |
5838 | BasicBlock* lpEntry, |
5839 | BasicBlock* lpBottom, |
5840 | unsigned char lpExitCnt, |
5841 | BasicBlock* lpExit, |
5842 | unsigned parentLoop = BasicBlock::NOT_IN_LOOP); |
5843 | void optPrintLoopInfo(unsigned lnum); |
5844 | void optPrintLoopRecording(unsigned lnum); |
5845 | |
5846 | void optCheckPreds(); |
5847 | #endif |
5848 | |
5849 | void optSetBlockWeights(); |
5850 | |
5851 | void optMarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk, bool excludeEndBlk); |
5852 | |
5853 | void optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk); |
5854 | |
5855 | void optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmarkLoop = false); |
5856 | |
5857 | bool optIsLoopTestEvalIntoTemp(GenTree* test, GenTree** newTest); |
5858 | unsigned optIsLoopIncrTree(GenTree* incr); |
5859 | bool optCheckIterInLoopTest(unsigned loopInd, GenTree* test, BasicBlock* from, BasicBlock* to, unsigned iterVar); |
5860 | bool optComputeIterInfo(GenTree* incr, BasicBlock* from, BasicBlock* to, unsigned* pIterVar); |
5861 | bool optPopulateInitInfo(unsigned loopInd, GenTree* init, unsigned iterVar); |
5862 | bool ( |
5863 | BasicBlock* head, BasicBlock* bottom, BasicBlock* exit, GenTree** ppInit, GenTree** ppTest, GenTree** ppIncr); |
5864 | |
5865 | void optFindNaturalLoops(); |
5866 | |
5867 | // Ensures that all the loops in the loop nest rooted at "loopInd" (an index into the loop table) are 'canonical' -- |
5868 | // each loop has a unique "top." Returns "true" iff the flowgraph has been modified. |
5869 | bool optCanonicalizeLoopNest(unsigned char loopInd); |
5870 | |
5871 | // Ensures that the loop "loopInd" (an index into the loop table) is 'canonical' -- it has a unique "top," |
5872 | // unshared with any other loop. Returns "true" iff the flowgraph has been modified |
5873 | bool optCanonicalizeLoop(unsigned char loopInd); |
5874 | |
5875 | // Requires "l1" to be a valid loop table index, and not "BasicBlock::NOT_IN_LOOP". Requires "l2" to be |
5876 | // a valid loop table index, or else "BasicBlock::NOT_IN_LOOP". Returns true |
5877 | // iff "l2" is not NOT_IN_LOOP, and "l1" contains "l2". |
5878 | bool optLoopContains(unsigned l1, unsigned l2); |
5879 | |
5880 | // Requires "loopInd" to be a valid index into the loop table. |
5881 | // Updates the loop table by changing loop "loopInd", whose head is required |
5882 | // to be "from", to be "to". Also performs this transformation for any |
5883 | // loop nested in "loopInd" that shares the same head as "loopInd". |
5884 | void optUpdateLoopHead(unsigned loopInd, BasicBlock* from, BasicBlock* to); |
5885 | |
5886 | // Updates the successors of "blk": if "blk2" is a successor of "blk", and there is a mapping for "blk2->blk3" in |
5887 | // "redirectMap", change "blk" so that "blk3" is this successor. Note that the predecessor lists are not updated. |
5888 | void optRedirectBlock(BasicBlock* blk, BlockToBlockMap* redirectMap); |
5889 | |
5890 | // Marks the containsCall information to "lnum" and any parent loops. |
5891 | void AddContainsCallAllContainingLoops(unsigned lnum); |
5892 | // Adds the variable liveness information from 'blk' to "lnum" and any parent loops. |
5893 | void AddVariableLivenessAllContainingLoops(unsigned lnum, BasicBlock* blk); |
5894 | // Adds "fldHnd" to the set of modified fields of "lnum" and any parent loops. |
5895 | void AddModifiedFieldAllContainingLoops(unsigned lnum, CORINFO_FIELD_HANDLE fldHnd); |
5896 | // Adds "elemType" to the set of modified array element types of "lnum" and any parent loops. |
5897 | void AddModifiedElemTypeAllContainingLoops(unsigned lnum, CORINFO_CLASS_HANDLE elemType); |
5898 | |
5899 | // Requires that "from" and "to" have the same "bbJumpKind" (perhaps because "to" is a clone |
5900 | // of "from".) Copies the jump destination from "from" to "to". |
5901 | void optCopyBlkDest(BasicBlock* from, BasicBlock* to); |
5902 | |
5903 | // The depth of the loop described by "lnum" (an index into the loop table.) (0 == top level) |
5904 | unsigned optLoopDepth(unsigned lnum) |
5905 | { |
5906 | unsigned par = optLoopTable[lnum].lpParent; |
5907 | if (par == BasicBlock::NOT_IN_LOOP) |
5908 | { |
5909 | return 0; |
5910 | } |
5911 | else |
5912 | { |
5913 | return 1 + optLoopDepth(par); |
5914 | } |
5915 | } |
5916 | |
5917 | void fgOptWhileLoop(BasicBlock* block); |
5918 | |
5919 | bool optComputeLoopRep(int constInit, |
5920 | int constLimit, |
5921 | int iterInc, |
5922 | genTreeOps iterOper, |
5923 | var_types iterType, |
5924 | genTreeOps testOper, |
5925 | bool unsignedTest, |
5926 | bool dupCond, |
5927 | unsigned* iterCount); |
5928 | |
5929 | private: |
5930 | static fgWalkPreFn optIsVarAssgCB; |
5931 | |
5932 | protected: |
5933 | bool optIsVarAssigned(BasicBlock* beg, BasicBlock* end, GenTree* skip, unsigned var); |
5934 | |
5935 | bool optIsVarAssgLoop(unsigned lnum, unsigned var); |
5936 | |
5937 | int optIsSetAssgLoop(unsigned lnum, ALLVARSET_VALARG_TP vars, varRefKinds inds = VR_NONE); |
5938 | |
5939 | bool optNarrowTree(GenTree* tree, var_types srct, var_types dstt, ValueNumPair vnpNarrow, bool doit); |
5940 | |
5941 | /************************************************************************** |
5942 | * Optimization conditions |
5943 | *************************************************************************/ |
5944 | |
5945 | bool optFastCodeOrBlendedLoop(BasicBlock::weight_t bbWeight); |
5946 | bool optPentium4(void); |
5947 | bool optAvoidIncDec(BasicBlock::weight_t bbWeight); |
5948 | bool optAvoidIntMult(void); |
5949 | |
5950 | #if FEATURE_ANYCSE |
5951 | |
5952 | protected: |
5953 | // The following is the upper limit on how many expressions we'll keep track |
5954 | // of for the CSE analysis. |
5955 | // |
5956 | static const unsigned MAX_CSE_CNT = EXPSET_SZ; |
5957 | |
5958 | static const int MIN_CSE_COST = 2; |
5959 | |
5960 | // Keeps tracked cse indices |
5961 | BitVecTraits* cseTraits; |
5962 | EXPSET_TP cseFull; |
5963 | |
5964 | /* Generic list of nodes - used by the CSE logic */ |
5965 | |
5966 | struct treeLst |
5967 | { |
5968 | treeLst* tlNext; |
5969 | GenTree* tlTree; |
5970 | }; |
5971 | |
5972 | struct treeStmtLst |
5973 | { |
5974 | treeStmtLst* tslNext; |
5975 | GenTree* tslTree; // tree node |
5976 | GenTree* tslStmt; // statement containing the tree |
5977 | BasicBlock* tslBlock; // block containing the statement |
5978 | }; |
5979 | |
5980 | // The following logic keeps track of expressions via a simple hash table. |
5981 | |
5982 | struct CSEdsc |
5983 | { |
5984 | CSEdsc* csdNextInBucket; // used by the hash table |
5985 | |
5986 | unsigned csdHashKey; // the orginal hashkey |
5987 | |
5988 | unsigned csdIndex; // 1..optCSECandidateCount |
5989 | char csdLiveAcrossCall; // 0 or 1 |
5990 | |
5991 | unsigned short csdDefCount; // definition count |
5992 | unsigned short csdUseCount; // use count (excluding the implicit uses at defs) |
5993 | |
5994 | unsigned csdDefWtCnt; // weighted def count |
5995 | unsigned csdUseWtCnt; // weighted use count (excluding the implicit uses at defs) |
5996 | |
5997 | GenTree* csdTree; // treenode containing the 1st occurance |
5998 | GenTree* csdStmt; // stmt containing the 1st occurance |
5999 | BasicBlock* csdBlock; // block containing the 1st occurance |
6000 | |
6001 | treeStmtLst* csdTreeList; // list of matching tree nodes: head |
6002 | treeStmtLst* csdTreeLast; // list of matching tree nodes: tail |
6003 | |
6004 | ValueNum defExcSetPromise; // The exception set that is now required for all defs of this CSE. |
6005 | // This will be set to NoVN if we decide to abandon this CSE |
6006 | |
6007 | ValueNum defExcSetCurrent; // The set of exceptions we currently can use for CSE uses. |
6008 | |
6009 | ValueNum defConservNormVN; // if all def occurrences share the same conservative normal value |
6010 | // number, this will reflect it; otherwise, NoVN. |
6011 | }; |
6012 | |
6013 | static const size_t s_optCSEhashSize; |
6014 | CSEdsc** optCSEhash; |
6015 | CSEdsc** optCSEtab; |
6016 | |
6017 | typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, GenTree*> NodeToNodeMap; |
6018 | |
6019 | NodeToNodeMap* optCseCheckedBoundMap; // Maps bound nodes to ancestor compares that should be |
6020 | // re-numbered with the bound to improve range check elimination |
6021 | |
6022 | // Given a compare, look for a cse candidate checked bound feeding it and add a map entry if found. |
6023 | void optCseUpdateCheckedBoundMap(GenTree* compare); |
6024 | |
6025 | void optCSEstop(); |
6026 | |
6027 | CSEdsc* optCSEfindDsc(unsigned index); |
6028 | bool optUnmarkCSE(GenTree* tree); |
6029 | |
6030 | // user defined callback data for the tree walk function optCSE_MaskHelper() |
6031 | struct optCSE_MaskData |
6032 | { |
6033 | EXPSET_TP CSE_defMask; |
6034 | EXPSET_TP CSE_useMask; |
6035 | }; |
6036 | |
6037 | // Treewalk helper for optCSE_DefMask and optCSE_UseMask |
6038 | static fgWalkPreFn optCSE_MaskHelper; |
6039 | |
6040 | // This function walks all the node for an given tree |
6041 | // and return the mask of CSE definitions and uses for the tree |
6042 | // |
6043 | void optCSE_GetMaskData(GenTree* tree, optCSE_MaskData* pMaskData); |
6044 | |
6045 | // Given a binary tree node return true if it is safe to swap the order of evaluation for op1 and op2. |
6046 | bool optCSE_canSwap(GenTree* firstNode, GenTree* secondNode); |
6047 | bool optCSE_canSwap(GenTree* tree); |
6048 | |
6049 | static int __cdecl optCSEcostCmpEx(const void* op1, const void* op2); |
6050 | static int __cdecl optCSEcostCmpSz(const void* op1, const void* op2); |
6051 | |
6052 | void optCleanupCSEs(); |
6053 | |
6054 | #ifdef DEBUG |
6055 | void optEnsureClearCSEInfo(); |
6056 | #endif // DEBUG |
6057 | |
6058 | #endif // FEATURE_ANYCSE |
6059 | |
6060 | #if FEATURE_VALNUM_CSE |
6061 | /************************************************************************** |
6062 | * Value Number based CSEs |
6063 | *************************************************************************/ |
6064 | |
6065 | public: |
6066 | void optOptimizeValnumCSEs(); |
6067 | |
6068 | protected: |
6069 | void optValnumCSE_Init(); |
6070 | unsigned optValnumCSE_Index(GenTree* tree, GenTree* stmt); |
6071 | unsigned optValnumCSE_Locate(); |
6072 | void optValnumCSE_InitDataFlow(); |
6073 | void optValnumCSE_DataFlow(); |
6074 | void optValnumCSE_Availablity(); |
6075 | void optValnumCSE_Heuristic(); |
6076 | |
6077 | #endif // FEATURE_VALNUM_CSE |
6078 | |
6079 | #if FEATURE_ANYCSE |
6080 | bool optDoCSE; // True when we have found a duplicate CSE tree |
6081 | bool optValnumCSE_phase; // True when we are executing the optValnumCSE_phase |
6082 | unsigned optCSECandidateTotal; // Grand total of CSE candidates for both Lexical and ValNum |
6083 | unsigned optCSECandidateCount; // Count of CSE's candidates, reset for Lexical and ValNum CSE's |
6084 | unsigned optCSEstart; // The first local variable number that is a CSE |
6085 | unsigned optCSEcount; // The total count of CSE's introduced. |
6086 | unsigned optCSEweight; // The weight of the current block when we are |
6087 | // scanning for CSE expressions |
6088 | |
6089 | bool optIsCSEcandidate(GenTree* tree); |
6090 | |
6091 | // lclNumIsTrueCSE returns true if the LclVar was introduced by the CSE phase of the compiler |
6092 | // |
6093 | bool lclNumIsTrueCSE(unsigned lclNum) const |
6094 | { |
6095 | return ((optCSEcount > 0) && (lclNum >= optCSEstart) && (lclNum < optCSEstart + optCSEcount)); |
6096 | } |
6097 | |
6098 | // lclNumIsCSE returns true if the LclVar should be treated like a CSE with regards to constant prop. |
6099 | // |
6100 | bool lclNumIsCSE(unsigned lclNum) const |
6101 | { |
6102 | return lvaTable[lclNum].lvIsCSE; |
6103 | } |
6104 | |
6105 | #ifdef DEBUG |
6106 | bool optConfigDisableCSE(); |
6107 | bool optConfigDisableCSE2(); |
6108 | #endif |
6109 | void optOptimizeCSEs(); |
6110 | |
6111 | #endif // FEATURE_ANYCSE |
6112 | |
6113 | struct isVarAssgDsc |
6114 | { |
6115 | GenTree* ivaSkip; |
6116 | #ifdef DEBUG |
6117 | void* ivaSelf; |
6118 | #endif |
6119 | unsigned ivaVar; // Variable we are interested in, or -1 |
6120 | ALLVARSET_TP ivaMaskVal; // Set of variables assigned to. This is a set of all vars, not tracked vars. |
6121 | bool ivaMaskIncomplete; // Variables not representable in ivaMaskVal were assigned to. |
6122 | varRefKinds ivaMaskInd; // What kind of indirect assignments are there? |
6123 | callInterf ivaMaskCall; // What kind of calls are there? |
6124 | }; |
6125 | |
6126 | static callInterf optCallInterf(GenTreeCall* call); |
6127 | |
6128 | public: |
6129 | // VN based copy propagation. |
6130 | typedef ArrayStack<GenTree*> GenTreePtrStack; |
6131 | typedef JitHashTable<unsigned, JitSmallPrimitiveKeyFuncs<unsigned>, GenTreePtrStack*> LclNumToGenTreePtrStack; |
6132 | |
6133 | // Kill set to track variables with intervening definitions. |
6134 | VARSET_TP optCopyPropKillSet; |
6135 | |
6136 | // Copy propagation functions. |
6137 | void optCopyProp(BasicBlock* block, GenTree* stmt, GenTree* tree, LclNumToGenTreePtrStack* ); |
6138 | void optBlockCopyPropPopStacks(BasicBlock* block, LclNumToGenTreePtrStack* ); |
6139 | void optBlockCopyProp(BasicBlock* block, LclNumToGenTreePtrStack* ); |
6140 | bool optIsSsaLocal(GenTree* tree); |
6141 | int optCopyProp_LclVarScore(LclVarDsc* lclVarDsc, LclVarDsc* copyVarDsc, bool preferOp2); |
6142 | void optVnCopyProp(); |
6143 | INDEBUG(void optDumpCopyPropStack(LclNumToGenTreePtrStack* )); |
6144 | |
6145 | /************************************************************************** |
6146 | * Early value propagation |
6147 | *************************************************************************/ |
6148 | struct SSAName |
6149 | { |
6150 | unsigned m_lvNum; |
6151 | unsigned m_ssaNum; |
6152 | |
6153 | SSAName(unsigned lvNum, unsigned ssaNum) : m_lvNum(lvNum), m_ssaNum(ssaNum) |
6154 | { |
6155 | } |
6156 | |
6157 | static unsigned GetHashCode(SSAName ssaNm) |
6158 | { |
6159 | return (ssaNm.m_lvNum << 16) | (ssaNm.m_ssaNum); |
6160 | } |
6161 | |
6162 | static bool Equals(SSAName ssaNm1, SSAName ssaNm2) |
6163 | { |
6164 | return (ssaNm1.m_lvNum == ssaNm2.m_lvNum) && (ssaNm1.m_ssaNum == ssaNm2.m_ssaNum); |
6165 | } |
6166 | }; |
6167 | |
6168 | #define OMF_HAS_NEWARRAY 0x00000001 // Method contains 'new' of an array |
6169 | #define OMF_HAS_NEWOBJ 0x00000002 // Method contains 'new' of an object type. |
6170 | #define OMF_HAS_ARRAYREF 0x00000004 // Method contains array element loads or stores. |
6171 | #define OMF_HAS_VTABLEREF 0x00000008 // Method contains method table reference. |
6172 | #define OMF_HAS_NULLCHECK 0x00000010 // Method contains null check. |
6173 | #define OMF_HAS_FATPOINTER 0x00000020 // Method contains call, that needs fat pointer transformation. |
6174 | #define OMF_HAS_OBJSTACKALLOC 0x00000040 // Method contains an object allocated on the stack. |
6175 | #define OMF_HAS_GUARDEDDEVIRT 0x00000080 // Method contains guarded devirtualization candidate |
6176 | |
6177 | bool doesMethodHaveFatPointer() |
6178 | { |
6179 | return (optMethodFlags & OMF_HAS_FATPOINTER) != 0; |
6180 | } |
6181 | |
6182 | void setMethodHasFatPointer() |
6183 | { |
6184 | optMethodFlags |= OMF_HAS_FATPOINTER; |
6185 | } |
6186 | |
6187 | void clearMethodHasFatPointer() |
6188 | { |
6189 | optMethodFlags &= ~OMF_HAS_FATPOINTER; |
6190 | } |
6191 | |
6192 | void addFatPointerCandidate(GenTreeCall* call); |
6193 | |
6194 | bool doesMethodHaveGuardedDevirtualization() |
6195 | { |
6196 | return (optMethodFlags & OMF_HAS_GUARDEDDEVIRT) != 0; |
6197 | } |
6198 | |
6199 | void setMethodHasGuardedDevirtualization() |
6200 | { |
6201 | optMethodFlags |= OMF_HAS_GUARDEDDEVIRT; |
6202 | } |
6203 | |
6204 | void clearMethodHasGuardedDevirtualization() |
6205 | { |
6206 | optMethodFlags &= ~OMF_HAS_GUARDEDDEVIRT; |
6207 | } |
6208 | |
6209 | void addGuardedDevirtualizationCandidate(GenTreeCall* call, |
6210 | CORINFO_METHOD_HANDLE methodHandle, |
6211 | CORINFO_CLASS_HANDLE classHandle, |
6212 | unsigned methodAttr, |
6213 | unsigned classAttr); |
6214 | |
6215 | unsigned optMethodFlags; |
6216 | |
6217 | // Recursion bound controls how far we can go backwards tracking for a SSA value. |
6218 | // No throughput diff was found with backward walk bound between 3-8. |
6219 | static const int optEarlyPropRecurBound = 5; |
6220 | |
6221 | enum class optPropKind |
6222 | { |
6223 | OPK_INVALID, |
6224 | OPK_ARRAYLEN, |
6225 | OPK_OBJ_GETTYPE, |
6226 | OPK_NULLCHECK |
6227 | }; |
6228 | |
6229 | bool gtIsVtableRef(GenTree* tree); |
6230 | GenTree* getArrayLengthFromAllocation(GenTree* tree); |
6231 | GenTree* getObjectHandleNodeFromAllocation(GenTree* tree); |
6232 | GenTree* optPropGetValueRec(unsigned lclNum, unsigned ssaNum, optPropKind valueKind, int walkDepth); |
6233 | GenTree* optPropGetValue(unsigned lclNum, unsigned ssaNum, optPropKind valueKind); |
6234 | GenTree* optEarlyPropRewriteTree(GenTree* tree); |
6235 | bool optDoEarlyPropForBlock(BasicBlock* block); |
6236 | bool optDoEarlyPropForFunc(); |
6237 | void optEarlyProp(); |
6238 | void optFoldNullCheck(GenTree* tree); |
6239 | bool optCanMoveNullCheckPastTree(GenTree* tree, bool isInsideTry); |
6240 | |
6241 | #if ASSERTION_PROP |
6242 | /************************************************************************** |
6243 | * Value/Assertion propagation |
6244 | *************************************************************************/ |
6245 | public: |
6246 | // Data structures for assertion prop |
6247 | BitVecTraits* apTraits; |
6248 | ASSERT_TP apFull; |
6249 | |
6250 | enum optAssertionKind |
6251 | { |
6252 | OAK_INVALID, |
6253 | OAK_EQUAL, |
6254 | OAK_NOT_EQUAL, |
6255 | OAK_SUBRANGE, |
6256 | OAK_NO_THROW, |
6257 | OAK_COUNT |
6258 | }; |
6259 | |
6260 | enum optOp1Kind |
6261 | { |
6262 | O1K_INVALID, |
6263 | O1K_LCLVAR, |
6264 | O1K_ARR_BND, |
6265 | O1K_BOUND_OPER_BND, |
6266 | O1K_BOUND_LOOP_BND, |
6267 | O1K_CONSTANT_LOOP_BND, |
6268 | O1K_EXACT_TYPE, |
6269 | O1K_SUBTYPE, |
6270 | O1K_VALUE_NUMBER, |
6271 | O1K_COUNT |
6272 | }; |
6273 | |
6274 | enum optOp2Kind |
6275 | { |
6276 | O2K_INVALID, |
6277 | O2K_LCLVAR_COPY, |
6278 | O2K_IND_CNS_INT, |
6279 | O2K_CONST_INT, |
6280 | O2K_CONST_LONG, |
6281 | O2K_CONST_DOUBLE, |
6282 | O2K_ARR_LEN, |
6283 | O2K_SUBRANGE, |
6284 | O2K_COUNT |
6285 | }; |
6286 | struct AssertionDsc |
6287 | { |
6288 | optAssertionKind assertionKind; |
6289 | struct SsaVar |
6290 | { |
6291 | unsigned lclNum; // assigned to or property of this local var number |
6292 | unsigned ssaNum; |
6293 | }; |
6294 | struct ArrBnd |
6295 | { |
6296 | ValueNum vnIdx; |
6297 | ValueNum vnLen; |
6298 | }; |
6299 | struct AssertionDscOp1 |
6300 | { |
6301 | optOp1Kind kind; // a normal LclVar, or Exact-type or Subtype |
6302 | ValueNum vn; |
6303 | union { |
6304 | SsaVar lcl; |
6305 | ArrBnd bnd; |
6306 | }; |
6307 | } op1; |
6308 | struct AssertionDscOp2 |
6309 | { |
6310 | optOp2Kind kind; // a const or copy assignment |
6311 | ValueNum vn; |
6312 | struct IntVal |
6313 | { |
6314 | ssize_t iconVal; // integer |
6315 | unsigned iconFlags; // gtFlags |
6316 | }; |
6317 | struct Range // integer subrange |
6318 | { |
6319 | ssize_t loBound; |
6320 | ssize_t hiBound; |
6321 | }; |
6322 | union { |
6323 | SsaVar lcl; |
6324 | IntVal u1; |
6325 | __int64 lconVal; |
6326 | double dconVal; |
6327 | Range u2; |
6328 | }; |
6329 | } op2; |
6330 | |
6331 | bool IsCheckedBoundArithBound() |
6332 | { |
6333 | return ((assertionKind == OAK_EQUAL || assertionKind == OAK_NOT_EQUAL) && op1.kind == O1K_BOUND_OPER_BND); |
6334 | } |
6335 | bool IsCheckedBoundBound() |
6336 | { |
6337 | return ((assertionKind == OAK_EQUAL || assertionKind == OAK_NOT_EQUAL) && op1.kind == O1K_BOUND_LOOP_BND); |
6338 | } |
6339 | bool IsConstantBound() |
6340 | { |
6341 | return ((assertionKind == OAK_EQUAL || assertionKind == OAK_NOT_EQUAL) && |
6342 | op1.kind == O1K_CONSTANT_LOOP_BND); |
6343 | } |
6344 | bool IsBoundsCheckNoThrow() |
6345 | { |
6346 | return ((assertionKind == OAK_NO_THROW) && (op1.kind == O1K_ARR_BND)); |
6347 | } |
6348 | |
6349 | bool IsCopyAssertion() |
6350 | { |
6351 | return ((assertionKind == OAK_EQUAL) && (op1.kind == O1K_LCLVAR) && (op2.kind == O2K_LCLVAR_COPY)); |
6352 | } |
6353 | |
6354 | static bool SameKind(AssertionDsc* a1, AssertionDsc* a2) |
6355 | { |
6356 | return a1->assertionKind == a2->assertionKind && a1->op1.kind == a2->op1.kind && |
6357 | a1->op2.kind == a2->op2.kind; |
6358 | } |
6359 | |
6360 | static bool ComplementaryKind(optAssertionKind kind, optAssertionKind kind2) |
6361 | { |
6362 | if (kind == OAK_EQUAL) |
6363 | { |
6364 | return kind2 == OAK_NOT_EQUAL; |
6365 | } |
6366 | else if (kind == OAK_NOT_EQUAL) |
6367 | { |
6368 | return kind2 == OAK_EQUAL; |
6369 | } |
6370 | return false; |
6371 | } |
6372 | |
6373 | static ssize_t GetLowerBoundForIntegralType(var_types type) |
6374 | { |
6375 | switch (type) |
6376 | { |
6377 | case TYP_BYTE: |
6378 | return SCHAR_MIN; |
6379 | case TYP_SHORT: |
6380 | return SHRT_MIN; |
6381 | case TYP_INT: |
6382 | return INT_MIN; |
6383 | case TYP_BOOL: |
6384 | case TYP_UBYTE: |
6385 | case TYP_USHORT: |
6386 | case TYP_UINT: |
6387 | return 0; |
6388 | default: |
6389 | unreached(); |
6390 | } |
6391 | } |
6392 | static ssize_t GetUpperBoundForIntegralType(var_types type) |
6393 | { |
6394 | switch (type) |
6395 | { |
6396 | case TYP_BOOL: |
6397 | return 1; |
6398 | case TYP_BYTE: |
6399 | return SCHAR_MAX; |
6400 | case TYP_SHORT: |
6401 | return SHRT_MAX; |
6402 | case TYP_INT: |
6403 | return INT_MAX; |
6404 | case TYP_UBYTE: |
6405 | return UCHAR_MAX; |
6406 | case TYP_USHORT: |
6407 | return USHRT_MAX; |
6408 | case TYP_UINT: |
6409 | return UINT_MAX; |
6410 | default: |
6411 | unreached(); |
6412 | } |
6413 | } |
6414 | |
6415 | bool HasSameOp1(AssertionDsc* that, bool vnBased) |
6416 | { |
6417 | if (op1.kind != that->op1.kind) |
6418 | { |
6419 | return false; |
6420 | } |
6421 | else if (op1.kind == O1K_ARR_BND) |
6422 | { |
6423 | assert(vnBased); |
6424 | return (op1.bnd.vnIdx == that->op1.bnd.vnIdx) && (op1.bnd.vnLen == that->op1.bnd.vnLen); |
6425 | } |
6426 | else |
6427 | { |
6428 | return ((vnBased && (op1.vn == that->op1.vn)) || |
6429 | (!vnBased && (op1.lcl.lclNum == that->op1.lcl.lclNum))); |
6430 | } |
6431 | } |
6432 | |
6433 | bool HasSameOp2(AssertionDsc* that, bool vnBased) |
6434 | { |
6435 | if (op2.kind != that->op2.kind) |
6436 | { |
6437 | return false; |
6438 | } |
6439 | switch (op2.kind) |
6440 | { |
6441 | case O2K_IND_CNS_INT: |
6442 | case O2K_CONST_INT: |
6443 | return ((op2.u1.iconVal == that->op2.u1.iconVal) && (op2.u1.iconFlags == that->op2.u1.iconFlags)); |
6444 | |
6445 | case O2K_CONST_LONG: |
6446 | return (op2.lconVal == that->op2.lconVal); |
6447 | |
6448 | case O2K_CONST_DOUBLE: |
6449 | // exact match because of positive and negative zero. |
6450 | return (memcmp(&op2.dconVal, &that->op2.dconVal, sizeof(double)) == 0); |
6451 | |
6452 | case O2K_LCLVAR_COPY: |
6453 | case O2K_ARR_LEN: |
6454 | return (op2.lcl.lclNum == that->op2.lcl.lclNum) && |
6455 | (!vnBased || op2.lcl.ssaNum == that->op2.lcl.ssaNum); |
6456 | |
6457 | case O2K_SUBRANGE: |
6458 | return ((op2.u2.loBound == that->op2.u2.loBound) && (op2.u2.hiBound == that->op2.u2.hiBound)); |
6459 | |
6460 | case O2K_INVALID: |
6461 | // we will return false |
6462 | break; |
6463 | |
6464 | default: |
6465 | assert(!"Unexpected value for op2.kind in AssertionDsc." ); |
6466 | break; |
6467 | } |
6468 | return false; |
6469 | } |
6470 | |
6471 | bool Complementary(AssertionDsc* that, bool vnBased) |
6472 | { |
6473 | return ComplementaryKind(assertionKind, that->assertionKind) && HasSameOp1(that, vnBased) && |
6474 | HasSameOp2(that, vnBased); |
6475 | } |
6476 | |
6477 | bool Equals(AssertionDsc* that, bool vnBased) |
6478 | { |
6479 | if (assertionKind != that->assertionKind) |
6480 | { |
6481 | return false; |
6482 | } |
6483 | else if (assertionKind == OAK_NO_THROW) |
6484 | { |
6485 | assert(op2.kind == O2K_INVALID); |
6486 | return HasSameOp1(that, vnBased); |
6487 | } |
6488 | else |
6489 | { |
6490 | return HasSameOp1(that, vnBased) && HasSameOp2(that, vnBased); |
6491 | } |
6492 | } |
6493 | }; |
6494 | |
6495 | protected: |
6496 | static fgWalkPreFn optAddCopiesCallback; |
6497 | static fgWalkPreFn optVNAssertionPropCurStmtVisitor; |
6498 | unsigned optAddCopyLclNum; |
6499 | GenTree* optAddCopyAsgnNode; |
6500 | |
6501 | bool optLocalAssertionProp; // indicates that we are performing local assertion prop |
6502 | bool optAssertionPropagated; // set to true if we modified the trees |
6503 | bool optAssertionPropagatedCurrentStmt; |
6504 | #ifdef DEBUG |
6505 | GenTree* optAssertionPropCurrentTree; |
6506 | #endif |
6507 | AssertionIndex* optComplementaryAssertionMap; |
6508 | JitExpandArray<ASSERT_TP>* optAssertionDep; // table that holds dependent assertions (assertions |
6509 | // using the value of a local var) for each local var |
6510 | AssertionDsc* optAssertionTabPrivate; // table that holds info about value assignments |
6511 | AssertionIndex optAssertionCount; // total number of assertions in the assertion table |
6512 | AssertionIndex optMaxAssertionCount; |
6513 | |
6514 | public: |
6515 | void optVnNonNullPropCurStmt(BasicBlock* block, GenTree* stmt, GenTree* tree); |
6516 | fgWalkResult optVNConstantPropCurStmt(BasicBlock* block, GenTree* stmt, GenTree* tree); |
6517 | GenTree* optVNConstantPropOnJTrue(BasicBlock* block, GenTree* stmt, GenTree* test); |
6518 | GenTree* optVNConstantPropOnTree(BasicBlock* block, GenTree* stmt, GenTree* tree); |
6519 | GenTree* optPrepareTreeForReplacement(GenTree* , GenTree* replaceTree); |
6520 | |
6521 | AssertionIndex GetAssertionCount() |
6522 | { |
6523 | return optAssertionCount; |
6524 | } |
6525 | ASSERT_TP* bbJtrueAssertionOut; |
6526 | typedef JitHashTable<ValueNum, JitSmallPrimitiveKeyFuncs<ValueNum>, ASSERT_TP> ValueNumToAssertsMap; |
6527 | ValueNumToAssertsMap* optValueNumToAsserts; |
6528 | |
6529 | // Assertion prop helpers. |
6530 | ASSERT_TP& GetAssertionDep(unsigned lclNum); |
6531 | AssertionDsc* optGetAssertion(AssertionIndex assertIndex); |
6532 | void optAssertionInit(bool isLocalProp); |
6533 | void optAssertionTraitsInit(AssertionIndex assertionCount); |
6534 | #if LOCAL_ASSERTION_PROP |
6535 | void optAssertionReset(AssertionIndex limit); |
6536 | void optAssertionRemove(AssertionIndex index); |
6537 | #endif |
6538 | |
6539 | // Assertion prop data flow functions. |
6540 | void optAssertionPropMain(); |
6541 | GenTree* optVNAssertionPropCurStmt(BasicBlock* block, GenTree* stmt); |
6542 | bool optIsTreeKnownIntValue(bool vnBased, GenTree* tree, ssize_t* pConstant, unsigned* pIconFlags); |
6543 | ASSERT_TP* optInitAssertionDataflowFlags(); |
6544 | ASSERT_TP* optComputeAssertionGen(); |
6545 | |
6546 | // Assertion Gen functions. |
6547 | void optAssertionGen(GenTree* tree); |
6548 | AssertionIndex optAssertionGenPhiDefn(GenTree* tree); |
6549 | AssertionInfo optCreateJTrueBoundsAssertion(GenTree* tree); |
6550 | AssertionInfo optAssertionGenJtrue(GenTree* tree); |
6551 | AssertionIndex optCreateJtrueAssertions(GenTree* op1, GenTree* op2, Compiler::optAssertionKind assertionKind); |
6552 | AssertionIndex optFindComplementary(AssertionIndex assertionIndex); |
6553 | void optMapComplementary(AssertionIndex assertionIndex, AssertionIndex index); |
6554 | |
6555 | // Assertion creation functions. |
6556 | AssertionIndex optCreateAssertion(GenTree* op1, GenTree* op2, optAssertionKind assertionKind); |
6557 | AssertionIndex optCreateAssertion(GenTree* op1, |
6558 | GenTree* op2, |
6559 | optAssertionKind assertionKind, |
6560 | AssertionDsc* assertion); |
6561 | void optCreateComplementaryAssertion(AssertionIndex assertionIndex, GenTree* op1, GenTree* op2); |
6562 | |
6563 | bool optAssertionVnInvolvesNan(AssertionDsc* assertion); |
6564 | AssertionIndex optAddAssertion(AssertionDsc* assertion); |
6565 | void optAddVnAssertionMapping(ValueNum vn, AssertionIndex index); |
6566 | #ifdef DEBUG |
6567 | void optPrintVnAssertionMapping(); |
6568 | #endif |
6569 | ASSERT_TP optGetVnMappedAssertions(ValueNum vn); |
6570 | |
6571 | // Used for respective assertion propagations. |
6572 | AssertionIndex optAssertionIsSubrange(GenTree* tree, var_types toType, ASSERT_VALARG_TP assertions); |
6573 | AssertionIndex optAssertionIsSubtype(GenTree* tree, GenTree* methodTableArg, ASSERT_VALARG_TP assertions); |
6574 | AssertionIndex optAssertionIsNonNullInternal(GenTree* op, ASSERT_VALARG_TP assertions); |
6575 | bool optAssertionIsNonNull(GenTree* op, |
6576 | ASSERT_VALARG_TP assertions DEBUGARG(bool* pVnBased) DEBUGARG(AssertionIndex* pIndex)); |
6577 | |
6578 | // Used for Relop propagation. |
6579 | AssertionIndex optGlobalAssertionIsEqualOrNotEqual(ASSERT_VALARG_TP assertions, GenTree* op1, GenTree* op2); |
6580 | AssertionIndex optGlobalAssertionIsEqualOrNotEqualZero(ASSERT_VALARG_TP assertions, GenTree* op1); |
6581 | AssertionIndex optLocalAssertionIsEqualOrNotEqual( |
6582 | optOp1Kind op1Kind, unsigned lclNum, optOp2Kind op2Kind, ssize_t cnsVal, ASSERT_VALARG_TP assertions); |
6583 | |
6584 | // Assertion prop for lcl var functions. |
6585 | bool optAssertionProp_LclVarTypeCheck(GenTree* tree, LclVarDsc* lclVarDsc, LclVarDsc* copyVarDsc); |
6586 | GenTree* optCopyAssertionProp(AssertionDsc* curAssertion, |
6587 | GenTree* tree, |
6588 | GenTree* stmt DEBUGARG(AssertionIndex index)); |
6589 | GenTree* optConstantAssertionProp(AssertionDsc* curAssertion, |
6590 | GenTree* tree, |
6591 | GenTree* stmt DEBUGARG(AssertionIndex index)); |
6592 | |
6593 | // Assertion propagation functions. |
6594 | GenTree* optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree, GenTree* stmt); |
6595 | GenTree* optAssertionProp_LclVar(ASSERT_VALARG_TP assertions, GenTree* tree, GenTree* stmt); |
6596 | GenTree* optAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* tree, GenTree* stmt); |
6597 | GenTree* optAssertionProp_Cast(ASSERT_VALARG_TP assertions, GenTree* tree, GenTree* stmt); |
6598 | GenTree* optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCall* call, GenTree* stmt); |
6599 | GenTree* optAssertionProp_RelOp(ASSERT_VALARG_TP assertions, GenTree* tree, GenTree* stmt); |
6600 | GenTree* optAssertionProp_Comma(ASSERT_VALARG_TP assertions, GenTree* tree, GenTree* stmt); |
6601 | GenTree* optAssertionProp_BndsChk(ASSERT_VALARG_TP assertions, GenTree* tree, GenTree* stmt); |
6602 | GenTree* optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions, GenTree* tree, GenTree* stmt); |
6603 | GenTree* optAssertionPropLocal_RelOp(ASSERT_VALARG_TP assertions, GenTree* tree, GenTree* stmt); |
6604 | GenTree* optAssertionProp_Update(GenTree* newTree, GenTree* tree, GenTree* stmt); |
6605 | GenTree* optNonNullAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCall* call, GenTree* stmt); |
6606 | |
6607 | // Implied assertion functions. |
6608 | void optImpliedAssertions(AssertionIndex assertionIndex, ASSERT_TP& activeAssertions); |
6609 | void optImpliedByTypeOfAssertions(ASSERT_TP& activeAssertions); |
6610 | void optImpliedByCopyAssertion(AssertionDsc* copyAssertion, AssertionDsc* depAssertion, ASSERT_TP& result); |
6611 | void optImpliedByConstAssertion(AssertionDsc* curAssertion, ASSERT_TP& result); |
6612 | |
6613 | #ifdef DEBUG |
6614 | void optPrintAssertion(AssertionDsc* newAssertion, AssertionIndex assertionIndex = 0); |
6615 | void optDebugCheckAssertion(AssertionDsc* assertion); |
6616 | void optDebugCheckAssertions(AssertionIndex AssertionIndex); |
6617 | #endif |
6618 | void optAddCopies(); |
6619 | #endif // ASSERTION_PROP |
6620 | |
6621 | /************************************************************************** |
6622 | * Range checks |
6623 | *************************************************************************/ |
6624 | |
6625 | public: |
6626 | struct LoopCloneVisitorInfo |
6627 | { |
6628 | LoopCloneContext* context; |
6629 | unsigned loopNum; |
6630 | GenTree* stmt; |
6631 | LoopCloneVisitorInfo(LoopCloneContext* context, unsigned loopNum, GenTree* stmt) |
6632 | : context(context), loopNum(loopNum), stmt(nullptr) |
6633 | { |
6634 | } |
6635 | }; |
6636 | |
6637 | bool optIsStackLocalInvariant(unsigned loopNum, unsigned lclNum); |
6638 | bool (GenTree* tree, ArrIndex* result, unsigned lhsNum); |
6639 | bool optReconstructArrIndex(GenTree* tree, ArrIndex* result, unsigned lhsNum); |
6640 | bool optIdentifyLoopOptInfo(unsigned loopNum, LoopCloneContext* context); |
6641 | static fgWalkPreFn optCanOptimizeByLoopCloningVisitor; |
6642 | fgWalkResult optCanOptimizeByLoopCloning(GenTree* tree, LoopCloneVisitorInfo* info); |
6643 | void optObtainLoopCloningOpts(LoopCloneContext* context); |
6644 | bool optIsLoopClonable(unsigned loopInd); |
6645 | |
6646 | bool optCanCloneLoops(); |
6647 | |
6648 | #ifdef DEBUG |
6649 | void optDebugLogLoopCloning(BasicBlock* block, GenTree* insertBefore); |
6650 | #endif |
6651 | void optPerformStaticOptimizations(unsigned loopNum, LoopCloneContext* context DEBUGARG(bool fastPath)); |
6652 | bool optComputeDerefConditions(unsigned loopNum, LoopCloneContext* context); |
6653 | bool optDeriveLoopCloningConditions(unsigned loopNum, LoopCloneContext* context); |
6654 | BasicBlock* optInsertLoopChoiceConditions(LoopCloneContext* context, |
6655 | unsigned loopNum, |
6656 | BasicBlock* head, |
6657 | BasicBlock* slow); |
6658 | |
6659 | protected: |
6660 | ssize_t optGetArrayRefScaleAndIndex(GenTree* mul, GenTree** pIndex DEBUGARG(bool bRngChk)); |
6661 | |
6662 | bool optReachWithoutCall(BasicBlock* srcBB, BasicBlock* dstBB); |
6663 | |
6664 | protected: |
6665 | bool optLoopsMarked; |
6666 | |
6667 | /* |
6668 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6669 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6670 | XX XX |
6671 | XX RegAlloc XX |
6672 | XX XX |
6673 | XX Does the register allocation and puts the remaining lclVars on the stack XX |
6674 | XX XX |
6675 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6676 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6677 | */ |
6678 | |
6679 | public: |
6680 | regNumber raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc); |
6681 | |
6682 | void raMarkStkVars(); |
6683 | |
6684 | protected: |
6685 | // Some things are used by both LSRA and regpredict allocators. |
6686 | |
6687 | FrameType rpFrameType; |
6688 | bool rpMustCreateEBPCalled; // Set to true after we have called rpMustCreateEBPFrame once |
6689 | |
6690 | bool rpMustCreateEBPFrame(INDEBUG(const char** wbReason)); |
6691 | |
6692 | private: |
6693 | Lowering* m_pLowering; // Lowering; needed to Lower IR that's added or modified after Lowering. |
6694 | LinearScanInterface* m_pLinearScan; // Linear Scan allocator |
6695 | |
6696 | /* raIsVarargsStackArg is called by raMaskStkVars and by |
6697 | lvaSortByRefCount. It identifies the special case |
6698 | where a varargs function has a parameter passed on the |
6699 | stack, other than the special varargs handle. Such parameters |
6700 | require special treatment, because they cannot be tracked |
6701 | by the GC (their offsets in the stack are not known |
6702 | at compile time). |
6703 | */ |
6704 | |
6705 | bool raIsVarargsStackArg(unsigned lclNum) |
6706 | { |
6707 | #ifdef _TARGET_X86_ |
6708 | |
6709 | LclVarDsc* varDsc = &lvaTable[lclNum]; |
6710 | |
6711 | assert(varDsc->lvIsParam); |
6712 | |
6713 | return (info.compIsVarArgs && !varDsc->lvIsRegArg && (lclNum != lvaVarargsHandleArg)); |
6714 | |
6715 | #else // _TARGET_X86_ |
6716 | |
6717 | return false; |
6718 | |
6719 | #endif // _TARGET_X86_ |
6720 | } |
6721 | |
6722 | /* |
6723 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6724 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6725 | XX XX |
6726 | XX EEInterface XX |
6727 | XX XX |
6728 | XX Get to the class and method info from the Execution Engine given XX |
6729 | XX tokens for the class and method XX |
6730 | XX XX |
6731 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6732 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
6733 | */ |
6734 | |
6735 | public: |
6736 | /* These are the different addressing modes used to access a local var. |
6737 | * The JIT has to report the location of the locals back to the EE |
6738 | * for debugging purposes. |
6739 | */ |
6740 | |
6741 | enum siVarLocType |
6742 | { |
6743 | VLT_REG, |
6744 | VLT_REG_BYREF, // this type is currently only used for value types on X64 |
6745 | VLT_REG_FP, |
6746 | VLT_STK, |
6747 | VLT_STK_BYREF, // this type is currently only used for value types on X64 |
6748 | VLT_REG_REG, |
6749 | VLT_REG_STK, |
6750 | VLT_STK_REG, |
6751 | VLT_STK2, |
6752 | VLT_FPSTK, |
6753 | VLT_FIXED_VA, |
6754 | |
6755 | VLT_COUNT, |
6756 | VLT_INVALID |
6757 | }; |
6758 | |
6759 | struct siVarLoc |
6760 | { |
6761 | siVarLocType vlType; |
6762 | |
6763 | union { |
6764 | // VLT_REG/VLT_REG_FP -- Any pointer-sized enregistered value (TYP_INT, TYP_REF, etc) |
6765 | // eg. EAX |
6766 | // VLT_REG_BYREF -- the specified register contains the address of the variable |
6767 | // eg. [EAX] |
6768 | |
6769 | struct |
6770 | { |
6771 | regNumber vlrReg; |
6772 | } vlReg; |
6773 | |
6774 | // VLT_STK -- Any 32 bit value which is on the stack |
6775 | // eg. [ESP+0x20], or [EBP-0x28] |
6776 | // VLT_STK_BYREF -- the specified stack location contains the address of the variable |
6777 | // eg. mov EAX, [ESP+0x20]; [EAX] |
6778 | |
6779 | struct |
6780 | { |
6781 | regNumber vlsBaseReg; |
6782 | NATIVE_OFFSET vlsOffset; |
6783 | } vlStk; |
6784 | |
6785 | // VLT_REG_REG -- TYP_LONG/TYP_DOUBLE with both DWords enregistered |
6786 | // eg. RBM_EAXEDX |
6787 | |
6788 | struct |
6789 | { |
6790 | regNumber vlrrReg1; |
6791 | regNumber vlrrReg2; |
6792 | } vlRegReg; |
6793 | |
6794 | // VLT_REG_STK -- Partly enregistered TYP_LONG/TYP_DOUBLE |
6795 | // eg { LowerDWord=EAX UpperDWord=[ESP+0x8] } |
6796 | |
6797 | struct |
6798 | { |
6799 | regNumber vlrsReg; |
6800 | |
6801 | struct |
6802 | { |
6803 | regNumber ; |
6804 | NATIVE_OFFSET ; |
6805 | } ; |
6806 | } vlRegStk; |
6807 | |
6808 | // VLT_STK_REG -- Partly enregistered TYP_LONG/TYP_DOUBLE |
6809 | // eg { LowerDWord=[ESP+0x8] UpperDWord=EAX } |
6810 | |
6811 | struct |
6812 | { |
6813 | struct |
6814 | { |
6815 | regNumber vlsrsBaseReg; |
6816 | NATIVE_OFFSET vlsrsOffset; |
6817 | } vlsrStk; |
6818 | |
6819 | regNumber vlsrReg; |
6820 | } vlStkReg; |
6821 | |
6822 | // VLT_STK2 -- Any 64 bit value which is on the stack, in 2 successsive DWords |
6823 | // eg 2 DWords at [ESP+0x10] |
6824 | |
6825 | struct |
6826 | { |
6827 | regNumber vls2BaseReg; |
6828 | NATIVE_OFFSET vls2Offset; |
6829 | } vlStk2; |
6830 | |
6831 | // VLT_FPSTK -- enregisterd TYP_DOUBLE (on the FP stack) |
6832 | // eg. ST(3). Actually it is ST("FPstkHeight - vpFpStk") |
6833 | |
6834 | struct |
6835 | { |
6836 | unsigned vlfReg; |
6837 | } vlFPstk; |
6838 | |
6839 | // VLT_FIXED_VA -- fixed argument of a varargs function. |
6840 | // The argument location depends on the size of the variable |
6841 | // arguments (...). Inspecting the VARARGS_HANDLE indicates the |
6842 | // location of the first arg. This argument can then be accessed |
6843 | // relative to the position of the first arg |
6844 | |
6845 | struct |
6846 | { |
6847 | unsigned vlfvOffset; |
6848 | } vlFixedVarArg; |
6849 | |
6850 | // VLT_MEMORY |
6851 | |
6852 | struct |
6853 | { |
6854 | void* rpValue; // pointer to the in-process |
6855 | // location of the value. |
6856 | } vlMemory; |
6857 | }; |
6858 | |
6859 | // Helper functions |
6860 | |
6861 | bool vlIsInReg(regNumber reg); |
6862 | bool vlIsOnStk(regNumber reg, signed offset); |
6863 | }; |
6864 | |
6865 | /*************************************************************************/ |
6866 | |
6867 | public: |
6868 | // Get handles |
6869 | |
6870 | void eeGetCallInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken, |
6871 | CORINFO_RESOLVED_TOKEN* pConstrainedToken, |
6872 | CORINFO_CALLINFO_FLAGS flags, |
6873 | CORINFO_CALL_INFO* pResult); |
6874 | inline CORINFO_CALLINFO_FLAGS addVerifyFlag(CORINFO_CALLINFO_FLAGS flags); |
6875 | |
6876 | void eeGetFieldInfo(CORINFO_RESOLVED_TOKEN* pResolvedToken, |
6877 | CORINFO_ACCESS_FLAGS flags, |
6878 | CORINFO_FIELD_INFO* pResult); |
6879 | |
6880 | // Get the flags |
6881 | |
6882 | BOOL eeIsValueClass(CORINFO_CLASS_HANDLE clsHnd); |
6883 | |
6884 | #if defined(DEBUG) || defined(FEATURE_JIT_METHOD_PERF) || defined(FEATURE_SIMD) || defined(TRACK_LSRA_STATS) |
6885 | |
6886 | bool IsSuperPMIException(unsigned code) |
6887 | { |
6888 | // Copied from NDP\clr\src\ToolBox\SuperPMI\SuperPMI-Shared\ErrorHandling.h |
6889 | |
6890 | const unsigned EXCEPTIONCODE_DebugBreakorAV = 0xe0421000; |
6891 | const unsigned EXCEPTIONCODE_MC = 0xe0422000; |
6892 | const unsigned EXCEPTIONCODE_LWM = 0xe0423000; |
6893 | const unsigned EXCEPTIONCODE_SASM = 0xe0424000; |
6894 | const unsigned EXCEPTIONCODE_SSYM = 0xe0425000; |
6895 | const unsigned EXCEPTIONCODE_CALLUTILS = 0xe0426000; |
6896 | const unsigned EXCEPTIONCODE_TYPEUTILS = 0xe0427000; |
6897 | const unsigned EXCEPTIONCODE_ASSERT = 0xe0440000; |
6898 | |
6899 | switch (code) |
6900 | { |
6901 | case EXCEPTIONCODE_DebugBreakorAV: |
6902 | case EXCEPTIONCODE_MC: |
6903 | case EXCEPTIONCODE_LWM: |
6904 | case EXCEPTIONCODE_SASM: |
6905 | case EXCEPTIONCODE_SSYM: |
6906 | case EXCEPTIONCODE_CALLUTILS: |
6907 | case EXCEPTIONCODE_TYPEUTILS: |
6908 | case EXCEPTIONCODE_ASSERT: |
6909 | return true; |
6910 | default: |
6911 | return false; |
6912 | } |
6913 | } |
6914 | |
6915 | const char* eeGetMethodName(CORINFO_METHOD_HANDLE hnd, const char** className); |
6916 | const char* eeGetMethodFullName(CORINFO_METHOD_HANDLE hnd); |
6917 | |
6918 | bool eeIsNativeMethod(CORINFO_METHOD_HANDLE method); |
6919 | CORINFO_METHOD_HANDLE eeGetMethodHandleForNative(CORINFO_METHOD_HANDLE method); |
6920 | #endif |
6921 | |
6922 | var_types eeGetArgType(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig); |
6923 | var_types eeGetArgType(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig, bool* isPinned); |
6924 | unsigned eeGetArgSize(CORINFO_ARG_LIST_HANDLE list, CORINFO_SIG_INFO* sig); |
6925 | |
6926 | // VOM info, method sigs |
6927 | |
6928 | void eeGetSig(unsigned sigTok, |
6929 | CORINFO_MODULE_HANDLE scope, |
6930 | CORINFO_CONTEXT_HANDLE context, |
6931 | CORINFO_SIG_INFO* retSig); |
6932 | |
6933 | void eeGetCallSiteSig(unsigned sigTok, |
6934 | CORINFO_MODULE_HANDLE scope, |
6935 | CORINFO_CONTEXT_HANDLE context, |
6936 | CORINFO_SIG_INFO* retSig); |
6937 | |
6938 | void eeGetMethodSig(CORINFO_METHOD_HANDLE methHnd, CORINFO_SIG_INFO* retSig, CORINFO_CLASS_HANDLE owner = nullptr); |
6939 | |
6940 | // Method entry-points, instrs |
6941 | |
6942 | CORINFO_METHOD_HANDLE eeMarkNativeTarget(CORINFO_METHOD_HANDLE method); |
6943 | |
6944 | CORINFO_EE_INFO eeInfo; |
6945 | bool eeInfoInitialized; |
6946 | |
6947 | CORINFO_EE_INFO* eeGetEEInfo(); |
6948 | |
6949 | // Gets the offset of a SDArray's first element |
6950 | unsigned eeGetArrayDataOffset(var_types type); |
6951 | // Gets the offset of a MDArray's first element |
6952 | unsigned eeGetMDArrayDataOffset(var_types type, unsigned rank); |
6953 | |
6954 | GenTree* eeGetPInvokeCookie(CORINFO_SIG_INFO* szMetaSig); |
6955 | |
6956 | // Returns the page size for the target machine as reported by the EE. |
6957 | target_size_t eeGetPageSize() |
6958 | { |
6959 | return (target_size_t)eeGetEEInfo()->osPageSize; |
6960 | } |
6961 | |
6962 | // Returns the frame size at which we will generate a loop to probe the stack. |
6963 | target_size_t getVeryLargeFrameSize() |
6964 | { |
6965 | #ifdef _TARGET_ARM_ |
6966 | // The looping probe code is 40 bytes, whereas the straight-line probing for |
6967 | // the (0x2000..0x3000) case is 44, so use looping for anything 0x2000 bytes |
6968 | // or greater, to generate smaller code. |
6969 | return 2 * eeGetPageSize(); |
6970 | #else |
6971 | return 3 * eeGetPageSize(); |
6972 | #endif |
6973 | } |
6974 | |
6975 | //------------------------------------------------------------------------ |
6976 | // VirtualStubParam: virtual stub dispatch extra parameter (slot address). |
6977 | // |
6978 | // It represents Abi and target specific registers for the parameter. |
6979 | // |
6980 | class VirtualStubParamInfo |
6981 | { |
6982 | public: |
6983 | VirtualStubParamInfo(bool isCoreRTABI) |
6984 | { |
6985 | #if defined(_TARGET_X86_) |
6986 | reg = REG_EAX; |
6987 | regMask = RBM_EAX; |
6988 | #elif defined(_TARGET_AMD64_) |
6989 | if (isCoreRTABI) |
6990 | { |
6991 | reg = REG_R10; |
6992 | regMask = RBM_R10; |
6993 | } |
6994 | else |
6995 | { |
6996 | reg = REG_R11; |
6997 | regMask = RBM_R11; |
6998 | } |
6999 | #elif defined(_TARGET_ARM_) |
7000 | if (isCoreRTABI) |
7001 | { |
7002 | reg = REG_R12; |
7003 | regMask = RBM_R12; |
7004 | } |
7005 | else |
7006 | { |
7007 | reg = REG_R4; |
7008 | regMask = RBM_R4; |
7009 | } |
7010 | #elif defined(_TARGET_ARM64_) |
7011 | reg = REG_R11; |
7012 | regMask = RBM_R11; |
7013 | #else |
7014 | #error Unsupported or unset target architecture |
7015 | #endif |
7016 | } |
7017 | |
7018 | regNumber GetReg() const |
7019 | { |
7020 | return reg; |
7021 | } |
7022 | |
7023 | _regMask_enum GetRegMask() const |
7024 | { |
7025 | return regMask; |
7026 | } |
7027 | |
7028 | private: |
7029 | regNumber reg; |
7030 | _regMask_enum regMask; |
7031 | }; |
7032 | |
7033 | VirtualStubParamInfo* virtualStubParamInfo; |
7034 | |
7035 | bool IsTargetAbi(CORINFO_RUNTIME_ABI abi) |
7036 | { |
7037 | return eeGetEEInfo()->targetAbi == abi; |
7038 | } |
7039 | |
7040 | bool generateCFIUnwindCodes() |
7041 | { |
7042 | #if defined(_TARGET_UNIX_) |
7043 | return IsTargetAbi(CORINFO_CORERT_ABI); |
7044 | #else |
7045 | return false; |
7046 | #endif |
7047 | } |
7048 | |
7049 | // Debugging support - Line number info |
7050 | |
7051 | void eeGetStmtOffsets(); |
7052 | |
7053 | unsigned eeBoundariesCount; |
7054 | |
7055 | struct boundariesDsc |
7056 | { |
7057 | UNATIVE_OFFSET nativeIP; |
7058 | IL_OFFSET ilOffset; |
7059 | unsigned sourceReason; |
7060 | } * eeBoundaries; // Boundaries to report to EE |
7061 | void eeSetLIcount(unsigned count); |
7062 | void eeSetLIinfo(unsigned which, UNATIVE_OFFSET offs, unsigned srcIP, bool stkEmpty, bool callInstruction); |
7063 | void eeSetLIdone(); |
7064 | |
7065 | #ifdef DEBUG |
7066 | static void eeDispILOffs(IL_OFFSET offs); |
7067 | static void eeDispLineInfo(const boundariesDsc* line); |
7068 | void eeDispLineInfos(); |
7069 | #endif // DEBUG |
7070 | |
7071 | // Debugging support - Local var info |
7072 | |
7073 | void eeGetVars(); |
7074 | |
7075 | unsigned eeVarsCount; |
7076 | |
7077 | struct VarResultInfo |
7078 | { |
7079 | UNATIVE_OFFSET startOffset; |
7080 | UNATIVE_OFFSET endOffset; |
7081 | DWORD varNumber; |
7082 | siVarLoc loc; |
7083 | } * eeVars; |
7084 | void eeSetLVcount(unsigned count); |
7085 | void eeSetLVinfo(unsigned which, |
7086 | UNATIVE_OFFSET startOffs, |
7087 | UNATIVE_OFFSET length, |
7088 | unsigned varNum, |
7089 | unsigned LVnum, |
7090 | VarName namex, |
7091 | bool avail, |
7092 | const siVarLoc& loc); |
7093 | void eeSetLVdone(); |
7094 | |
7095 | #ifdef DEBUG |
7096 | void eeDispVar(ICorDebugInfo::NativeVarInfo* var); |
7097 | void eeDispVars(CORINFO_METHOD_HANDLE ftn, ULONG32 cVars, ICorDebugInfo::NativeVarInfo* vars); |
7098 | #endif // DEBUG |
7099 | |
7100 | // ICorJitInfo wrappers |
7101 | |
7102 | void eeReserveUnwindInfo(BOOL isFunclet, BOOL isColdCode, ULONG unwindSize); |
7103 | |
7104 | void eeAllocUnwindInfo(BYTE* pHotCode, |
7105 | BYTE* pColdCode, |
7106 | ULONG startOffset, |
7107 | ULONG endOffset, |
7108 | ULONG unwindSize, |
7109 | BYTE* pUnwindBlock, |
7110 | CorJitFuncKind funcKind); |
7111 | |
7112 | void eeSetEHcount(unsigned cEH); |
7113 | |
7114 | void eeSetEHinfo(unsigned EHnumber, const CORINFO_EH_CLAUSE* clause); |
7115 | |
7116 | WORD eeGetRelocTypeHint(void* target); |
7117 | |
7118 | // ICorStaticInfo wrapper functions |
7119 | |
7120 | bool eeTryResolveToken(CORINFO_RESOLVED_TOKEN* resolvedToken); |
7121 | |
7122 | #if defined(UNIX_AMD64_ABI) |
7123 | #ifdef DEBUG |
7124 | static void dumpSystemVClassificationType(SystemVClassificationType ct); |
7125 | #endif // DEBUG |
7126 | |
7127 | void eeGetSystemVAmd64PassStructInRegisterDescriptor( |
7128 | /*IN*/ CORINFO_CLASS_HANDLE structHnd, |
7129 | /*OUT*/ SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr); |
7130 | #endif // UNIX_AMD64_ABI |
7131 | |
7132 | template <typename ParamType> |
7133 | bool eeRunWithErrorTrap(void (*function)(ParamType*), ParamType* param) |
7134 | { |
7135 | return eeRunWithErrorTrapImp(reinterpret_cast<void (*)(void*)>(function), reinterpret_cast<void*>(param)); |
7136 | } |
7137 | |
7138 | bool eeRunWithErrorTrapImp(void (*function)(void*), void* param); |
7139 | |
7140 | // Utility functions |
7141 | |
7142 | const char* eeGetFieldName(CORINFO_FIELD_HANDLE fieldHnd, const char** classNamePtr = nullptr); |
7143 | |
7144 | #if defined(DEBUG) |
7145 | const wchar_t* eeGetCPString(size_t stringHandle); |
7146 | #endif |
7147 | |
7148 | const char* eeGetClassName(CORINFO_CLASS_HANDLE clsHnd); |
7149 | |
7150 | static CORINFO_METHOD_HANDLE eeFindHelper(unsigned helper); |
7151 | static CorInfoHelpFunc eeGetHelperNum(CORINFO_METHOD_HANDLE method); |
7152 | |
7153 | static fgWalkPreFn CountSharedStaticHelper; |
7154 | static bool IsSharedStaticHelper(GenTree* tree); |
7155 | static bool IsTreeAlwaysHoistable(GenTree* tree); |
7156 | static bool IsGcSafePoint(GenTree* tree); |
7157 | |
7158 | static CORINFO_FIELD_HANDLE eeFindJitDataOffs(unsigned jitDataOffs); |
7159 | // returns true/false if 'field' is a Jit Data offset |
7160 | static bool eeIsJitDataOffs(CORINFO_FIELD_HANDLE field); |
7161 | // returns a number < 0 if 'field' is not a Jit Data offset, otherwise the data offset (limited to 2GB) |
7162 | static int eeGetJitDataOffs(CORINFO_FIELD_HANDLE field); |
7163 | |
7164 | /*****************************************************************************/ |
7165 | |
7166 | /* |
7167 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7168 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7169 | XX XX |
7170 | XX CodeGenerator XX |
7171 | XX XX |
7172 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7173 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7174 | */ |
7175 | |
7176 | public: |
7177 | CodeGenInterface* codeGen; |
7178 | |
7179 | // The following holds information about instr offsets in terms of generated code. |
7180 | |
7181 | struct IPmappingDsc |
7182 | { |
7183 | IPmappingDsc* ipmdNext; // next line# record |
7184 | IL_OFFSETX ipmdILoffsx; // the instr offset |
7185 | emitLocation ipmdNativeLoc; // the emitter location of the native code corresponding to the IL offset |
7186 | bool ipmdIsLabel; // Can this code be a branch label? |
7187 | }; |
7188 | |
7189 | // Record the instr offset mapping to the generated code |
7190 | |
7191 | IPmappingDsc* genIPmappingList; |
7192 | IPmappingDsc* genIPmappingLast; |
7193 | |
7194 | // Managed RetVal - A side hash table meant to record the mapping from a |
7195 | // GT_CALL node to its IL offset. This info is used to emit sequence points |
7196 | // that can be used by debugger to determine the native offset at which the |
7197 | // managed RetVal will be available. |
7198 | // |
7199 | // In fact we can store IL offset in a GT_CALL node. This was ruled out in |
7200 | // favor of a side table for two reasons: 1) We need IL offset for only those |
7201 | // GT_CALL nodes (created during importation) that correspond to an IL call and |
7202 | // whose return type is other than TYP_VOID. 2) GT_CALL node is a frequently used |
7203 | // structure and IL offset is needed only when generating debuggable code. Therefore |
7204 | // it is desirable to avoid memory size penalty in retail scenarios. |
7205 | typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, IL_OFFSETX> CallSiteILOffsetTable; |
7206 | CallSiteILOffsetTable* genCallSite2ILOffsetMap; |
7207 | |
7208 | unsigned genReturnLocal; // Local number for the return value when applicable. |
7209 | BasicBlock* genReturnBB; // jumped to when not optimizing for speed. |
7210 | |
7211 | // The following properties are part of CodeGenContext. Getters are provided here for |
7212 | // convenience and backward compatibility, but the properties can only be set by invoking |
7213 | // the setter on CodeGenContext directly. |
7214 | |
7215 | __declspec(property(get = getEmitter)) emitter* genEmitter; |
7216 | emitter* getEmitter() |
7217 | { |
7218 | return codeGen->getEmitter(); |
7219 | } |
7220 | |
7221 | bool isFramePointerUsed() |
7222 | { |
7223 | return codeGen->isFramePointerUsed(); |
7224 | } |
7225 | |
7226 | __declspec(property(get = getInterruptible, put = setInterruptible)) bool genInterruptible; |
7227 | bool getInterruptible() |
7228 | { |
7229 | return codeGen->genInterruptible; |
7230 | } |
7231 | void setInterruptible(bool value) |
7232 | { |
7233 | codeGen->setInterruptible(value); |
7234 | } |
7235 | |
7236 | #ifdef _TARGET_ARMARCH_ |
7237 | __declspec(property(get = getHasTailCalls, put = setHasTailCalls)) bool hasTailCalls; |
7238 | bool getHasTailCalls() |
7239 | { |
7240 | return codeGen->hasTailCalls; |
7241 | } |
7242 | void setHasTailCalls(bool value) |
7243 | { |
7244 | codeGen->setHasTailCalls(value); |
7245 | } |
7246 | #endif // _TARGET_ARMARCH_ |
7247 | |
7248 | #if DOUBLE_ALIGN |
7249 | const bool genDoubleAlign() |
7250 | { |
7251 | return codeGen->doDoubleAlign(); |
7252 | } |
7253 | DWORD getCanDoubleAlign(); |
7254 | bool shouldDoubleAlign(unsigned refCntStk, |
7255 | unsigned refCntReg, |
7256 | unsigned refCntWtdReg, |
7257 | unsigned refCntStkParam, |
7258 | unsigned refCntWtdStkDbl); |
7259 | #endif // DOUBLE_ALIGN |
7260 | |
7261 | __declspec(property(get = getFullPtrRegMap, put = setFullPtrRegMap)) bool genFullPtrRegMap; |
7262 | bool getFullPtrRegMap() |
7263 | { |
7264 | return codeGen->genFullPtrRegMap; |
7265 | } |
7266 | void setFullPtrRegMap(bool value) |
7267 | { |
7268 | codeGen->setFullPtrRegMap(value); |
7269 | } |
7270 | |
7271 | // Things that MAY belong either in CodeGen or CodeGenContext |
7272 | |
7273 | #if FEATURE_EH_FUNCLETS |
7274 | FuncInfoDsc* compFuncInfos; |
7275 | unsigned short compCurrFuncIdx; |
7276 | unsigned short compFuncInfoCount; |
7277 | |
7278 | unsigned short compFuncCount() |
7279 | { |
7280 | assert(fgFuncletsCreated); |
7281 | return compFuncInfoCount; |
7282 | } |
7283 | |
7284 | #else // !FEATURE_EH_FUNCLETS |
7285 | |
7286 | // This is a no-op when there are no funclets! |
7287 | void genUpdateCurrentFunclet(BasicBlock* block) |
7288 | { |
7289 | return; |
7290 | } |
7291 | |
7292 | FuncInfoDsc compFuncInfoRoot; |
7293 | |
7294 | static const unsigned compCurrFuncIdx = 0; |
7295 | |
7296 | unsigned short compFuncCount() |
7297 | { |
7298 | return 1; |
7299 | } |
7300 | |
7301 | #endif // !FEATURE_EH_FUNCLETS |
7302 | |
7303 | FuncInfoDsc* funCurrentFunc(); |
7304 | void funSetCurrentFunc(unsigned funcIdx); |
7305 | FuncInfoDsc* funGetFunc(unsigned funcIdx); |
7306 | unsigned int funGetFuncIdx(BasicBlock* block); |
7307 | |
7308 | // LIVENESS |
7309 | |
7310 | VARSET_TP compCurLife; // current live variables |
7311 | GenTree* compCurLifeTree; // node after which compCurLife has been computed |
7312 | |
7313 | template <bool ForCodeGen> |
7314 | void compChangeLife(VARSET_VALARG_TP newLife); |
7315 | |
7316 | void genChangeLife(VARSET_VALARG_TP newLife) |
7317 | { |
7318 | compChangeLife</*ForCodeGen*/ true>(newLife); |
7319 | } |
7320 | |
7321 | template <bool ForCodeGen> |
7322 | inline void compUpdateLife(VARSET_VALARG_TP newLife); |
7323 | |
7324 | // Gets a register mask that represent the kill set for a helper call since |
7325 | // not all JIT Helper calls follow the standard ABI on the target architecture. |
7326 | regMaskTP compHelperCallKillSet(CorInfoHelpFunc helper); |
7327 | |
7328 | // Gets a register mask that represent the kill set for a NoGC helper call. |
7329 | regMaskTP compNoGCHelperCallKillSet(CorInfoHelpFunc helper); |
7330 | |
7331 | #ifdef _TARGET_ARM_ |
7332 | // Requires that "varDsc" be a promoted struct local variable being passed as an argument, beginning at |
7333 | // "firstArgRegNum", which is assumed to have already been aligned to the register alignment restriction of the |
7334 | // struct type. Adds bits to "*pArgSkippedRegMask" for any argument registers *not* used in passing "varDsc" -- |
7335 | // i.e., internal "holes" caused by internal alignment constraints. For example, if the struct contained an int and |
7336 | // a double, and we at R0 (on ARM), then R1 would be skipped, and the bit for R1 would be added to the mask. |
7337 | void fgAddSkippedRegsInPromotedStructArg(LclVarDsc* varDsc, unsigned firstArgRegNum, regMaskTP* pArgSkippedRegMask); |
7338 | #endif // _TARGET_ARM_ |
7339 | |
7340 | // If "tree" is a indirection (GT_IND, or GT_OBJ) whose arg is an ADDR, whose arg is a LCL_VAR, return that LCL_VAR |
7341 | // node, else NULL. |
7342 | static GenTree* fgIsIndirOfAddrOfLocal(GenTree* tree); |
7343 | |
7344 | // This map is indexed by GT_OBJ nodes that are address of promoted struct variables, which |
7345 | // have been annotated with the GTF_VAR_DEATH flag. If such a node is *not* mapped in this |
7346 | // table, one may assume that all the (tracked) field vars die at this GT_OBJ. Otherwise, |
7347 | // the node maps to a pointer to a VARSET_TP, containing set bits for each of the tracked field |
7348 | // vars of the promoted struct local that go dead at the given node (the set bits are the bits |
7349 | // for the tracked var indices of the field vars, as in a live var set). |
7350 | // |
7351 | // The map is allocated on demand so all map operations should use one of the following three |
7352 | // wrapper methods. |
7353 | |
7354 | NodeToVarsetPtrMap* m_promotedStructDeathVars; |
7355 | |
7356 | NodeToVarsetPtrMap* GetPromotedStructDeathVars() |
7357 | { |
7358 | if (m_promotedStructDeathVars == nullptr) |
7359 | { |
7360 | m_promotedStructDeathVars = new (getAllocator()) NodeToVarsetPtrMap(getAllocator()); |
7361 | } |
7362 | return m_promotedStructDeathVars; |
7363 | } |
7364 | |
7365 | void ClearPromotedStructDeathVars() |
7366 | { |
7367 | if (m_promotedStructDeathVars != nullptr) |
7368 | { |
7369 | m_promotedStructDeathVars->RemoveAll(); |
7370 | } |
7371 | } |
7372 | |
7373 | bool LookupPromotedStructDeathVars(GenTree* tree, VARSET_TP** bits) |
7374 | { |
7375 | bits = nullptr; |
7376 | bool result = false; |
7377 | |
7378 | if (m_promotedStructDeathVars != nullptr) |
7379 | { |
7380 | result = m_promotedStructDeathVars->Lookup(tree, bits); |
7381 | } |
7382 | |
7383 | return result; |
7384 | } |
7385 | |
7386 | /* |
7387 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7388 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7389 | XX XX |
7390 | XX UnwindInfo XX |
7391 | XX XX |
7392 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7393 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7394 | */ |
7395 | |
7396 | #if !defined(__GNUC__) |
7397 | #pragma region Unwind information |
7398 | #endif |
7399 | |
7400 | public: |
7401 | // |
7402 | // Infrastructure functions: start/stop/reserve/emit. |
7403 | // |
7404 | |
7405 | void unwindBegProlog(); |
7406 | void unwindEndProlog(); |
7407 | void unwindBegEpilog(); |
7408 | void unwindEndEpilog(); |
7409 | void unwindReserve(); |
7410 | void unwindEmit(void* pHotCode, void* pColdCode); |
7411 | |
7412 | // |
7413 | // Specific unwind information functions: called by code generation to indicate a particular |
7414 | // prolog or epilog unwindable instruction has been generated. |
7415 | // |
7416 | |
7417 | void unwindPush(regNumber reg); |
7418 | void unwindAllocStack(unsigned size); |
7419 | void unwindSetFrameReg(regNumber reg, unsigned offset); |
7420 | void unwindSaveReg(regNumber reg, unsigned offset); |
7421 | |
7422 | #if defined(_TARGET_ARM_) |
7423 | void unwindPushMaskInt(regMaskTP mask); |
7424 | void unwindPushMaskFloat(regMaskTP mask); |
7425 | void unwindPopMaskInt(regMaskTP mask); |
7426 | void unwindPopMaskFloat(regMaskTP mask); |
7427 | void unwindBranch16(); // The epilog terminates with a 16-bit branch (e.g., "bx lr") |
7428 | void unwindNop(unsigned codeSizeInBytes); // Generate unwind NOP code. 'codeSizeInBytes' is 2 or 4 bytes. Only |
7429 | // called via unwindPadding(). |
7430 | void unwindPadding(); // Generate a sequence of unwind NOP codes representing instructions between the last |
7431 | // instruction and the current location. |
7432 | #endif // _TARGET_ARM_ |
7433 | |
7434 | #if defined(_TARGET_ARM64_) |
7435 | void unwindNop(); |
7436 | void unwindPadding(); // Generate a sequence of unwind NOP codes representing instructions between the last |
7437 | // instruction and the current location. |
7438 | void unwindSaveReg(regNumber reg, int offset); // str reg, [sp, #offset] |
7439 | void unwindSaveRegPreindexed(regNumber reg, int offset); // str reg, [sp, #offset]! |
7440 | void unwindSaveRegPair(regNumber reg1, regNumber reg2, int offset); // stp reg1, reg2, [sp, #offset] |
7441 | void unwindSaveRegPairPreindexed(regNumber reg1, regNumber reg2, int offset); // stp reg1, reg2, [sp, #offset]! |
7442 | void unwindSaveNext(); // unwind code: save_next |
7443 | void unwindReturn(regNumber reg); // ret lr |
7444 | #endif // defined(_TARGET_ARM64_) |
7445 | |
7446 | // |
7447 | // Private "helper" functions for the unwind implementation. |
7448 | // |
7449 | |
7450 | private: |
7451 | #if FEATURE_EH_FUNCLETS |
7452 | void unwindGetFuncLocations(FuncInfoDsc* func, |
7453 | bool getHotSectionData, |
7454 | /* OUT */ emitLocation** ppStartLoc, |
7455 | /* OUT */ emitLocation** ppEndLoc); |
7456 | #endif // FEATURE_EH_FUNCLETS |
7457 | |
7458 | void unwindReserveFunc(FuncInfoDsc* func); |
7459 | void unwindEmitFunc(FuncInfoDsc* func, void* pHotCode, void* pColdCode); |
7460 | |
7461 | #if defined(_TARGET_AMD64_) || (defined(_TARGET_X86_) && FEATURE_EH_FUNCLETS) |
7462 | |
7463 | void unwindReserveFuncHelper(FuncInfoDsc* func, bool isHotCode); |
7464 | void unwindEmitFuncHelper(FuncInfoDsc* func, void* pHotCode, void* pColdCode, bool isHotCode); |
7465 | |
7466 | #endif // _TARGET_AMD64_ || (_TARGET_X86_ && FEATURE_EH_FUNCLETS) |
7467 | |
7468 | UNATIVE_OFFSET unwindGetCurrentOffset(FuncInfoDsc* func); |
7469 | |
7470 | #if defined(_TARGET_AMD64_) |
7471 | |
7472 | void unwindBegPrologWindows(); |
7473 | void unwindPushWindows(regNumber reg); |
7474 | void unwindAllocStackWindows(unsigned size); |
7475 | void unwindSetFrameRegWindows(regNumber reg, unsigned offset); |
7476 | void unwindSaveRegWindows(regNumber reg, unsigned offset); |
7477 | |
7478 | #ifdef UNIX_AMD64_ABI |
7479 | void unwindSaveRegCFI(regNumber reg, unsigned offset); |
7480 | #endif // UNIX_AMD64_ABI |
7481 | #elif defined(_TARGET_ARM_) |
7482 | |
7483 | void unwindPushPopMaskInt(regMaskTP mask, bool useOpsize16); |
7484 | void unwindPushPopMaskFloat(regMaskTP mask); |
7485 | |
7486 | #endif // _TARGET_ARM_ |
7487 | |
7488 | #if defined(_TARGET_UNIX_) |
7489 | int mapRegNumToDwarfReg(regNumber reg); |
7490 | void createCfiCode(FuncInfoDsc* func, UCHAR codeOffset, UCHAR opcode, USHORT dwarfReg, INT offset = 0); |
7491 | void unwindPushPopCFI(regNumber reg); |
7492 | void unwindBegPrologCFI(); |
7493 | void unwindPushPopMaskCFI(regMaskTP regMask, bool isFloat); |
7494 | void unwindAllocStackCFI(unsigned size); |
7495 | void unwindSetFrameRegCFI(regNumber reg, unsigned offset); |
7496 | void unwindEmitFuncCFI(FuncInfoDsc* func, void* pHotCode, void* pColdCode); |
7497 | #ifdef DEBUG |
7498 | void DumpCfiInfo(bool isHotCode, |
7499 | UNATIVE_OFFSET startOffset, |
7500 | UNATIVE_OFFSET endOffset, |
7501 | DWORD cfiCodeBytes, |
7502 | const CFI_CODE* const pCfiCode); |
7503 | #endif |
7504 | |
7505 | #endif // _TARGET_UNIX_ |
7506 | |
7507 | #if !defined(__GNUC__) |
7508 | #pragma endregion // Note: region is NOT under !defined(__GNUC__) |
7509 | #endif |
7510 | |
7511 | /* |
7512 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7513 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7514 | XX XX |
7515 | XX SIMD XX |
7516 | XX XX |
7517 | XX Info about SIMD types, methods and the SIMD assembly (i.e. the assembly XX |
7518 | XX that contains the distinguished, well-known SIMD type definitions). XX |
7519 | XX XX |
7520 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7521 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7522 | */ |
7523 | |
7524 | // Get highest available level for SIMD codegen |
7525 | SIMDLevel getSIMDSupportLevel() |
7526 | { |
7527 | #if defined(_TARGET_XARCH_) |
7528 | if (compSupports(InstructionSet_AVX2)) |
7529 | { |
7530 | return SIMD_AVX2_Supported; |
7531 | } |
7532 | |
7533 | if (compSupports(InstructionSet_SSE42)) |
7534 | { |
7535 | return SIMD_SSE4_Supported; |
7536 | } |
7537 | |
7538 | // min bar is SSE2 |
7539 | return SIMD_SSE2_Supported; |
7540 | #else |
7541 | assert(!"Available instruction set(s) for SIMD codegen is not defined for target arch" ); |
7542 | unreached(); |
7543 | return SIMD_Not_Supported; |
7544 | #endif |
7545 | } |
7546 | |
7547 | #ifdef FEATURE_SIMD |
7548 | |
7549 | // Should we support SIMD intrinsics? |
7550 | bool featureSIMD; |
7551 | |
7552 | // Have we identified any SIMD types? |
7553 | // This is currently used by struct promotion to avoid getting type information for a struct |
7554 | // field to see if it is a SIMD type, if we haven't seen any SIMD types or operations in |
7555 | // the method. |
7556 | bool _usesSIMDTypes; |
7557 | bool usesSIMDTypes() |
7558 | { |
7559 | return _usesSIMDTypes; |
7560 | } |
7561 | void setUsesSIMDTypes(bool value) |
7562 | { |
7563 | _usesSIMDTypes = value; |
7564 | } |
7565 | |
7566 | // This is a temp lclVar allocated on the stack as TYP_SIMD. It is used to implement intrinsics |
7567 | // that require indexed access to the individual fields of the vector, which is not well supported |
7568 | // by the hardware. It is allocated when/if such situations are encountered during Lowering. |
7569 | unsigned lvaSIMDInitTempVarNum; |
7570 | |
7571 | struct SIMDHandlesCache |
7572 | { |
7573 | // SIMD Types |
7574 | CORINFO_CLASS_HANDLE SIMDFloatHandle; |
7575 | CORINFO_CLASS_HANDLE SIMDDoubleHandle; |
7576 | CORINFO_CLASS_HANDLE SIMDIntHandle; |
7577 | CORINFO_CLASS_HANDLE SIMDUShortHandle; |
7578 | CORINFO_CLASS_HANDLE SIMDUByteHandle; |
7579 | CORINFO_CLASS_HANDLE SIMDShortHandle; |
7580 | CORINFO_CLASS_HANDLE SIMDByteHandle; |
7581 | CORINFO_CLASS_HANDLE SIMDLongHandle; |
7582 | CORINFO_CLASS_HANDLE SIMDUIntHandle; |
7583 | CORINFO_CLASS_HANDLE SIMDULongHandle; |
7584 | CORINFO_CLASS_HANDLE SIMDVector2Handle; |
7585 | CORINFO_CLASS_HANDLE SIMDVector3Handle; |
7586 | CORINFO_CLASS_HANDLE SIMDVector4Handle; |
7587 | CORINFO_CLASS_HANDLE SIMDVectorHandle; |
7588 | |
7589 | #ifdef FEATURE_HW_INTRINSICS |
7590 | #if defined(_TARGET_ARM64_) |
7591 | CORINFO_CLASS_HANDLE Vector64FloatHandle; |
7592 | CORINFO_CLASS_HANDLE Vector64IntHandle; |
7593 | CORINFO_CLASS_HANDLE Vector64UShortHandle; |
7594 | CORINFO_CLASS_HANDLE Vector64UByteHandle; |
7595 | CORINFO_CLASS_HANDLE Vector64ShortHandle; |
7596 | CORINFO_CLASS_HANDLE Vector64ByteHandle; |
7597 | CORINFO_CLASS_HANDLE Vector64UIntHandle; |
7598 | #endif // defined(_TARGET_ARM64_) |
7599 | CORINFO_CLASS_HANDLE Vector128FloatHandle; |
7600 | CORINFO_CLASS_HANDLE Vector128DoubleHandle; |
7601 | CORINFO_CLASS_HANDLE Vector128IntHandle; |
7602 | CORINFO_CLASS_HANDLE Vector128UShortHandle; |
7603 | CORINFO_CLASS_HANDLE Vector128UByteHandle; |
7604 | CORINFO_CLASS_HANDLE Vector128ShortHandle; |
7605 | CORINFO_CLASS_HANDLE Vector128ByteHandle; |
7606 | CORINFO_CLASS_HANDLE Vector128LongHandle; |
7607 | CORINFO_CLASS_HANDLE Vector128UIntHandle; |
7608 | CORINFO_CLASS_HANDLE Vector128ULongHandle; |
7609 | #if defined(_TARGET_XARCH_) |
7610 | CORINFO_CLASS_HANDLE Vector256FloatHandle; |
7611 | CORINFO_CLASS_HANDLE Vector256DoubleHandle; |
7612 | CORINFO_CLASS_HANDLE Vector256IntHandle; |
7613 | CORINFO_CLASS_HANDLE Vector256UShortHandle; |
7614 | CORINFO_CLASS_HANDLE Vector256UByteHandle; |
7615 | CORINFO_CLASS_HANDLE Vector256ShortHandle; |
7616 | CORINFO_CLASS_HANDLE Vector256ByteHandle; |
7617 | CORINFO_CLASS_HANDLE Vector256LongHandle; |
7618 | CORINFO_CLASS_HANDLE Vector256UIntHandle; |
7619 | CORINFO_CLASS_HANDLE Vector256ULongHandle; |
7620 | #endif // defined(_TARGET_XARCH_) |
7621 | #endif // FEATURE_HW_INTRINSICS |
7622 | |
7623 | SIMDHandlesCache() |
7624 | { |
7625 | memset(this, 0, sizeof(*this)); |
7626 | } |
7627 | }; |
7628 | |
7629 | SIMDHandlesCache* m_simdHandleCache; |
7630 | |
7631 | // Get an appropriate "zero" for the given type and class handle. |
7632 | GenTree* gtGetSIMDZero(var_types simdType, var_types baseType, CORINFO_CLASS_HANDLE simdHandle); |
7633 | |
7634 | // Get the handle for a SIMD type. |
7635 | CORINFO_CLASS_HANDLE gtGetStructHandleForSIMD(var_types simdType, var_types simdBaseType) |
7636 | { |
7637 | if (m_simdHandleCache == nullptr) |
7638 | { |
7639 | // This may happen if the JIT generates SIMD node on its own, without importing them. |
7640 | // Otherwise getBaseTypeAndSizeOfSIMDType should have created the cache. |
7641 | return NO_CLASS_HANDLE; |
7642 | } |
7643 | |
7644 | if (simdBaseType == TYP_FLOAT) |
7645 | { |
7646 | switch (simdType) |
7647 | { |
7648 | case TYP_SIMD8: |
7649 | return m_simdHandleCache->SIMDVector2Handle; |
7650 | case TYP_SIMD12: |
7651 | return m_simdHandleCache->SIMDVector3Handle; |
7652 | case TYP_SIMD16: |
7653 | if ((getSIMDVectorType() == TYP_SIMD32) || |
7654 | (m_simdHandleCache->SIMDVector4Handle != NO_CLASS_HANDLE)) |
7655 | { |
7656 | return m_simdHandleCache->SIMDVector4Handle; |
7657 | } |
7658 | break; |
7659 | case TYP_SIMD32: |
7660 | break; |
7661 | default: |
7662 | unreached(); |
7663 | } |
7664 | } |
7665 | assert(emitTypeSize(simdType) <= maxSIMDStructBytes()); |
7666 | switch (simdBaseType) |
7667 | { |
7668 | case TYP_FLOAT: |
7669 | return m_simdHandleCache->SIMDFloatHandle; |
7670 | case TYP_DOUBLE: |
7671 | return m_simdHandleCache->SIMDDoubleHandle; |
7672 | case TYP_INT: |
7673 | return m_simdHandleCache->SIMDIntHandle; |
7674 | case TYP_USHORT: |
7675 | return m_simdHandleCache->SIMDUShortHandle; |
7676 | case TYP_UBYTE: |
7677 | return m_simdHandleCache->SIMDUByteHandle; |
7678 | case TYP_SHORT: |
7679 | return m_simdHandleCache->SIMDShortHandle; |
7680 | case TYP_BYTE: |
7681 | return m_simdHandleCache->SIMDByteHandle; |
7682 | case TYP_LONG: |
7683 | return m_simdHandleCache->SIMDLongHandle; |
7684 | case TYP_UINT: |
7685 | return m_simdHandleCache->SIMDUIntHandle; |
7686 | case TYP_ULONG: |
7687 | return m_simdHandleCache->SIMDULongHandle; |
7688 | default: |
7689 | assert(!"Didn't find a class handle for simdType" ); |
7690 | } |
7691 | return NO_CLASS_HANDLE; |
7692 | } |
7693 | |
7694 | // Returns true if this is a SIMD type that should be considered an opaque |
7695 | // vector type (i.e. do not analyze or promote its fields). |
7696 | // Note that all but the fixed vector types are opaque, even though they may |
7697 | // actually be declared as having fields. |
7698 | bool isOpaqueSIMDType(CORINFO_CLASS_HANDLE structHandle) |
7699 | { |
7700 | return ((m_simdHandleCache != nullptr) && (structHandle != m_simdHandleCache->SIMDVector2Handle) && |
7701 | (structHandle != m_simdHandleCache->SIMDVector3Handle) && |
7702 | (structHandle != m_simdHandleCache->SIMDVector4Handle)); |
7703 | } |
7704 | |
7705 | // Returns true if the tree corresponds to a TYP_SIMD lcl var. |
7706 | // Note that both SIMD vector args and locals are mared as lvSIMDType = true, but |
7707 | // type of an arg node is TYP_BYREF and a local node is TYP_SIMD or TYP_STRUCT. |
7708 | bool isSIMDTypeLocal(GenTree* tree) |
7709 | { |
7710 | return tree->OperIsLocal() && lvaTable[tree->AsLclVarCommon()->gtLclNum].lvSIMDType; |
7711 | } |
7712 | |
7713 | // Returns true if the lclVar is an opaque SIMD type. |
7714 | bool isOpaqueSIMDLclVar(LclVarDsc* varDsc) |
7715 | { |
7716 | if (!varDsc->lvSIMDType) |
7717 | { |
7718 | return false; |
7719 | } |
7720 | return isOpaqueSIMDType(varDsc->lvVerTypeInfo.GetClassHandle()); |
7721 | } |
7722 | |
7723 | // Returns true if the type of the tree is a byref of TYP_SIMD |
7724 | bool isAddrOfSIMDType(GenTree* tree) |
7725 | { |
7726 | if (tree->TypeGet() == TYP_BYREF || tree->TypeGet() == TYP_I_IMPL) |
7727 | { |
7728 | switch (tree->OperGet()) |
7729 | { |
7730 | case GT_ADDR: |
7731 | return varTypeIsSIMD(tree->gtGetOp1()); |
7732 | |
7733 | case GT_LCL_VAR_ADDR: |
7734 | return lvaTable[tree->AsLclVarCommon()->gtLclNum].lvSIMDType; |
7735 | |
7736 | default: |
7737 | return isSIMDTypeLocal(tree); |
7738 | } |
7739 | } |
7740 | |
7741 | return false; |
7742 | } |
7743 | |
7744 | static bool isRelOpSIMDIntrinsic(SIMDIntrinsicID intrinsicId) |
7745 | { |
7746 | return (intrinsicId == SIMDIntrinsicEqual || intrinsicId == SIMDIntrinsicLessThan || |
7747 | intrinsicId == SIMDIntrinsicLessThanOrEqual || intrinsicId == SIMDIntrinsicGreaterThan || |
7748 | intrinsicId == SIMDIntrinsicGreaterThanOrEqual); |
7749 | } |
7750 | |
7751 | // Returns base type of a TYP_SIMD local. |
7752 | // Returns TYP_UNKNOWN if the local is not TYP_SIMD. |
7753 | var_types getBaseTypeOfSIMDLocal(GenTree* tree) |
7754 | { |
7755 | if (isSIMDTypeLocal(tree)) |
7756 | { |
7757 | return lvaTable[tree->AsLclVarCommon()->gtLclNum].lvBaseType; |
7758 | } |
7759 | |
7760 | return TYP_UNKNOWN; |
7761 | } |
7762 | |
7763 | bool isSIMDClass(CORINFO_CLASS_HANDLE clsHnd) |
7764 | { |
7765 | return info.compCompHnd->isInSIMDModule(clsHnd); |
7766 | } |
7767 | |
7768 | bool isIntrinsicType(CORINFO_CLASS_HANDLE clsHnd) |
7769 | { |
7770 | return (info.compCompHnd->getClassAttribs(clsHnd) & CORINFO_FLG_INTRINSIC_TYPE) != 0; |
7771 | } |
7772 | |
7773 | const char* getClassNameFromMetadata(CORINFO_CLASS_HANDLE cls, const char** namespaceName) |
7774 | { |
7775 | return info.compCompHnd->getClassNameFromMetadata(cls, namespaceName); |
7776 | } |
7777 | |
7778 | CORINFO_CLASS_HANDLE getTypeInstantiationArgument(CORINFO_CLASS_HANDLE cls, unsigned index) |
7779 | { |
7780 | return info.compCompHnd->getTypeInstantiationArgument(cls, index); |
7781 | } |
7782 | |
7783 | bool isSIMDClass(typeInfo* pTypeInfo) |
7784 | { |
7785 | return pTypeInfo->IsStruct() && isSIMDClass(pTypeInfo->GetClassHandleForValueClass()); |
7786 | } |
7787 | |
7788 | bool isHWSIMDClass(CORINFO_CLASS_HANDLE clsHnd) |
7789 | { |
7790 | #ifdef FEATURE_HW_INTRINSICS |
7791 | if (isIntrinsicType(clsHnd)) |
7792 | { |
7793 | const char* namespaceName = nullptr; |
7794 | (void)getClassNameFromMetadata(clsHnd, &namespaceName); |
7795 | return strcmp(namespaceName, "System.Runtime.Intrinsics" ) == 0; |
7796 | } |
7797 | #endif // FEATURE_HW_INTRINSICS |
7798 | return false; |
7799 | } |
7800 | |
7801 | bool isHWSIMDClass(typeInfo* pTypeInfo) |
7802 | { |
7803 | #ifdef FEATURE_HW_INTRINSICS |
7804 | return pTypeInfo->IsStruct() && isHWSIMDClass(pTypeInfo->GetClassHandleForValueClass()); |
7805 | #else |
7806 | return false; |
7807 | #endif |
7808 | } |
7809 | |
7810 | bool isSIMDorHWSIMDClass(CORINFO_CLASS_HANDLE clsHnd) |
7811 | { |
7812 | return isSIMDClass(clsHnd) || isHWSIMDClass(clsHnd); |
7813 | } |
7814 | |
7815 | bool isSIMDorHWSIMDClass(typeInfo* pTypeInfo) |
7816 | { |
7817 | return isSIMDClass(pTypeInfo) || isHWSIMDClass(pTypeInfo); |
7818 | } |
7819 | |
7820 | // Get the base (element) type and size in bytes for a SIMD type. Returns TYP_UNKNOWN |
7821 | // if it is not a SIMD type or is an unsupported base type. |
7822 | var_types getBaseTypeAndSizeOfSIMDType(CORINFO_CLASS_HANDLE typeHnd, unsigned* sizeBytes = nullptr); |
7823 | |
7824 | var_types getBaseTypeOfSIMDType(CORINFO_CLASS_HANDLE typeHnd) |
7825 | { |
7826 | return getBaseTypeAndSizeOfSIMDType(typeHnd, nullptr); |
7827 | } |
7828 | |
7829 | // Get SIMD Intrinsic info given the method handle. |
7830 | // Also sets typeHnd, argCount, baseType and sizeBytes out params. |
7831 | const SIMDIntrinsicInfo* getSIMDIntrinsicInfo(CORINFO_CLASS_HANDLE* typeHnd, |
7832 | CORINFO_METHOD_HANDLE methodHnd, |
7833 | CORINFO_SIG_INFO* sig, |
7834 | bool isNewObj, |
7835 | unsigned* argCount, |
7836 | var_types* baseType, |
7837 | unsigned* sizeBytes); |
7838 | |
7839 | // Pops and returns GenTree node from importers type stack. |
7840 | // Normalizes TYP_STRUCT value in case of GT_CALL, GT_RET_EXPR and arg nodes. |
7841 | GenTree* impSIMDPopStack(var_types type, bool expectAddr = false, CORINFO_CLASS_HANDLE structType = nullptr); |
7842 | |
7843 | // Create a GT_SIMD tree for a Get property of SIMD vector with a fixed index. |
7844 | GenTreeSIMD* impSIMDGetFixed(var_types simdType, var_types baseType, unsigned simdSize, int index); |
7845 | |
7846 | // Creates a GT_SIMD tree for Select operation |
7847 | GenTree* impSIMDSelect(CORINFO_CLASS_HANDLE typeHnd, |
7848 | var_types baseType, |
7849 | unsigned simdVectorSize, |
7850 | GenTree* op1, |
7851 | GenTree* op2, |
7852 | GenTree* op3); |
7853 | |
7854 | // Creates a GT_SIMD tree for Min/Max operation |
7855 | GenTree* impSIMDMinMax(SIMDIntrinsicID intrinsicId, |
7856 | CORINFO_CLASS_HANDLE typeHnd, |
7857 | var_types baseType, |
7858 | unsigned simdVectorSize, |
7859 | GenTree* op1, |
7860 | GenTree* op2); |
7861 | |
7862 | // Transforms operands and returns the SIMD intrinsic to be applied on |
7863 | // transformed operands to obtain given relop result. |
7864 | SIMDIntrinsicID impSIMDRelOp(SIMDIntrinsicID relOpIntrinsicId, |
7865 | CORINFO_CLASS_HANDLE typeHnd, |
7866 | unsigned simdVectorSize, |
7867 | var_types* baseType, |
7868 | GenTree** op1, |
7869 | GenTree** op2); |
7870 | |
7871 | // Creates a GT_SIMD tree for Abs intrinsic. |
7872 | GenTree* impSIMDAbs(CORINFO_CLASS_HANDLE typeHnd, var_types baseType, unsigned simdVectorSize, GenTree* op1); |
7873 | |
7874 | #if defined(_TARGET_XARCH_) |
7875 | |
7876 | // Transforms operands and returns the SIMD intrinsic to be applied on |
7877 | // transformed operands to obtain == comparison result. |
7878 | SIMDIntrinsicID impSIMDLongRelOpEqual(CORINFO_CLASS_HANDLE typeHnd, |
7879 | unsigned simdVectorSize, |
7880 | GenTree** op1, |
7881 | GenTree** op2); |
7882 | |
7883 | // Transforms operands and returns the SIMD intrinsic to be applied on |
7884 | // transformed operands to obtain > comparison result. |
7885 | SIMDIntrinsicID impSIMDLongRelOpGreaterThan(CORINFO_CLASS_HANDLE typeHnd, |
7886 | unsigned simdVectorSize, |
7887 | GenTree** op1, |
7888 | GenTree** op2); |
7889 | |
7890 | // Transforms operands and returns the SIMD intrinsic to be applied on |
7891 | // transformed operands to obtain >= comparison result. |
7892 | SIMDIntrinsicID impSIMDLongRelOpGreaterThanOrEqual(CORINFO_CLASS_HANDLE typeHnd, |
7893 | unsigned simdVectorSize, |
7894 | GenTree** op1, |
7895 | GenTree** op2); |
7896 | |
7897 | // Transforms operands and returns the SIMD intrinsic to be applied on |
7898 | // transformed operands to obtain >= comparison result in case of int32 |
7899 | // and small int base type vectors. |
7900 | SIMDIntrinsicID impSIMDIntegralRelOpGreaterThanOrEqual( |
7901 | CORINFO_CLASS_HANDLE typeHnd, unsigned simdVectorSize, var_types baseType, GenTree** op1, GenTree** op2); |
7902 | |
7903 | #endif // defined(_TARGET_XARCH_) |
7904 | |
7905 | void setLclRelatedToSIMDIntrinsic(GenTree* tree); |
7906 | bool areFieldsContiguous(GenTree* op1, GenTree* op2); |
7907 | bool areArrayElementsContiguous(GenTree* op1, GenTree* op2); |
7908 | bool areArgumentsContiguous(GenTree* op1, GenTree* op2); |
7909 | GenTree* createAddressNodeForSIMDInit(GenTree* tree, unsigned simdSize); |
7910 | |
7911 | // check methodHnd to see if it is a SIMD method that is expanded as an intrinsic in the JIT. |
7912 | GenTree* impSIMDIntrinsic(OPCODE opcode, |
7913 | GenTree* newobjThis, |
7914 | CORINFO_CLASS_HANDLE clsHnd, |
7915 | CORINFO_METHOD_HANDLE method, |
7916 | CORINFO_SIG_INFO* sig, |
7917 | unsigned methodFlags, |
7918 | int memberRef); |
7919 | |
7920 | GenTree* getOp1ForConstructor(OPCODE opcode, GenTree* newobjThis, CORINFO_CLASS_HANDLE clsHnd); |
7921 | |
7922 | // Whether SIMD vector occupies part of SIMD register. |
7923 | // SSE2: vector2f/3f are considered sub register SIMD types. |
7924 | // AVX: vector2f, 3f and 4f are all considered sub register SIMD types. |
7925 | bool isSubRegisterSIMDType(CORINFO_CLASS_HANDLE typeHnd) |
7926 | { |
7927 | unsigned sizeBytes = 0; |
7928 | var_types baseType = getBaseTypeAndSizeOfSIMDType(typeHnd, &sizeBytes); |
7929 | return (baseType == TYP_FLOAT) && (sizeBytes < getSIMDVectorRegisterByteLength()); |
7930 | } |
7931 | |
7932 | bool isSubRegisterSIMDType(GenTreeSIMD* simdNode) |
7933 | { |
7934 | return (simdNode->gtSIMDSize < getSIMDVectorRegisterByteLength()); |
7935 | } |
7936 | |
7937 | // Get the type for the hardware SIMD vector. |
7938 | // This is the maximum SIMD type supported for this target. |
7939 | var_types getSIMDVectorType() |
7940 | { |
7941 | #if defined(_TARGET_XARCH_) |
7942 | if (getSIMDSupportLevel() == SIMD_AVX2_Supported) |
7943 | { |
7944 | return TYP_SIMD32; |
7945 | } |
7946 | else |
7947 | { |
7948 | assert(getSIMDSupportLevel() >= SIMD_SSE2_Supported); |
7949 | return TYP_SIMD16; |
7950 | } |
7951 | #elif defined(_TARGET_ARM64_) |
7952 | return TYP_SIMD16; |
7953 | #else |
7954 | assert(!"getSIMDVectorType() unimplemented on target arch" ); |
7955 | unreached(); |
7956 | #endif |
7957 | } |
7958 | |
7959 | // Get the size of the SIMD type in bytes |
7960 | int getSIMDTypeSizeInBytes(CORINFO_CLASS_HANDLE typeHnd) |
7961 | { |
7962 | unsigned sizeBytes = 0; |
7963 | (void)getBaseTypeAndSizeOfSIMDType(typeHnd, &sizeBytes); |
7964 | return sizeBytes; |
7965 | } |
7966 | |
7967 | // Get the the number of elements of basetype of SIMD vector given by its size and baseType |
7968 | static int getSIMDVectorLength(unsigned simdSize, var_types baseType); |
7969 | |
7970 | // Get the the number of elements of basetype of SIMD vector given by its type handle |
7971 | int getSIMDVectorLength(CORINFO_CLASS_HANDLE typeHnd); |
7972 | |
7973 | // Get preferred alignment of SIMD type. |
7974 | int getSIMDTypeAlignment(var_types simdType); |
7975 | |
7976 | // Get the number of bytes in a System.Numeric.Vector<T> for the current compilation. |
7977 | // Note - cannot be used for System.Runtime.Intrinsic |
7978 | unsigned getSIMDVectorRegisterByteLength() |
7979 | { |
7980 | #if defined(_TARGET_XARCH_) |
7981 | if (getSIMDSupportLevel() == SIMD_AVX2_Supported) |
7982 | { |
7983 | return YMM_REGSIZE_BYTES; |
7984 | } |
7985 | else |
7986 | { |
7987 | assert(getSIMDSupportLevel() >= SIMD_SSE2_Supported); |
7988 | return XMM_REGSIZE_BYTES; |
7989 | } |
7990 | #elif defined(_TARGET_ARM64_) |
7991 | return FP_REGSIZE_BYTES; |
7992 | #else |
7993 | assert(!"getSIMDVectorRegisterByteLength() unimplemented on target arch" ); |
7994 | unreached(); |
7995 | #endif |
7996 | } |
7997 | |
7998 | // The minimum and maximum possible number of bytes in a SIMD vector. |
7999 | |
8000 | // maxSIMDStructBytes |
8001 | // The minimum SIMD size supported by System.Numeric.Vectors or System.Runtime.Intrinsic |
8002 | // SSE: 16-byte Vector<T> and Vector128<T> |
8003 | // AVX: 32-byte Vector256<T> (Vector<T> is 16-byte) |
8004 | // AVX2: 32-byte Vector<T> and Vector256<T> |
8005 | unsigned int maxSIMDStructBytes() |
8006 | { |
8007 | #if defined(FEATURE_HW_INTRINSICS) && defined(_TARGET_XARCH_) |
8008 | if (compSupports(InstructionSet_AVX)) |
8009 | { |
8010 | return YMM_REGSIZE_BYTES; |
8011 | } |
8012 | else |
8013 | { |
8014 | assert(getSIMDSupportLevel() >= SIMD_SSE2_Supported); |
8015 | return XMM_REGSIZE_BYTES; |
8016 | } |
8017 | #else |
8018 | return getSIMDVectorRegisterByteLength(); |
8019 | #endif |
8020 | } |
8021 | unsigned int minSIMDStructBytes() |
8022 | { |
8023 | return emitTypeSize(TYP_SIMD8); |
8024 | } |
8025 | |
8026 | // Returns the codegen type for a given SIMD size. |
8027 | var_types getSIMDTypeForSize(unsigned size) |
8028 | { |
8029 | var_types simdType = TYP_UNDEF; |
8030 | if (size == 8) |
8031 | { |
8032 | simdType = TYP_SIMD8; |
8033 | } |
8034 | else if (size == 12) |
8035 | { |
8036 | simdType = TYP_SIMD12; |
8037 | } |
8038 | else if (size == 16) |
8039 | { |
8040 | simdType = TYP_SIMD16; |
8041 | } |
8042 | else if (size == 32) |
8043 | { |
8044 | simdType = TYP_SIMD32; |
8045 | } |
8046 | else |
8047 | { |
8048 | noway_assert(!"Unexpected size for SIMD type" ); |
8049 | } |
8050 | return simdType; |
8051 | } |
8052 | |
8053 | unsigned getSIMDInitTempVarNum() |
8054 | { |
8055 | if (lvaSIMDInitTempVarNum == BAD_VAR_NUM) |
8056 | { |
8057 | lvaSIMDInitTempVarNum = lvaGrabTempWithImplicitUse(false DEBUGARG("SIMDInitTempVar" )); |
8058 | lvaTable[lvaSIMDInitTempVarNum].lvType = getSIMDVectorType(); |
8059 | } |
8060 | return lvaSIMDInitTempVarNum; |
8061 | } |
8062 | |
8063 | #else // !FEATURE_SIMD |
8064 | bool isOpaqueSIMDLclVar(LclVarDsc* varDsc) |
8065 | { |
8066 | return false; |
8067 | } |
8068 | #endif // FEATURE_SIMD |
8069 | |
8070 | public: |
8071 | //------------------------------------------------------------------------ |
8072 | // largestEnregisterableStruct: The size in bytes of the largest struct that can be enregistered. |
8073 | // |
8074 | // Notes: It is not guaranteed that the struct of this size or smaller WILL be a |
8075 | // candidate for enregistration. |
8076 | |
8077 | unsigned largestEnregisterableStructSize() |
8078 | { |
8079 | #ifdef FEATURE_SIMD |
8080 | unsigned vectorRegSize = getSIMDVectorRegisterByteLength(); |
8081 | if (vectorRegSize > TARGET_POINTER_SIZE) |
8082 | { |
8083 | return vectorRegSize; |
8084 | } |
8085 | else |
8086 | #endif // FEATURE_SIMD |
8087 | { |
8088 | return TARGET_POINTER_SIZE; |
8089 | } |
8090 | } |
8091 | |
8092 | private: |
8093 | // These routines need not be enclosed under FEATURE_SIMD since lvIsSIMDType() |
8094 | // is defined for both FEATURE_SIMD and !FEATURE_SIMD apropriately. The use |
8095 | // of this routines also avoids the need of #ifdef FEATURE_SIMD specific code. |
8096 | |
8097 | // Is this var is of type simd struct? |
8098 | bool lclVarIsSIMDType(unsigned varNum) |
8099 | { |
8100 | LclVarDsc* varDsc = lvaTable + varNum; |
8101 | return varDsc->lvIsSIMDType(); |
8102 | } |
8103 | |
8104 | // Is this Local node a SIMD local? |
8105 | bool lclVarIsSIMDType(GenTreeLclVarCommon* lclVarTree) |
8106 | { |
8107 | return lclVarIsSIMDType(lclVarTree->gtLclNum); |
8108 | } |
8109 | |
8110 | // Returns true if the TYP_SIMD locals on stack are aligned at their |
8111 | // preferred byte boundary specified by getSIMDTypeAlignment(). |
8112 | // |
8113 | // As per the Intel manual, the preferred alignment for AVX vectors is 32-bytes. On Amd64, |
8114 | // RSP/EBP is aligned at 16-bytes, therefore to align SIMD types at 32-bytes we need even |
8115 | // RSP/EBP to be 32-byte aligned. It is not clear whether additional stack space used in |
8116 | // aligning stack is worth the benefit and for now will use 16-byte alignment for AVX |
8117 | // 256-bit vectors with unaligned load/stores to/from memory. On x86, the stack frame |
8118 | // is aligned to 4 bytes. We need to extend existing support for double (8-byte) alignment |
8119 | // to 16 or 32 byte alignment for frames with local SIMD vars, if that is determined to be |
8120 | // profitable. |
8121 | // |
8122 | bool isSIMDTypeLocalAligned(unsigned varNum) |
8123 | { |
8124 | #if defined(FEATURE_SIMD) && ALIGN_SIMD_TYPES |
8125 | if (lclVarIsSIMDType(varNum) && lvaTable[varNum].lvType != TYP_BYREF) |
8126 | { |
8127 | bool ebpBased; |
8128 | int off = lvaFrameAddress(varNum, &ebpBased); |
8129 | // TODO-Cleanup: Can't this use the lvExactSize on the varDsc? |
8130 | int alignment = getSIMDTypeAlignment(lvaTable[varNum].lvType); |
8131 | bool isAligned = (alignment <= STACK_ALIGN) && ((off % alignment) == 0); |
8132 | return isAligned; |
8133 | } |
8134 | #endif // FEATURE_SIMD |
8135 | |
8136 | return false; |
8137 | } |
8138 | |
8139 | bool compSupports(InstructionSet isa) const |
8140 | { |
8141 | #if defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_) |
8142 | return (opts.compSupportsISA & (1ULL << isa)) != 0; |
8143 | #else |
8144 | return false; |
8145 | #endif |
8146 | } |
8147 | |
8148 | bool canUseVexEncoding() const |
8149 | { |
8150 | #ifdef _TARGET_XARCH_ |
8151 | return compSupports(InstructionSet_AVX); |
8152 | #else |
8153 | return false; |
8154 | #endif |
8155 | } |
8156 | |
8157 | /* |
8158 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
8159 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
8160 | XX XX |
8161 | XX Compiler XX |
8162 | XX XX |
8163 | XX Generic info about the compilation and the method being compiled. XX |
8164 | XX It is responsible for driving the other phases. XX |
8165 | XX It is also responsible for all the memory management. XX |
8166 | XX XX |
8167 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
8168 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
8169 | */ |
8170 | |
8171 | public: |
8172 | Compiler* InlineeCompiler; // The Compiler instance for the inlinee |
8173 | |
8174 | InlineResult* compInlineResult; // The result of importing the inlinee method. |
8175 | |
8176 | bool compDoAggressiveInlining; // If true, mark every method as CORINFO_FLG_FORCEINLINE |
8177 | bool compJmpOpUsed; // Does the method do a JMP |
8178 | bool compLongUsed; // Does the method use TYP_LONG |
8179 | bool compFloatingPointUsed; // Does the method use TYP_FLOAT or TYP_DOUBLE |
8180 | bool compTailCallUsed; // Does the method do a tailcall |
8181 | bool compLocallocUsed; // Does the method use localloc. |
8182 | bool compLocallocOptimized; // Does the method have an optimized localloc |
8183 | bool compQmarkUsed; // Does the method use GT_QMARK/GT_COLON |
8184 | bool compQmarkRationalized; // Is it allowed to use a GT_QMARK/GT_COLON node. |
8185 | bool compUnsafeCastUsed; // Does the method use LDIND/STIND to cast between scalar/refernce types |
8186 | |
8187 | // NOTE: These values are only reliable after |
8188 | // the importing is completely finished. |
8189 | |
8190 | #ifdef DEBUG |
8191 | // State information - which phases have completed? |
8192 | // These are kept together for easy discoverability |
8193 | |
8194 | bool bRangeAllowStress; |
8195 | bool compCodeGenDone; |
8196 | int64_t compNumStatementLinksTraversed; // # of links traversed while doing debug checks |
8197 | bool fgNormalizeEHDone; // Has the flowgraph EH normalization phase been done? |
8198 | size_t compSizeEstimate; // The estimated size of the method as per `gtSetEvalOrder`. |
8199 | size_t compCycleEstimate; // The estimated cycle count of the method as per `gtSetEvalOrder` |
8200 | #endif // DEBUG |
8201 | |
8202 | bool fgLocalVarLivenessDone; // Note that this one is used outside of debug. |
8203 | bool fgLocalVarLivenessChanged; |
8204 | #if STACK_PROBES |
8205 | bool compStackProbePrologDone; |
8206 | #endif |
8207 | bool compLSRADone; |
8208 | bool compRationalIRForm; |
8209 | |
8210 | bool compUsesThrowHelper; // There is a call to a THOROW_HELPER for the compiled method. |
8211 | |
8212 | bool compGeneratingProlog; |
8213 | bool compGeneratingEpilog; |
8214 | bool compNeedsGSSecurityCookie; // There is an unsafe buffer (or localloc) on the stack. |
8215 | // Insert cookie on frame and code to check the cookie, like VC++ -GS. |
8216 | bool compGSReorderStackLayout; // There is an unsafe buffer on the stack, reorder locals and make local |
8217 | // copies of susceptible parameters to avoid buffer overrun attacks through locals/params |
8218 | bool getNeedsGSSecurityCookie() const |
8219 | { |
8220 | return compNeedsGSSecurityCookie; |
8221 | } |
8222 | void setNeedsGSSecurityCookie() |
8223 | { |
8224 | compNeedsGSSecurityCookie = true; |
8225 | } |
8226 | |
8227 | FrameLayoutState lvaDoneFrameLayout; // The highest frame layout state that we've completed. During |
8228 | // frame layout calculations, this is the level we are currently |
8229 | // computing. |
8230 | |
8231 | //---------------------------- JITing options ----------------------------- |
8232 | |
8233 | enum codeOptimize |
8234 | { |
8235 | BLENDED_CODE, |
8236 | SMALL_CODE, |
8237 | FAST_CODE, |
8238 | |
8239 | COUNT_OPT_CODE |
8240 | }; |
8241 | |
8242 | struct Options |
8243 | { |
8244 | JitFlags* jitFlags; // all flags passed from the EE |
8245 | unsigned compFlags; // method attributes |
8246 | |
8247 | codeOptimize compCodeOpt; // what type of code optimizations |
8248 | |
8249 | bool compUseFCOMI; |
8250 | bool compUseCMOV; |
8251 | |
8252 | #if defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_) |
8253 | uint64_t compSupportsISA; |
8254 | void setSupportedISA(InstructionSet isa) |
8255 | { |
8256 | compSupportsISA |= 1ULL << isa; |
8257 | } |
8258 | #endif |
8259 | |
8260 | // optimize maximally and/or favor speed over size? |
8261 | |
8262 | #define DEFAULT_MIN_OPTS_CODE_SIZE 60000 |
8263 | #define DEFAULT_MIN_OPTS_INSTR_COUNT 20000 |
8264 | #define DEFAULT_MIN_OPTS_BB_COUNT 2000 |
8265 | #define DEFAULT_MIN_OPTS_LV_NUM_COUNT 2000 |
8266 | #define DEFAULT_MIN_OPTS_LV_REF_COUNT 8000 |
8267 | |
8268 | // Maximun number of locals before turning off the inlining |
8269 | #define MAX_LV_NUM_COUNT_FOR_INLINING 512 |
8270 | |
8271 | bool compMinOpts; |
8272 | unsigned instrCount; |
8273 | unsigned lvRefCount; |
8274 | bool compMinOptsIsSet; |
8275 | #ifdef DEBUG |
8276 | bool compMinOptsIsUsed; |
8277 | |
8278 | bool MinOpts() |
8279 | { |
8280 | assert(compMinOptsIsSet); |
8281 | compMinOptsIsUsed = true; |
8282 | return compMinOpts; |
8283 | } |
8284 | bool IsMinOptsSet() |
8285 | { |
8286 | return compMinOptsIsSet; |
8287 | } |
8288 | #else // !DEBUG |
8289 | bool MinOpts() |
8290 | { |
8291 | return compMinOpts; |
8292 | } |
8293 | bool IsMinOptsSet() |
8294 | { |
8295 | return compMinOptsIsSet; |
8296 | } |
8297 | #endif // !DEBUG |
8298 | |
8299 | bool OptimizationDisabled() |
8300 | { |
8301 | return MinOpts() || compDbgCode; |
8302 | } |
8303 | bool OptimizationEnabled() |
8304 | { |
8305 | return !OptimizationDisabled(); |
8306 | } |
8307 | |
8308 | void SetMinOpts(bool val) |
8309 | { |
8310 | assert(!compMinOptsIsUsed); |
8311 | assert(!compMinOptsIsSet || (compMinOpts == val)); |
8312 | compMinOpts = val; |
8313 | compMinOptsIsSet = true; |
8314 | } |
8315 | |
8316 | // true if the CLFLG_* for an optimization is set. |
8317 | bool OptEnabled(unsigned optFlag) |
8318 | { |
8319 | return !!(compFlags & optFlag); |
8320 | } |
8321 | |
8322 | #ifdef FEATURE_READYTORUN_COMPILER |
8323 | bool IsReadyToRun() |
8324 | { |
8325 | return jitFlags->IsSet(JitFlags::JIT_FLAG_READYTORUN); |
8326 | } |
8327 | #else |
8328 | bool IsReadyToRun() |
8329 | { |
8330 | return false; |
8331 | } |
8332 | #endif |
8333 | |
8334 | // true if we should use the PINVOKE_{BEGIN,END} helpers instead of generating |
8335 | // PInvoke transitions inline (e.g. when targeting CoreRT). |
8336 | bool ShouldUsePInvokeHelpers() |
8337 | { |
8338 | return jitFlags->IsSet(JitFlags::JIT_FLAG_USE_PINVOKE_HELPERS); |
8339 | } |
8340 | |
8341 | // true if we should use insert the REVERSE_PINVOKE_{ENTER,EXIT} helpers in the method |
8342 | // prolog/epilog |
8343 | bool IsReversePInvoke() |
8344 | { |
8345 | return jitFlags->IsSet(JitFlags::JIT_FLAG_REVERSE_PINVOKE); |
8346 | } |
8347 | |
8348 | // true if we must generate code compatible with JIT32 quirks |
8349 | bool IsJit32Compat() |
8350 | { |
8351 | #if defined(_TARGET_X86_) |
8352 | return jitFlags->IsSet(JitFlags::JIT_FLAG_DESKTOP_QUIRKS); |
8353 | #else |
8354 | return false; |
8355 | #endif |
8356 | } |
8357 | |
8358 | // true if we must generate code compatible with Jit64 quirks |
8359 | bool IsJit64Compat() |
8360 | { |
8361 | #if defined(_TARGET_AMD64_) |
8362 | return jitFlags->IsSet(JitFlags::JIT_FLAG_DESKTOP_QUIRKS); |
8363 | #elif !defined(FEATURE_CORECLR) |
8364 | return true; |
8365 | #else |
8366 | return false; |
8367 | #endif |
8368 | } |
8369 | |
8370 | bool compScopeInfo; // Generate the LocalVar info ? |
8371 | bool compDbgCode; // Generate debugger-friendly code? |
8372 | bool compDbgInfo; // Gather debugging info? |
8373 | bool compDbgEnC; |
8374 | |
8375 | #ifdef PROFILING_SUPPORTED |
8376 | bool compNoPInvokeInlineCB; |
8377 | #else |
8378 | static const bool compNoPInvokeInlineCB; |
8379 | #endif |
8380 | |
8381 | #ifdef DEBUG |
8382 | bool compGcChecks; // Check arguments and return values to ensure they are sane |
8383 | #endif |
8384 | |
8385 | #if defined(DEBUG) && defined(_TARGET_XARCH_) |
8386 | |
8387 | bool compStackCheckOnRet; // Check stack pointer on return to ensure it is correct. |
8388 | |
8389 | #endif // defined(DEBUG) && defined(_TARGET_XARCH_) |
8390 | |
8391 | #if defined(DEBUG) && defined(_TARGET_X86_) |
8392 | |
8393 | bool compStackCheckOnCall; // Check stack pointer after call to ensure it is correct. Only for x86. |
8394 | |
8395 | #endif // defined(DEBUG) && defined(_TARGET_X86_) |
8396 | |
8397 | bool compNeedSecurityCheck; // This flag really means where or not a security object needs |
8398 | // to be allocated on the stack. |
8399 | // It will be set to true in the following cases: |
8400 | // 1. When the method being compiled has a declarative security |
8401 | // (i.e. when CORINFO_FLG_NOSECURITYWRAP is reset for the current method). |
8402 | // This is also the case when we inject a prolog and epilog in the method. |
8403 | // (or) |
8404 | // 2. When the method being compiled has imperative security (i.e. the method |
8405 | // calls into another method that has CORINFO_FLG_SECURITYCHECK flag set). |
8406 | // (or) |
8407 | // 3. When opts.compDbgEnC is true. (See also Compiler::compCompile). |
8408 | // |
8409 | // When this flag is set, jit will allocate a gc-reference local variable (lvaSecurityObject), |
8410 | // which gets reported as a GC root to stackwalker. |
8411 | // (See also ICodeManager::GetAddrOfSecurityObject.) |
8412 | |
8413 | bool compReloc; // Generate relocs for pointers in code, true for all ngen/prejit codegen |
8414 | |
8415 | #ifdef DEBUG |
8416 | #if defined(_TARGET_XARCH_) |
8417 | bool compEnablePCRelAddr; // Whether absolute addr be encoded as PC-rel offset by RyuJIT where possible |
8418 | #endif |
8419 | #endif // DEBUG |
8420 | |
8421 | #ifdef UNIX_AMD64_ABI |
8422 | // This flag is indicating if there is a need to align the frame. |
8423 | // On AMD64-Windows, if there are calls, 4 slots for the outgoing ars are allocated, except for |
8424 | // FastTailCall. This slots makes the frame size non-zero, so alignment logic will be called. |
8425 | // On AMD64-Unix, there are no such slots. There is a possibility to have calls in the method with frame size of |
8426 | // 0. The frame alignment logic won't kick in. This flags takes care of the AMD64-Unix case by remembering that |
8427 | // there are calls and making sure the frame alignment logic is executed. |
8428 | bool compNeedToAlignFrame; |
8429 | #endif // UNIX_AMD64_ABI |
8430 | |
8431 | bool compProcedureSplitting; // Separate cold code from hot code |
8432 | |
8433 | bool genFPorder; // Preserve FP order (operations are non-commutative) |
8434 | bool genFPopt; // Can we do frame-pointer-omission optimization? |
8435 | bool altJit; // True if we are an altjit and are compiling this method |
8436 | |
8437 | #ifdef OPT_CONFIG |
8438 | bool optRepeat; // Repeat optimizer phases k times |
8439 | #endif |
8440 | |
8441 | #ifdef DEBUG |
8442 | bool compProcedureSplittingEH; // Separate cold code from hot code for functions with EH |
8443 | bool dspCode; // Display native code generated |
8444 | bool dspEHTable; // Display the EH table reported to the VM |
8445 | bool dspDebugInfo; // Display the Debug info reported to the VM |
8446 | bool dspInstrs; // Display the IL instructions intermixed with the native code output |
8447 | bool dspEmit; // Display emitter output |
8448 | bool dspLines; // Display source-code lines intermixed with native code output |
8449 | bool dmpHex; // Display raw bytes in hex of native code output |
8450 | bool varNames; // Display variables names in native code output |
8451 | bool disAsm; // Display native code as it is generated |
8452 | bool disAsmSpilled; // Display native code when any register spilling occurs |
8453 | bool disDiffable; // Makes the Disassembly code 'diff-able' |
8454 | bool disAsm2; // Display native code after it is generated using external disassembler |
8455 | bool dspOrder; // Display names of each of the methods that we ngen/jit |
8456 | bool dspUnwind; // Display the unwind info output |
8457 | bool dspDiffable; // Makes the Jit Dump 'diff-able' (currently uses same COMPlus_* flag as disDiffable) |
8458 | bool compLongAddress; // Force using large pseudo instructions for long address |
8459 | // (IF_LARGEJMP/IF_LARGEADR/IF_LARGLDC) |
8460 | bool dspGCtbls; // Display the GC tables |
8461 | #endif |
8462 | |
8463 | #ifdef LATE_DISASM |
8464 | bool doLateDisasm; // Run the late disassembler |
8465 | #endif // LATE_DISASM |
8466 | |
8467 | #if DUMP_GC_TABLES && !defined(DEBUG) && defined(JIT32_GCENCODER) |
8468 | // Only the JIT32_GCENCODER implements GC dumping in non-DEBUG code. |
8469 | #pragma message("NOTE: this non-debug build has GC ptr table dumping always enabled!") |
8470 | static const bool dspGCtbls = true; |
8471 | #endif |
8472 | |
8473 | // We need stack probes to guarantee that we won't trigger a stack overflow |
8474 | // when calling unmanaged code until they get a chance to set up a frame, because |
8475 | // the EE will have no idea where it is. |
8476 | // |
8477 | // We will only be doing this currently for hosted environments. Unfortunately |
8478 | // we need to take care of stubs, so potentially, we will have to do the probes |
8479 | // for any call. We have a plan for not needing for stubs though |
8480 | bool compNeedStackProbes; |
8481 | |
8482 | #ifdef PROFILING_SUPPORTED |
8483 | // Whether to emit Enter/Leave/TailCall hooks using a dummy stub (DummyProfilerELTStub()). |
8484 | // This option helps make the JIT behave as if it is running under a profiler. |
8485 | bool compJitELTHookEnabled; |
8486 | #endif // PROFILING_SUPPORTED |
8487 | |
8488 | #if FEATURE_TAILCALL_OPT |
8489 | // Whether opportunistic or implicit tail call optimization is enabled. |
8490 | bool compTailCallOpt; |
8491 | // Whether optimization of transforming a recursive tail call into a loop is enabled. |
8492 | bool compTailCallLoopOpt; |
8493 | #endif |
8494 | |
8495 | #ifdef ARM_SOFTFP |
8496 | static const bool compUseSoftFP = true; |
8497 | #else // !ARM_SOFTFP |
8498 | static const bool compUseSoftFP = false; |
8499 | #endif |
8500 | |
8501 | GCPollType compGCPollType; |
8502 | } opts; |
8503 | |
8504 | #ifdef ALT_JIT |
8505 | static bool s_pAltJitExcludeAssembliesListInitialized; |
8506 | static AssemblyNamesList2* s_pAltJitExcludeAssembliesList; |
8507 | #endif // ALT_JIT |
8508 | |
8509 | #ifdef DEBUG |
8510 | static bool s_pJitDisasmIncludeAssembliesListInitialized; |
8511 | static AssemblyNamesList2* s_pJitDisasmIncludeAssembliesList; |
8512 | #endif // DEBUG |
8513 | |
8514 | #ifdef DEBUG |
8515 | // silence warning of cast to greater size. It is easier to silence than construct code the compiler is happy with, and |
8516 | // it is safe in this case |
8517 | #pragma warning(push) |
8518 | #pragma warning(disable : 4312) |
8519 | |
8520 | template <typename T> |
8521 | T dspPtr(T p) |
8522 | { |
8523 | return (p == ZERO) ? ZERO : (opts.dspDiffable ? T(0xD1FFAB1E) : p); |
8524 | } |
8525 | |
8526 | template <typename T> |
8527 | T dspOffset(T o) |
8528 | { |
8529 | return (o == ZERO) ? ZERO : (opts.dspDiffable ? T(0xD1FFAB1E) : o); |
8530 | } |
8531 | #pragma warning(pop) |
8532 | |
8533 | static int dspTreeID(GenTree* tree) |
8534 | { |
8535 | return tree->gtTreeID; |
8536 | } |
8537 | static void printTreeID(GenTree* tree) |
8538 | { |
8539 | if (tree == nullptr) |
8540 | { |
8541 | printf("[------]" ); |
8542 | } |
8543 | else |
8544 | { |
8545 | printf("[%06d]" , dspTreeID(tree)); |
8546 | } |
8547 | } |
8548 | |
8549 | #endif // DEBUG |
8550 | |
8551 | // clang-format off |
8552 | #define STRESS_MODES \ |
8553 | \ |
8554 | STRESS_MODE(NONE) \ |
8555 | \ |
8556 | /* "Variations" stress areas which we try to mix up with each other. */ \ |
8557 | /* These should not be exhaustively used as they might */ \ |
8558 | /* hide/trivialize other areas */ \ |
8559 | \ |
8560 | STRESS_MODE(REGS) \ |
8561 | STRESS_MODE(DBL_ALN) \ |
8562 | STRESS_MODE(LCL_FLDS) \ |
8563 | STRESS_MODE(UNROLL_LOOPS) \ |
8564 | STRESS_MODE(MAKE_CSE) \ |
8565 | STRESS_MODE(LEGACY_INLINE) \ |
8566 | STRESS_MODE(CLONE_EXPR) \ |
8567 | STRESS_MODE(USE_FCOMI) \ |
8568 | STRESS_MODE(USE_CMOV) \ |
8569 | STRESS_MODE(FOLD) \ |
8570 | STRESS_MODE(BB_PROFILE) \ |
8571 | STRESS_MODE(OPT_BOOLS_GC) \ |
8572 | STRESS_MODE(REMORPH_TREES) \ |
8573 | STRESS_MODE(64RSLT_MUL) \ |
8574 | STRESS_MODE(DO_WHILE_LOOPS) \ |
8575 | STRESS_MODE(MIN_OPTS) \ |
8576 | STRESS_MODE(REVERSE_FLAG) /* Will set GTF_REVERSE_OPS whenever we can */ \ |
8577 | STRESS_MODE(REVERSE_COMMA) /* Will reverse commas created with gtNewCommaNode */ \ |
8578 | STRESS_MODE(TAILCALL) /* Will make the call as a tailcall whenever legal */ \ |
8579 | STRESS_MODE(CATCH_ARG) /* Will spill catch arg */ \ |
8580 | STRESS_MODE(UNSAFE_BUFFER_CHECKS) \ |
8581 | STRESS_MODE(NULL_OBJECT_CHECK) \ |
8582 | STRESS_MODE(PINVOKE_RESTORE_ESP) \ |
8583 | STRESS_MODE(RANDOM_INLINE) \ |
8584 | STRESS_MODE(SWITCH_CMP_BR_EXPANSION) \ |
8585 | STRESS_MODE(GENERIC_VARN) \ |
8586 | \ |
8587 | /* After COUNT_VARN, stress level 2 does all of these all the time */ \ |
8588 | \ |
8589 | STRESS_MODE(COUNT_VARN) \ |
8590 | \ |
8591 | /* "Check" stress areas that can be exhaustively used if we */ \ |
8592 | /* dont care about performance at all */ \ |
8593 | \ |
8594 | STRESS_MODE(FORCE_INLINE) /* Treat every method as AggressiveInlining */ \ |
8595 | STRESS_MODE(CHK_FLOW_UPDATE) \ |
8596 | STRESS_MODE(EMITTER) \ |
8597 | STRESS_MODE(CHK_REIMPORT) \ |
8598 | STRESS_MODE(FLATFP) \ |
8599 | STRESS_MODE(GENERIC_CHECK) \ |
8600 | STRESS_MODE(COUNT) |
8601 | |
8602 | enum compStressArea |
8603 | { |
8604 | #define STRESS_MODE(mode) STRESS_##mode, |
8605 | STRESS_MODES |
8606 | #undef STRESS_MODE |
8607 | }; |
8608 | // clang-format on |
8609 | |
8610 | #ifdef DEBUG |
8611 | static const LPCWSTR s_compStressModeNames[STRESS_COUNT + 1]; |
8612 | BYTE compActiveStressModes[STRESS_COUNT]; |
8613 | #endif // DEBUG |
8614 | |
8615 | #define MAX_STRESS_WEIGHT 100 |
8616 | |
8617 | bool compStressCompile(compStressArea stressArea, unsigned weightPercentage); |
8618 | |
8619 | #ifdef DEBUG |
8620 | |
8621 | bool compInlineStress() |
8622 | { |
8623 | return compStressCompile(STRESS_LEGACY_INLINE, 50); |
8624 | } |
8625 | |
8626 | bool compRandomInlineStress() |
8627 | { |
8628 | return compStressCompile(STRESS_RANDOM_INLINE, 50); |
8629 | } |
8630 | |
8631 | #endif // DEBUG |
8632 | |
8633 | bool compTailCallStress() |
8634 | { |
8635 | #ifdef DEBUG |
8636 | return (JitConfig.TailcallStress() != 0 || compStressCompile(STRESS_TAILCALL, 5)); |
8637 | #else |
8638 | return false; |
8639 | #endif |
8640 | } |
8641 | |
8642 | codeOptimize compCodeOpt() |
8643 | { |
8644 | #if 0 |
8645 | // Switching between size & speed has measurable throughput impact |
8646 | // (3.5% on NGen mscorlib when measured). It used to be enabled for |
8647 | // DEBUG, but should generate identical code between CHK & RET builds, |
8648 | // so that's not acceptable. |
8649 | // TODO-Throughput: Figure out what to do about size vs. speed & throughput. |
8650 | // Investigate the cause of the throughput regression. |
8651 | |
8652 | return opts.compCodeOpt; |
8653 | #else |
8654 | return BLENDED_CODE; |
8655 | #endif |
8656 | } |
8657 | |
8658 | //--------------------- Info about the procedure -------------------------- |
8659 | |
8660 | struct Info |
8661 | { |
8662 | COMP_HANDLE compCompHnd; |
8663 | CORINFO_MODULE_HANDLE compScopeHnd; |
8664 | CORINFO_CLASS_HANDLE compClassHnd; |
8665 | CORINFO_METHOD_HANDLE compMethodHnd; |
8666 | CORINFO_METHOD_INFO* compMethodInfo; |
8667 | |
8668 | BOOL hasCircularClassConstraints; |
8669 | BOOL hasCircularMethodConstraints; |
8670 | |
8671 | #if defined(DEBUG) || defined(LATE_DISASM) |
8672 | const char* compMethodName; |
8673 | const char* compClassName; |
8674 | const char* compFullName; |
8675 | #endif // defined(DEBUG) || defined(LATE_DISASM) |
8676 | |
8677 | #if defined(DEBUG) || defined(INLINE_DATA) |
8678 | // Method hash is logcally const, but computed |
8679 | // on first demand. |
8680 | mutable unsigned compMethodHashPrivate; |
8681 | unsigned compMethodHash() const; |
8682 | #endif // defined(DEBUG) || defined(INLINE_DATA) |
8683 | |
8684 | #ifdef PSEUDORANDOM_NOP_INSERTION |
8685 | // things for pseudorandom nop insertion |
8686 | unsigned compChecksum; |
8687 | CLRRandom compRNG; |
8688 | #endif |
8689 | |
8690 | // The following holds the FLG_xxxx flags for the method we're compiling. |
8691 | unsigned compFlags; |
8692 | |
8693 | // The following holds the class attributes for the method we're compiling. |
8694 | unsigned compClassAttr; |
8695 | |
8696 | const BYTE* compCode; |
8697 | IL_OFFSET compILCodeSize; // The IL code size |
8698 | UNATIVE_OFFSET compNativeCodeSize; // The native code size, after instructions are issued. This |
8699 | // is less than (compTotalHotCodeSize + compTotalColdCodeSize) only if: |
8700 | // (1) the code is not hot/cold split, and we issued less code than we expected, or |
8701 | // (2) the code is hot/cold split, and we issued less code than we expected |
8702 | // in the cold section (the hot section will always be padded out to compTotalHotCodeSize). |
8703 | |
8704 | bool compIsStatic : 1; // Is the method static (no 'this' pointer)? |
8705 | bool compIsVarArgs : 1; // Does the method have varargs parameters? |
8706 | bool compIsContextful : 1; // contextful method |
8707 | bool compInitMem : 1; // Is the CORINFO_OPT_INIT_LOCALS bit set in the method info options? |
8708 | bool compUnwrapContextful : 1; // JIT should unwrap proxies when possible |
8709 | bool compProfilerCallback : 1; // JIT inserted a profiler Enter callback |
8710 | bool compPublishStubParam : 1; // EAX captured in prolog will be available through an instrinsic |
8711 | bool compRetBuffDefStack : 1; // The ret buff argument definitely points into the stack. |
8712 | |
8713 | var_types compRetType; // Return type of the method as declared in IL |
8714 | var_types compRetNativeType; // Normalized return type as per target arch ABI |
8715 | unsigned compILargsCount; // Number of arguments (incl. implicit but not hidden) |
8716 | unsigned compArgsCount; // Number of arguments (incl. implicit and hidden) |
8717 | |
8718 | #if FEATURE_FASTTAILCALL |
8719 | size_t compArgStackSize; // Incoming argument stack size in bytes |
8720 | #endif // FEATURE_FASTTAILCALL |
8721 | |
8722 | unsigned compRetBuffArg; // position of hidden return param var (0, 1) (BAD_VAR_NUM means not present); |
8723 | int compTypeCtxtArg; // position of hidden param for type context for generic code (CORINFO_CALLCONV_PARAMTYPE) |
8724 | unsigned compThisArg; // position of implicit this pointer param (not to be confused with lvaArg0Var) |
8725 | unsigned compILlocalsCount; // Number of vars : args + locals (incl. implicit but not hidden) |
8726 | unsigned compLocalsCount; // Number of vars : args + locals (incl. implicit and hidden) |
8727 | unsigned compMaxStack; |
8728 | UNATIVE_OFFSET compTotalHotCodeSize; // Total number of bytes of Hot Code in the method |
8729 | UNATIVE_OFFSET compTotalColdCodeSize; // Total number of bytes of Cold Code in the method |
8730 | |
8731 | unsigned compCallUnmanaged; // count of unmanaged calls |
8732 | unsigned compLvFrameListRoot; // lclNum for the Frame root |
8733 | unsigned compXcptnsCount; // Number of exception-handling clauses read in the method's IL. |
8734 | // You should generally use compHndBBtabCount instead: it is the |
8735 | // current number of EH clauses (after additions like synchronized |
8736 | // methods and funclets, and removals like unreachable code deletion). |
8737 | |
8738 | bool compMatchedVM; // true if the VM is "matched": either the JIT is a cross-compiler |
8739 | // and the VM expects that, or the JIT is a "self-host" compiler |
8740 | // (e.g., x86 hosted targeting x86) and the VM expects that. |
8741 | |
8742 | /* The following holds IL scope information about local variables. |
8743 | */ |
8744 | |
8745 | unsigned compVarScopesCount; |
8746 | VarScopeDsc* compVarScopes; |
8747 | |
8748 | /* The following holds information about instr offsets for |
8749 | * which we need to report IP-mappings |
8750 | */ |
8751 | |
8752 | IL_OFFSET* compStmtOffsets; // sorted |
8753 | unsigned compStmtOffsetsCount; |
8754 | ICorDebugInfo::BoundaryTypes compStmtOffsetsImplicit; |
8755 | |
8756 | #define CPU_X86 0x0100 // The generic X86 CPU |
8757 | #define CPU_X86_PENTIUM_4 0x0110 |
8758 | |
8759 | #define CPU_X64 0x0200 // The generic x64 CPU |
8760 | #define CPU_AMD_X64 0x0210 // AMD x64 CPU |
8761 | #define CPU_INTEL_X64 0x0240 // Intel x64 CPU |
8762 | |
8763 | #define CPU_ARM 0x0300 // The generic ARM CPU |
8764 | #define CPU_ARM64 0x0400 // The generic ARM64 CPU |
8765 | |
8766 | unsigned genCPU; // What CPU are we running on |
8767 | } info; |
8768 | |
8769 | // Returns true if the method being compiled returns a non-void and non-struct value. |
8770 | // Note that lvaInitTypeRef() normalizes compRetNativeType for struct returns in a |
8771 | // single register as per target arch ABI (e.g on Amd64 Windows structs of size 1, 2, |
8772 | // 4 or 8 gets normalized to TYP_BYTE/TYP_SHORT/TYP_INT/TYP_LONG; On Arm HFA structs). |
8773 | // Methods returning such structs are considered to return non-struct return value and |
8774 | // this method returns true in that case. |
8775 | bool compMethodReturnsNativeScalarType() |
8776 | { |
8777 | return (info.compRetType != TYP_VOID) && !varTypeIsStruct(info.compRetNativeType); |
8778 | } |
8779 | |
8780 | // Returns true if the method being compiled returns RetBuf addr as its return value |
8781 | bool compMethodReturnsRetBufAddr() |
8782 | { |
8783 | // There are cases where implicit RetBuf argument should be explicitly returned in a register. |
8784 | // In such cases the return type is changed to TYP_BYREF and appropriate IR is generated. |
8785 | // These cases are: |
8786 | // 1. Profiler Leave calllback expects the address of retbuf as return value for |
8787 | // methods with hidden RetBuf argument. impReturnInstruction() when profiler |
8788 | // callbacks are needed creates GT_RETURN(TYP_BYREF, op1 = Addr of RetBuf) for |
8789 | // methods with hidden RetBufArg. |
8790 | // |
8791 | // 2. As per the System V ABI, the address of RetBuf needs to be returned by |
8792 | // methods with hidden RetBufArg in RAX. In such case GT_RETURN is of TYP_BYREF, |
8793 | // returning the address of RetBuf. |
8794 | // |
8795 | // 3. Windows 64-bit native calling convention also requires the address of RetBuff |
8796 | // to be returned in RAX. |
8797 | CLANG_FORMAT_COMMENT_ANCHOR; |
8798 | |
8799 | #ifdef _TARGET_AMD64_ |
8800 | return (info.compRetBuffArg != BAD_VAR_NUM); |
8801 | #else // !_TARGET_AMD64_ |
8802 | return (compIsProfilerHookNeeded()) && (info.compRetBuffArg != BAD_VAR_NUM); |
8803 | #endif // !_TARGET_AMD64_ |
8804 | } |
8805 | |
8806 | // Returns true if the method returns a value in more than one return register |
8807 | // TODO-ARM-Bug: Deal with multi-register genReturnLocaled structs? |
8808 | // TODO-ARM64: Does this apply for ARM64 too? |
8809 | bool compMethodReturnsMultiRegRetType() |
8810 | { |
8811 | #if FEATURE_MULTIREG_RET |
8812 | #if defined(_TARGET_X86_) |
8813 | // On x86 only 64-bit longs are returned in multiple registers |
8814 | return varTypeIsLong(info.compRetNativeType); |
8815 | #else // targets: X64-UNIX, ARM64 or ARM32 |
8816 | // On all other targets that support multireg return values: |
8817 | // Methods returning a struct in multiple registers have a return value of TYP_STRUCT. |
8818 | // Such method's compRetNativeType is TYP_STRUCT without a hidden RetBufArg |
8819 | return varTypeIsStruct(info.compRetNativeType) && (info.compRetBuffArg == BAD_VAR_NUM); |
8820 | #endif // TARGET_XXX |
8821 | |
8822 | #else // not FEATURE_MULTIREG_RET |
8823 | |
8824 | // For this architecture there are no multireg returns |
8825 | return false; |
8826 | |
8827 | #endif // FEATURE_MULTIREG_RET |
8828 | } |
8829 | |
8830 | #if FEATURE_MULTIREG_ARGS |
8831 | // Given a GenTree node of TYP_STRUCT that represents a pass by value argument |
8832 | // return the gcPtr layout for the pointers sized fields |
8833 | void getStructGcPtrsFromOp(GenTree* op, BYTE* gcPtrsOut); |
8834 | #endif // FEATURE_MULTIREG_ARGS |
8835 | |
8836 | // Returns true if the method being compiled returns a value |
8837 | bool compMethodHasRetVal() |
8838 | { |
8839 | return compMethodReturnsNativeScalarType() || compMethodReturnsRetBufAddr() || |
8840 | compMethodReturnsMultiRegRetType(); |
8841 | } |
8842 | |
8843 | #if defined(DEBUG) |
8844 | |
8845 | void compDispLocalVars(); |
8846 | |
8847 | #endif // DEBUG |
8848 | |
8849 | //-------------------------- Global Compiler Data ------------------------------------ |
8850 | |
8851 | #ifdef DEBUG |
8852 | static unsigned s_compMethodsCount; // to produce unique label names |
8853 | unsigned compGenTreeID; |
8854 | unsigned compBasicBlockID; |
8855 | #endif |
8856 | |
8857 | BasicBlock* compCurBB; // the current basic block in process |
8858 | GenTree* compCurStmt; // the current statement in process |
8859 | #ifdef DEBUG |
8860 | unsigned compCurStmtNum; // to give all statements an increasing StmtNum when printing dumps |
8861 | #endif |
8862 | |
8863 | // The following is used to create the 'method JIT info' block. |
8864 | size_t compInfoBlkSize; |
8865 | BYTE* compInfoBlkAddr; |
8866 | |
8867 | EHblkDsc* compHndBBtab; // array of EH data |
8868 | unsigned compHndBBtabCount; // element count of used elements in EH data array |
8869 | unsigned compHndBBtabAllocCount; // element count of allocated elements in EH data array |
8870 | |
8871 | #if defined(_TARGET_X86_) |
8872 | |
8873 | //------------------------------------------------------------------------- |
8874 | // Tracking of region covered by the monitor in synchronized methods |
8875 | void* syncStartEmitCookie; // the emitter cookie for first instruction after the call to MON_ENTER |
8876 | void* syncEndEmitCookie; // the emitter cookie for first instruction after the call to MON_EXIT |
8877 | |
8878 | #endif // !_TARGET_X86_ |
8879 | |
8880 | Phases previousCompletedPhase; // the most recently completed phase |
8881 | |
8882 | //------------------------------------------------------------------------- |
8883 | // The following keeps track of how many bytes of local frame space we've |
8884 | // grabbed so far in the current function, and how many argument bytes we |
8885 | // need to pop when we return. |
8886 | // |
8887 | |
8888 | unsigned compLclFrameSize; // secObject+lclBlk+locals+temps |
8889 | |
8890 | // Count of callee-saved regs we pushed in the prolog. |
8891 | // Does not include EBP for isFramePointerUsed() and double-aligned frames. |
8892 | // In case of Amd64 this doesn't include float regs saved on stack. |
8893 | unsigned compCalleeRegsPushed; |
8894 | |
8895 | #if defined(_TARGET_XARCH_) |
8896 | // Mask of callee saved float regs on stack. |
8897 | regMaskTP compCalleeFPRegsSavedMask; |
8898 | #endif |
8899 | #ifdef _TARGET_AMD64_ |
8900 | // Quirk for VS debug-launch scenario to work: |
8901 | // Bytes of padding between save-reg area and locals. |
8902 | #define VSQUIRK_STACK_PAD (2 * REGSIZE_BYTES) |
8903 | unsigned compVSQuirkStackPaddingNeeded; |
8904 | bool compQuirkForPPPflag; |
8905 | #endif |
8906 | |
8907 | unsigned compArgSize; // total size of arguments in bytes (including register args (lvIsRegArg)) |
8908 | |
8909 | unsigned compMapILargNum(unsigned ILargNum); // map accounting for hidden args |
8910 | unsigned compMapILvarNum(unsigned ILvarNum); // map accounting for hidden args |
8911 | unsigned compMap2ILvarNum(unsigned varNum); // map accounting for hidden args |
8912 | |
8913 | //------------------------------------------------------------------------- |
8914 | |
8915 | static void compStartup(); // One-time initialization |
8916 | static void compShutdown(); // One-time finalization |
8917 | |
8918 | void compInit(ArenaAllocator* pAlloc, InlineInfo* inlineInfo); |
8919 | void compDone(); |
8920 | |
8921 | static void compDisplayStaticSizes(FILE* fout); |
8922 | |
8923 | //------------ Some utility functions -------------- |
8924 | |
8925 | void* compGetHelperFtn(CorInfoHelpFunc ftnNum, /* IN */ |
8926 | void** ppIndirection); /* OUT */ |
8927 | |
8928 | // Several JIT/EE interface functions return a CorInfoType, and also return a |
8929 | // class handle as an out parameter if the type is a value class. Returns the |
8930 | // size of the type these describe. |
8931 | unsigned compGetTypeSize(CorInfoType cit, CORINFO_CLASS_HANDLE clsHnd); |
8932 | |
8933 | #ifdef DEBUG |
8934 | // Components used by the compiler may write unit test suites, and |
8935 | // have them run within this method. They will be run only once per process, and only |
8936 | // in debug. (Perhaps should be under the control of a COMPlus_ flag.) |
8937 | // These should fail by asserting. |
8938 | void compDoComponentUnitTestsOnce(); |
8939 | #endif // DEBUG |
8940 | |
8941 | int compCompile(CORINFO_METHOD_HANDLE methodHnd, |
8942 | CORINFO_MODULE_HANDLE classPtr, |
8943 | COMP_HANDLE compHnd, |
8944 | CORINFO_METHOD_INFO* methodInfo, |
8945 | void** methodCodePtr, |
8946 | ULONG* methodCodeSize, |
8947 | JitFlags* compileFlags); |
8948 | void compCompileFinish(); |
8949 | int compCompileHelper(CORINFO_MODULE_HANDLE classPtr, |
8950 | COMP_HANDLE compHnd, |
8951 | CORINFO_METHOD_INFO* methodInfo, |
8952 | void** methodCodePtr, |
8953 | ULONG* methodCodeSize, |
8954 | JitFlags* compileFlags, |
8955 | CorInfoInstantiationVerification instVerInfo); |
8956 | |
8957 | ArenaAllocator* compGetArenaAllocator(); |
8958 | |
8959 | #if MEASURE_MEM_ALLOC |
8960 | static bool s_dspMemStats; // Display per-phase memory statistics for every function |
8961 | #endif // MEASURE_MEM_ALLOC |
8962 | |
8963 | #if LOOP_HOIST_STATS |
8964 | unsigned m_loopsConsidered; |
8965 | bool m_curLoopHasHoistedExpression; |
8966 | unsigned m_loopsWithHoistedExpressions; |
8967 | unsigned m_totalHoistedExpressions; |
8968 | |
8969 | void AddLoopHoistStats(); |
8970 | void PrintPerMethodLoopHoistStats(); |
8971 | |
8972 | static CritSecObject s_loopHoistStatsLock; // This lock protects the data structures below. |
8973 | static unsigned s_loopsConsidered; |
8974 | static unsigned s_loopsWithHoistedExpressions; |
8975 | static unsigned s_totalHoistedExpressions; |
8976 | |
8977 | static void PrintAggregateLoopHoistStats(FILE* f); |
8978 | #endif // LOOP_HOIST_STATS |
8979 | |
8980 | bool compIsForImportOnly(); |
8981 | bool compIsForInlining(); |
8982 | bool compDonotInline(); |
8983 | |
8984 | #ifdef DEBUG |
8985 | unsigned char compGetJitDefaultFill(); // Get the default fill char value |
8986 | // we randomize this value when JitStress is enabled |
8987 | |
8988 | const char* compLocalVarName(unsigned varNum, unsigned offs); |
8989 | VarName compVarName(regNumber reg, bool isFloatReg = false); |
8990 | const char* compRegVarName(regNumber reg, bool displayVar = false, bool isFloatReg = false); |
8991 | const char* compRegNameForSize(regNumber reg, size_t size); |
8992 | const char* compFPregVarName(unsigned fpReg, bool displayVar = false); |
8993 | void compDspSrcLinesByNativeIP(UNATIVE_OFFSET curIP); |
8994 | void compDspSrcLinesByLineNum(unsigned line, bool seek = false); |
8995 | #endif // DEBUG |
8996 | |
8997 | //------------------------------------------------------------------------- |
8998 | |
8999 | struct VarScopeListNode |
9000 | { |
9001 | VarScopeDsc* data; |
9002 | VarScopeListNode* next; |
9003 | static VarScopeListNode* Create(VarScopeDsc* value, CompAllocator alloc) |
9004 | { |
9005 | VarScopeListNode* node = new (alloc) VarScopeListNode; |
9006 | node->data = value; |
9007 | node->next = nullptr; |
9008 | return node; |
9009 | } |
9010 | }; |
9011 | |
9012 | struct VarScopeMapInfo |
9013 | { |
9014 | VarScopeListNode* head; |
9015 | VarScopeListNode* tail; |
9016 | static VarScopeMapInfo* Create(VarScopeListNode* node, CompAllocator alloc) |
9017 | { |
9018 | VarScopeMapInfo* info = new (alloc) VarScopeMapInfo; |
9019 | info->head = node; |
9020 | info->tail = node; |
9021 | return info; |
9022 | } |
9023 | }; |
9024 | |
9025 | // Max value of scope count for which we would use linear search; for larger values we would use hashtable lookup. |
9026 | static const unsigned MAX_LINEAR_FIND_LCL_SCOPELIST = 32; |
9027 | |
9028 | typedef JitHashTable<unsigned, JitSmallPrimitiveKeyFuncs<unsigned>, VarScopeMapInfo*> VarNumToScopeDscMap; |
9029 | |
9030 | // Map to keep variables' scope indexed by varNum containing it's scope dscs at the index. |
9031 | VarNumToScopeDscMap* compVarScopeMap; |
9032 | |
9033 | VarScopeDsc* compFindLocalVar(unsigned varNum, unsigned lifeBeg, unsigned lifeEnd); |
9034 | |
9035 | VarScopeDsc* compFindLocalVar(unsigned varNum, unsigned offs); |
9036 | |
9037 | VarScopeDsc* compFindLocalVarLinear(unsigned varNum, unsigned offs); |
9038 | |
9039 | void compInitVarScopeMap(); |
9040 | |
9041 | VarScopeDsc** compEnterScopeList; // List has the offsets where variables |
9042 | // enter scope, sorted by instr offset |
9043 | unsigned compNextEnterScope; |
9044 | |
9045 | VarScopeDsc** compExitScopeList; // List has the offsets where variables |
9046 | // go out of scope, sorted by instr offset |
9047 | unsigned compNextExitScope; |
9048 | |
9049 | void compInitScopeLists(); |
9050 | |
9051 | void compResetScopeLists(); |
9052 | |
9053 | VarScopeDsc* compGetNextEnterScope(unsigned offs, bool scan = false); |
9054 | |
9055 | VarScopeDsc* compGetNextExitScope(unsigned offs, bool scan = false); |
9056 | |
9057 | void compProcessScopesUntil(unsigned offset, |
9058 | VARSET_TP* inScope, |
9059 | void (Compiler::*enterScopeFn)(VARSET_TP* inScope, VarScopeDsc*), |
9060 | void (Compiler::*exitScopeFn)(VARSET_TP* inScope, VarScopeDsc*)); |
9061 | |
9062 | #ifdef DEBUG |
9063 | void compDispScopeLists(); |
9064 | #endif // DEBUG |
9065 | |
9066 | bool compIsProfilerHookNeeded(); |
9067 | |
9068 | //------------------------------------------------------------------------- |
9069 | /* Statistical Data Gathering */ |
9070 | |
9071 | void compJitStats(); // call this function and enable |
9072 | // various ifdef's below for statistical data |
9073 | |
9074 | #if CALL_ARG_STATS |
9075 | void compCallArgStats(); |
9076 | static void compDispCallArgStats(FILE* fout); |
9077 | #endif |
9078 | |
9079 | //------------------------------------------------------------------------- |
9080 | |
9081 | protected: |
9082 | #ifdef DEBUG |
9083 | bool skipMethod(); |
9084 | #endif |
9085 | |
9086 | ArenaAllocator* compArenaAllocator; |
9087 | |
9088 | public: |
9089 | void compFunctionTraceStart(); |
9090 | void compFunctionTraceEnd(void* methodCodePtr, ULONG methodCodeSize, bool isNYI); |
9091 | |
9092 | protected: |
9093 | size_t compMaxUncheckedOffsetForNullObject; |
9094 | |
9095 | void compInitOptions(JitFlags* compileFlags); |
9096 | |
9097 | void compSetProcessor(); |
9098 | void compInitDebuggingInfo(); |
9099 | void compSetOptimizationLevel(); |
9100 | #ifdef _TARGET_ARMARCH_ |
9101 | bool compRsvdRegCheck(FrameLayoutState curState); |
9102 | #endif |
9103 | void compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags* compileFlags); |
9104 | |
9105 | // Clear annotations produced during optimizations; to be used between iterations when repeating opts. |
9106 | void ResetOptAnnotations(); |
9107 | |
9108 | // Regenerate loop descriptors; to be used between iterations when repeating opts. |
9109 | void RecomputeLoopInfo(); |
9110 | |
9111 | #ifdef PROFILING_SUPPORTED |
9112 | // Data required for generating profiler Enter/Leave/TailCall hooks |
9113 | |
9114 | bool compProfilerHookNeeded; // Whether profiler Enter/Leave/TailCall hook needs to be generated for the method |
9115 | void* compProfilerMethHnd; // Profiler handle of the method being compiled. Passed as param to ELT callbacks |
9116 | bool compProfilerMethHndIndirected; // Whether compProfilerHandle is pointer to the handle or is an actual handle |
9117 | #endif |
9118 | |
9119 | #ifdef _TARGET_AMD64_ |
9120 | bool compQuirkForPPP(); // Check if this method should be Quirked for the PPP issue |
9121 | #endif |
9122 | public: |
9123 | // Assumes called as part of process shutdown; does any compiler-specific work associated with that. |
9124 | static void ProcessShutdownWork(ICorStaticInfo* statInfo); |
9125 | |
9126 | CompAllocator getAllocator(CompMemKind cmk = CMK_Generic) |
9127 | { |
9128 | return CompAllocator(compArenaAllocator, cmk); |
9129 | } |
9130 | |
9131 | CompAllocator getAllocatorGC() |
9132 | { |
9133 | return getAllocator(CMK_GC); |
9134 | } |
9135 | |
9136 | CompAllocator getAllocatorLoopHoist() |
9137 | { |
9138 | return getAllocator(CMK_LoopHoist); |
9139 | } |
9140 | |
9141 | #ifdef DEBUG |
9142 | CompAllocator getAllocatorDebugOnly() |
9143 | { |
9144 | return getAllocator(CMK_DebugOnly); |
9145 | } |
9146 | #endif // DEBUG |
9147 | |
9148 | /* |
9149 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9150 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9151 | XX XX |
9152 | XX typeInfo XX |
9153 | XX XX |
9154 | XX Checks for type compatibility and merges types XX |
9155 | XX XX |
9156 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9157 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9158 | */ |
9159 | |
9160 | public: |
9161 | // Set to TRUE if verification cannot be skipped for this method |
9162 | // If we detect unverifiable code, we will lazily check |
9163 | // canSkipMethodVerification() to see if verification is REALLY needed. |
9164 | BOOL tiVerificationNeeded; |
9165 | |
9166 | // It it initially TRUE, and it gets set to FALSE if we run into unverifiable code |
9167 | // Note that this is valid only if tiVerificationNeeded was ever TRUE. |
9168 | BOOL tiIsVerifiableCode; |
9169 | |
9170 | // Set to TRUE if runtime callout is needed for this method |
9171 | BOOL tiRuntimeCalloutNeeded; |
9172 | |
9173 | // Set to TRUE if security prolog/epilog callout is needed for this method |
9174 | // Note: This flag is different than compNeedSecurityCheck. |
9175 | // compNeedSecurityCheck means whether or not a security object needs |
9176 | // to be allocated on the stack, which is currently true for EnC as well. |
9177 | // tiSecurityCalloutNeeded means whether or not security callouts need |
9178 | // to be inserted in the jitted code. |
9179 | BOOL tiSecurityCalloutNeeded; |
9180 | |
9181 | // Returns TRUE if child is equal to or a subtype of parent for merge purposes |
9182 | // This support is necessary to suport attributes that are not described in |
9183 | // for example, signatures. For example, the permanent home byref (byref that |
9184 | // points to the gc heap), isn't a property of method signatures, therefore, |
9185 | // it is safe to have mismatches here (that tiCompatibleWith will not flag), |
9186 | // but when deciding if we need to reimport a block, we need to take these |
9187 | // in account |
9188 | BOOL tiMergeCompatibleWith(const typeInfo& pChild, const typeInfo& pParent, bool normalisedForStack) const; |
9189 | |
9190 | // Returns TRUE if child is equal to or a subtype of parent. |
9191 | // normalisedForStack indicates that both types are normalised for the stack |
9192 | BOOL tiCompatibleWith(const typeInfo& pChild, const typeInfo& pParent, bool normalisedForStack) const; |
9193 | |
9194 | // Merges pDest and pSrc. Returns FALSE if merge is undefined. |
9195 | // *pDest is modified to represent the merged type. Sets "*changed" to true |
9196 | // if this changes "*pDest". |
9197 | BOOL tiMergeToCommonParent(typeInfo* pDest, const typeInfo* pSrc, bool* changed) const; |
9198 | |
9199 | #ifdef DEBUG |
9200 | // <BUGNUM> VSW 471305 |
9201 | // IJW allows assigning REF to BYREF. The following allows us to temporarily |
9202 | // bypass the assert check in gcMarkRegSetGCref and gcMarkRegSetByref |
9203 | // We use a "short" as we need to push/pop this scope. |
9204 | // </BUGNUM> |
9205 | short compRegSetCheckLevel; |
9206 | #endif |
9207 | |
9208 | /* |
9209 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9210 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9211 | XX XX |
9212 | XX IL verification stuff XX |
9213 | XX XX |
9214 | XX XX |
9215 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9216 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9217 | */ |
9218 | |
9219 | public: |
9220 | // The following is used to track liveness of local variables, initialization |
9221 | // of valueclass constructors, and type safe use of IL instructions. |
9222 | |
9223 | // dynamic state info needed for verification |
9224 | EntryState verCurrentState; |
9225 | |
9226 | // this ptr of object type .ctors are considered intited only after |
9227 | // the base class ctor is called, or an alternate ctor is called. |
9228 | // An uninited this ptr can be used to access fields, but cannot |
9229 | // be used to call a member function. |
9230 | BOOL verTrackObjCtorInitState; |
9231 | |
9232 | void verInitBBEntryState(BasicBlock* block, EntryState* currentState); |
9233 | |
9234 | // Requires that "tis" is not TIS_Bottom -- it's a definite init/uninit state. |
9235 | void verSetThisInit(BasicBlock* block, ThisInitState tis); |
9236 | void verInitCurrentState(); |
9237 | void verResetCurrentState(BasicBlock* block, EntryState* currentState); |
9238 | |
9239 | // Merges the current verification state into the entry state of "block", return FALSE if that merge fails, |
9240 | // TRUE if it succeeds. Further sets "*changed" to true if this changes the entry state of "block". |
9241 | BOOL verMergeEntryStates(BasicBlock* block, bool* changed); |
9242 | |
9243 | void verConvertBBToThrowVerificationException(BasicBlock* block DEBUGARG(bool logMsg)); |
9244 | void verHandleVerificationFailure(BasicBlock* block DEBUGARG(bool logMsg)); |
9245 | typeInfo verMakeTypeInfo(CORINFO_CLASS_HANDLE clsHnd, |
9246 | bool bashStructToRef = false); // converts from jit type representation to typeInfo |
9247 | typeInfo verMakeTypeInfo(CorInfoType ciType, |
9248 | CORINFO_CLASS_HANDLE clsHnd); // converts from jit type representation to typeInfo |
9249 | BOOL verIsSDArray(typeInfo ti); |
9250 | typeInfo verGetArrayElemType(typeInfo ti); |
9251 | |
9252 | typeInfo verParseArgSigToTypeInfo(CORINFO_SIG_INFO* sig, CORINFO_ARG_LIST_HANDLE args); |
9253 | BOOL verNeedsVerification(); |
9254 | BOOL verIsByRefLike(const typeInfo& ti); |
9255 | BOOL verIsSafeToReturnByRef(const typeInfo& ti); |
9256 | |
9257 | // generic type variables range over types that satisfy IsBoxable |
9258 | BOOL verIsBoxable(const typeInfo& ti); |
9259 | |
9260 | void DECLSPEC_NORETURN verRaiseVerifyException(INDEBUG(const char* reason) DEBUGARG(const char* file) |
9261 | DEBUGARG(unsigned line)); |
9262 | void verRaiseVerifyExceptionIfNeeded(INDEBUG(const char* reason) DEBUGARG(const char* file) |
9263 | DEBUGARG(unsigned line)); |
9264 | bool verCheckTailCallConstraint(OPCODE opcode, |
9265 | CORINFO_RESOLVED_TOKEN* pResolvedToken, |
9266 | CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, // Is this a "constrained." call |
9267 | // on a type parameter? |
9268 | bool speculative // If true, won't throw if verificatoin fails. Instead it will |
9269 | // return false to the caller. |
9270 | // If false, it will throw. |
9271 | ); |
9272 | bool verIsBoxedValueType(typeInfo ti); |
9273 | |
9274 | void verVerifyCall(OPCODE opcode, |
9275 | CORINFO_RESOLVED_TOKEN* pResolvedToken, |
9276 | CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, |
9277 | bool tailCall, |
9278 | bool readonlyCall, // is this a "readonly." call? |
9279 | const BYTE* delegateCreateStart, |
9280 | const BYTE* codeAddr, |
9281 | CORINFO_CALL_INFO* callInfo DEBUGARG(const char* methodName)); |
9282 | |
9283 | BOOL verCheckDelegateCreation(const BYTE* delegateCreateStart, const BYTE* codeAddr, mdMemberRef& targetMemberRef); |
9284 | |
9285 | typeInfo verVerifySTIND(const typeInfo& ptr, const typeInfo& value, const typeInfo& instrType); |
9286 | typeInfo verVerifyLDIND(const typeInfo& ptr, const typeInfo& instrType); |
9287 | void verVerifyField(CORINFO_RESOLVED_TOKEN* pResolvedToken, |
9288 | const CORINFO_FIELD_INFO& fieldInfo, |
9289 | const typeInfo* tiThis, |
9290 | BOOL mutator, |
9291 | BOOL allowPlainStructAsThis = FALSE); |
9292 | void verVerifyCond(const typeInfo& tiOp1, const typeInfo& tiOp2, unsigned opcode); |
9293 | void verVerifyThisPtrInitialised(); |
9294 | BOOL verIsCallToInitThisPtr(CORINFO_CLASS_HANDLE context, CORINFO_CLASS_HANDLE target); |
9295 | |
9296 | #ifdef DEBUG |
9297 | |
9298 | // One line log function. Default level is 0. Increasing it gives you |
9299 | // more log information |
9300 | |
9301 | // levels are currently unused: #define JITDUMP(level,...) (); |
9302 | void JitLogEE(unsigned level, const char* fmt, ...); |
9303 | |
9304 | bool compDebugBreak; |
9305 | |
9306 | bool compJitHaltMethod(); |
9307 | |
9308 | #endif |
9309 | |
9310 | /* |
9311 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9312 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9313 | XX XX |
9314 | XX GS Security checks for unsafe buffers XX |
9315 | XX XX |
9316 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9317 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
9318 | */ |
9319 | public: |
9320 | struct ShadowParamVarInfo |
9321 | { |
9322 | FixedBitVect* assignGroup; // the closure set of variables whose values depend on each other |
9323 | unsigned shadowCopy; // Lcl var num, valid only if not set to NO_SHADOW_COPY |
9324 | |
9325 | static bool mayNeedShadowCopy(LclVarDsc* varDsc) |
9326 | { |
9327 | #if defined(_TARGET_AMD64_) |
9328 | // GS cookie logic to create shadow slots, create trees to copy reg args to shadow |
9329 | // slots and update all trees to refer to shadow slots is done immediately after |
9330 | // fgMorph(). Lsra could potentially mark a param as DoNotEnregister after JIT determines |
9331 | // not to shadow a parameter. Also, LSRA could potentially spill a param which is passed |
9332 | // in register. Therefore, conservatively all params may need a shadow copy. Note that |
9333 | // GS cookie logic further checks whether the param is a ptr or an unsafe buffer before |
9334 | // creating a shadow slot even though this routine returns true. |
9335 | // |
9336 | // TODO-AMD64-CQ: Revisit this conservative approach as it could create more shadow slots than |
9337 | // required. There are two cases under which a reg arg could potentially be used from its |
9338 | // home location: |
9339 | // a) LSRA marks it as DoNotEnregister (see LinearScan::identifyCandidates()) |
9340 | // b) LSRA spills it |
9341 | // |
9342 | // Possible solution to address case (a) |
9343 | // - The conditions under which LSRA marks a varDsc as DoNotEnregister could be checked |
9344 | // in this routine. Note that live out of exception handler is something we may not be |
9345 | // able to do it here since GS cookie logic is invoked ahead of liveness computation. |
9346 | // Therefore, for methods with exception handling and need GS cookie check we might have |
9347 | // to take conservative approach. |
9348 | // |
9349 | // Possible solution to address case (b) |
9350 | // - Whenver a parameter passed in an argument register needs to be spilled by LSRA, we |
9351 | // create a new spill temp if the method needs GS cookie check. |
9352 | return varDsc->lvIsParam; |
9353 | #else // !defined(_TARGET_AMD64_) |
9354 | return varDsc->lvIsParam && !varDsc->lvIsRegArg; |
9355 | #endif |
9356 | } |
9357 | |
9358 | #ifdef DEBUG |
9359 | void Print() |
9360 | { |
9361 | printf("assignGroup [%p]; shadowCopy: [%d];\n" , assignGroup, shadowCopy); |
9362 | } |
9363 | #endif |
9364 | }; |
9365 | |
9366 | GSCookie* gsGlobalSecurityCookieAddr; // Address of global cookie for unsafe buffer checks |
9367 | GSCookie gsGlobalSecurityCookieVal; // Value of global cookie if addr is NULL |
9368 | ShadowParamVarInfo* gsShadowVarInfo; // Table used by shadow param analysis code |
9369 | |
9370 | void gsGSChecksInitCookie(); // Grabs cookie variable |
9371 | void gsCopyShadowParams(); // Identify vulnerable params and create dhadow copies |
9372 | bool gsFindVulnerableParams(); // Shadow param analysis code |
9373 | void gsParamsToShadows(); // Insert copy code and replave param uses by shadow |
9374 | |
9375 | static fgWalkPreFn gsMarkPtrsAndAssignGroups; // Shadow param analysis tree-walk |
9376 | static fgWalkPreFn gsReplaceShadowParams; // Shadow param replacement tree-walk |
9377 | |
9378 | #define DEFAULT_MAX_INLINE_SIZE 100 // Methods with > DEFAULT_MAX_INLINE_SIZE IL bytes will never be inlined. |
9379 | // This can be overwritten by setting complus_JITInlineSize env variable. |
9380 | |
9381 | #define DEFAULT_MAX_INLINE_DEPTH 20 // Methods at more than this level deep will not be inlined |
9382 | |
9383 | #define DEFAULT_MAX_LOCALLOC_TO_LOCAL_SIZE 32 // fixed locallocs of this size or smaller will convert to local buffers |
9384 | |
9385 | private: |
9386 | #ifdef FEATURE_JIT_METHOD_PERF |
9387 | JitTimer* pCompJitTimer; // Timer data structure (by phases) for current compilation. |
9388 | static CompTimeSummaryInfo s_compJitTimerSummary; // Summary of the Timer information for the whole run. |
9389 | |
9390 | static LPCWSTR JitTimeLogCsv(); // Retrieve the file name for CSV from ConfigDWORD. |
9391 | static LPCWSTR compJitTimeLogFilename; // If a log file for JIT time is desired, filename to write it to. |
9392 | #endif |
9393 | inline void EndPhase(Phases phase); // Indicate the end of the given phase. |
9394 | |
9395 | #if MEASURE_CLRAPI_CALLS |
9396 | // Thin wrappers that call into JitTimer (if present). |
9397 | inline void CLRApiCallEnter(unsigned apix); |
9398 | inline void CLRApiCallLeave(unsigned apix); |
9399 | |
9400 | public: |
9401 | inline void CLR_API_Enter(API_ICorJitInfo_Names ename); |
9402 | inline void CLR_API_Leave(API_ICorJitInfo_Names ename); |
9403 | |
9404 | private: |
9405 | #endif |
9406 | |
9407 | #if defined(DEBUG) || defined(INLINE_DATA) || defined(FEATURE_CLRSQM) |
9408 | // These variables are associated with maintaining SQM data about compile time. |
9409 | unsigned __int64 m_compCyclesAtEndOfInlining; // The thread-virtualized cycle count at the end of the inlining phase |
9410 | // in the current compilation. |
9411 | unsigned __int64 m_compCycles; // Net cycle count for current compilation |
9412 | DWORD m_compTickCountAtEndOfInlining; // The result of GetTickCount() (# ms since some epoch marker) at the end of |
9413 | // the inlining phase in the current compilation. |
9414 | #endif // defined(DEBUG) || defined(INLINE_DATA) || defined(FEATURE_CLRSQM) |
9415 | |
9416 | // Records the SQM-relevant (cycles and tick count). Should be called after inlining is complete. |
9417 | // (We do this after inlining because this marks the last point at which the JIT is likely to cause |
9418 | // type-loading and class initialization). |
9419 | void RecordStateAtEndOfInlining(); |
9420 | // Assumes being called at the end of compilation. Update the SQM state. |
9421 | void RecordStateAtEndOfCompilation(); |
9422 | |
9423 | #ifdef FEATURE_CLRSQM |
9424 | // Does anything SQM related necessary at process shutdown time. |
9425 | static void ProcessShutdownSQMWork(ICorStaticInfo* statInfo); |
9426 | #endif // FEATURE_CLRSQM |
9427 | |
9428 | public: |
9429 | #if FUNC_INFO_LOGGING |
9430 | static LPCWSTR compJitFuncInfoFilename; // If a log file for per-function information is required, this is the |
9431 | // filename to write it to. |
9432 | static FILE* compJitFuncInfoFile; // And this is the actual FILE* to write to. |
9433 | #endif // FUNC_INFO_LOGGING |
9434 | |
9435 | Compiler* prevCompiler; // Previous compiler on stack for TLS Compiler* linked list for reentrant compilers. |
9436 | |
9437 | // Is the compilation in a full trust context? |
9438 | bool compIsFullTrust(); |
9439 | |
9440 | #if MEASURE_NOWAY |
9441 | void RecordNowayAssert(const char* filename, unsigned line, const char* condStr); |
9442 | #endif // MEASURE_NOWAY |
9443 | |
9444 | #ifndef FEATURE_TRACELOGGING |
9445 | // Should we actually fire the noway assert body and the exception handler? |
9446 | bool compShouldThrowOnNoway(); |
9447 | #else // FEATURE_TRACELOGGING |
9448 | // Should we actually fire the noway assert body and the exception handler? |
9449 | bool compShouldThrowOnNoway(const char* filename, unsigned line); |
9450 | |
9451 | // Telemetry instance to use per method compilation. |
9452 | JitTelemetry compJitTelemetry; |
9453 | |
9454 | // Get common parameters that have to be logged with most telemetry data. |
9455 | void compGetTelemetryDefaults(const char** assemblyName, |
9456 | const char** scopeName, |
9457 | const char** methodName, |
9458 | unsigned* methodHash); |
9459 | #endif // !FEATURE_TRACELOGGING |
9460 | |
9461 | #ifdef DEBUG |
9462 | private: |
9463 | NodeToTestDataMap* m_nodeTestData; |
9464 | |
9465 | static const unsigned FIRST_LOOP_HOIST_CSE_CLASS = 1000; |
9466 | unsigned m_loopHoistCSEClass; // LoopHoist test annotations turn into CSE requirements; we |
9467 | // label them with CSE Class #'s starting at FIRST_LOOP_HOIST_CSE_CLASS. |
9468 | // Current kept in this. |
9469 | public: |
9470 | NodeToTestDataMap* GetNodeTestData() |
9471 | { |
9472 | Compiler* compRoot = impInlineRoot(); |
9473 | if (compRoot->m_nodeTestData == nullptr) |
9474 | { |
9475 | compRoot->m_nodeTestData = new (getAllocatorDebugOnly()) NodeToTestDataMap(getAllocatorDebugOnly()); |
9476 | } |
9477 | return compRoot->m_nodeTestData; |
9478 | } |
9479 | |
9480 | typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, int> NodeToIntMap; |
9481 | |
9482 | // Returns the set (i.e., the domain of the result map) of nodes that are keys in m_nodeTestData, and |
9483 | // currently occur in the AST graph. |
9484 | NodeToIntMap* FindReachableNodesInNodeTestData(); |
9485 | |
9486 | // Node "from" is being eliminated, and being replaced by node "to". If "from" had any associated |
9487 | // test data, associate that data with "to". |
9488 | void TransferTestDataToNode(GenTree* from, GenTree* to); |
9489 | |
9490 | // Requires that "to" is a clone of "from". If any nodes in the "from" tree |
9491 | // have annotations, attach similar annotations to the corresponding nodes in "to". |
9492 | void CopyTestDataToCloneTree(GenTree* from, GenTree* to); |
9493 | |
9494 | // These are the methods that test that the various conditions implied by the |
9495 | // test attributes are satisfied. |
9496 | void JitTestCheckSSA(); // SSA builder tests. |
9497 | void JitTestCheckVN(); // Value numbering tests. |
9498 | #endif // DEBUG |
9499 | |
9500 | // The "FieldSeqStore", for canonicalizing field sequences. See the definition of FieldSeqStore for |
9501 | // operations. |
9502 | FieldSeqStore* m_fieldSeqStore; |
9503 | |
9504 | FieldSeqStore* GetFieldSeqStore() |
9505 | { |
9506 | Compiler* compRoot = impInlineRoot(); |
9507 | if (compRoot->m_fieldSeqStore == nullptr) |
9508 | { |
9509 | // Create a CompAllocator that labels sub-structure with CMK_FieldSeqStore, and use that for allocation. |
9510 | CompAllocator ialloc(getAllocator(CMK_FieldSeqStore)); |
9511 | compRoot->m_fieldSeqStore = new (ialloc) FieldSeqStore(ialloc); |
9512 | } |
9513 | return compRoot->m_fieldSeqStore; |
9514 | } |
9515 | |
9516 | typedef JitHashTable<GenTree*, JitPtrKeyFuncs<GenTree>, FieldSeqNode*> NodeToFieldSeqMap; |
9517 | |
9518 | // Some nodes of "TYP_BYREF" or "TYP_I_IMPL" actually represent the address of a field within a struct, but since |
9519 | // the offset of the field is zero, there's no "GT_ADD" node. We normally attach a field sequence to the constant |
9520 | // that is added, but what do we do when that constant is zero, and is thus not present? We use this mechanism to |
9521 | // attach the field sequence directly to the address node. |
9522 | NodeToFieldSeqMap* m_zeroOffsetFieldMap; |
9523 | |
9524 | NodeToFieldSeqMap* GetZeroOffsetFieldMap() |
9525 | { |
9526 | // Don't need to worry about inlining here |
9527 | if (m_zeroOffsetFieldMap == nullptr) |
9528 | { |
9529 | // Create a CompAllocator that labels sub-structure with CMK_ZeroOffsetFieldMap, and use that for |
9530 | // allocation. |
9531 | CompAllocator ialloc(getAllocator(CMK_ZeroOffsetFieldMap)); |
9532 | m_zeroOffsetFieldMap = new (ialloc) NodeToFieldSeqMap(ialloc); |
9533 | } |
9534 | return m_zeroOffsetFieldMap; |
9535 | } |
9536 | |
9537 | // Requires that "op1" is a node of type "TYP_BYREF" or "TYP_I_IMPL". We are dereferencing this with the fields in |
9538 | // "fieldSeq", whose offsets are required all to be zero. Ensures that any field sequence annotation currently on |
9539 | // "op1" or its components is augmented by appending "fieldSeq". In practice, if "op1" is a GT_LCL_FLD, it has |
9540 | // a field sequence as a member; otherwise, it may be the addition of an a byref and a constant, where the const |
9541 | // has a field sequence -- in this case "fieldSeq" is appended to that of the constant; otherwise, we |
9542 | // record the the field sequence using the ZeroOffsetFieldMap described above. |
9543 | // |
9544 | // One exception above is that "op1" is a node of type "TYP_REF" where "op1" is a GT_LCL_VAR. |
9545 | // This happens when System.Object vtable pointer is a regular field at offset 0 in System.Private.CoreLib in |
9546 | // CoreRT. Such case is handled same as the default case. |
9547 | void fgAddFieldSeqForZeroOffset(GenTree* op1, FieldSeqNode* fieldSeq); |
9548 | |
9549 | typedef JitHashTable<const GenTree*, JitPtrKeyFuncs<GenTree>, ArrayInfo> NodeToArrayInfoMap; |
9550 | NodeToArrayInfoMap* m_arrayInfoMap; |
9551 | |
9552 | NodeToArrayInfoMap* GetArrayInfoMap() |
9553 | { |
9554 | Compiler* compRoot = impInlineRoot(); |
9555 | if (compRoot->m_arrayInfoMap == nullptr) |
9556 | { |
9557 | // Create a CompAllocator that labels sub-structure with CMK_ArrayInfoMap, and use that for allocation. |
9558 | CompAllocator ialloc(getAllocator(CMK_ArrayInfoMap)); |
9559 | compRoot->m_arrayInfoMap = new (ialloc) NodeToArrayInfoMap(ialloc); |
9560 | } |
9561 | return compRoot->m_arrayInfoMap; |
9562 | } |
9563 | |
9564 | //----------------------------------------------------------------------------------------------------------------- |
9565 | // Compiler::TryGetArrayInfo: |
9566 | // Given an indirection node, checks to see whether or not that indirection represents an array access, and |
9567 | // if so returns information about the array. |
9568 | // |
9569 | // Arguments: |
9570 | // indir - The `GT_IND` node. |
9571 | // arrayInfo (out) - Information about the accessed array if this function returns true. Undefined otherwise. |
9572 | // |
9573 | // Returns: |
9574 | // True if the `GT_IND` node represents an array access; false otherwise. |
9575 | bool TryGetArrayInfo(GenTreeIndir* indir, ArrayInfo* arrayInfo) |
9576 | { |
9577 | if ((indir->gtFlags & GTF_IND_ARR_INDEX) == 0) |
9578 | { |
9579 | return false; |
9580 | } |
9581 | |
9582 | if (indir->gtOp1->OperIs(GT_INDEX_ADDR)) |
9583 | { |
9584 | GenTreeIndexAddr* const indexAddr = indir->gtOp1->AsIndexAddr(); |
9585 | *arrayInfo = ArrayInfo(indexAddr->gtElemType, indexAddr->gtElemSize, indexAddr->gtElemOffset, |
9586 | indexAddr->gtStructElemClass); |
9587 | return true; |
9588 | } |
9589 | |
9590 | bool found = GetArrayInfoMap()->Lookup(indir, arrayInfo); |
9591 | assert(found); |
9592 | return true; |
9593 | } |
9594 | |
9595 | NodeToUnsignedMap* m_memorySsaMap[MemoryKindCount]; |
9596 | |
9597 | // In some cases, we want to assign intermediate SSA #'s to memory states, and know what nodes create those memory |
9598 | // states. (We do this for try blocks, where, if the try block doesn't do a call that loses track of the memory |
9599 | // state, all the possible memory states are possible initial states of the corresponding catch block(s).) |
9600 | NodeToUnsignedMap* GetMemorySsaMap(MemoryKind memoryKind) |
9601 | { |
9602 | if (memoryKind == GcHeap && byrefStatesMatchGcHeapStates) |
9603 | { |
9604 | // Use the same map for GCHeap and ByrefExposed when their states match. |
9605 | memoryKind = ByrefExposed; |
9606 | } |
9607 | |
9608 | assert(memoryKind < MemoryKindCount); |
9609 | Compiler* compRoot = impInlineRoot(); |
9610 | if (compRoot->m_memorySsaMap[memoryKind] == nullptr) |
9611 | { |
9612 | // Create a CompAllocator that labels sub-structure with CMK_ArrayInfoMap, and use that for allocation. |
9613 | CompAllocator ialloc(getAllocator(CMK_ArrayInfoMap)); |
9614 | compRoot->m_memorySsaMap[memoryKind] = new (ialloc) NodeToUnsignedMap(ialloc); |
9615 | } |
9616 | return compRoot->m_memorySsaMap[memoryKind]; |
9617 | } |
9618 | |
9619 | // The Refany type is the only struct type whose structure is implicitly assumed by IL. We need its fields. |
9620 | CORINFO_CLASS_HANDLE m_refAnyClass; |
9621 | CORINFO_FIELD_HANDLE GetRefanyDataField() |
9622 | { |
9623 | if (m_refAnyClass == nullptr) |
9624 | { |
9625 | m_refAnyClass = info.compCompHnd->getBuiltinClass(CLASSID_TYPED_BYREF); |
9626 | } |
9627 | return info.compCompHnd->getFieldInClass(m_refAnyClass, 0); |
9628 | } |
9629 | CORINFO_FIELD_HANDLE GetRefanyTypeField() |
9630 | { |
9631 | if (m_refAnyClass == nullptr) |
9632 | { |
9633 | m_refAnyClass = info.compCompHnd->getBuiltinClass(CLASSID_TYPED_BYREF); |
9634 | } |
9635 | return info.compCompHnd->getFieldInClass(m_refAnyClass, 1); |
9636 | } |
9637 | |
9638 | #if VARSET_COUNTOPS |
9639 | static BitSetSupport::BitSetOpCounter m_varsetOpCounter; |
9640 | #endif |
9641 | #if ALLVARSET_COUNTOPS |
9642 | static BitSetSupport::BitSetOpCounter m_allvarsetOpCounter; |
9643 | #endif |
9644 | |
9645 | static HelperCallProperties s_helperCallProperties; |
9646 | |
9647 | #ifdef UNIX_AMD64_ABI |
9648 | static var_types GetTypeFromClassificationAndSizes(SystemVClassificationType classType, int size); |
9649 | static var_types GetEightByteType(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc, |
9650 | unsigned slotNum); |
9651 | |
9652 | static void GetStructTypeOffset(const SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR& structDesc, |
9653 | var_types* type0, |
9654 | var_types* type1, |
9655 | unsigned __int8* offset0, |
9656 | unsigned __int8* offset1); |
9657 | |
9658 | void GetStructTypeOffset(CORINFO_CLASS_HANDLE typeHnd, |
9659 | var_types* type0, |
9660 | var_types* type1, |
9661 | unsigned __int8* offset0, |
9662 | unsigned __int8* offset1); |
9663 | |
9664 | #endif // defined(UNIX_AMD64_ABI) |
9665 | |
9666 | void fgMorphMultiregStructArgs(GenTreeCall* call); |
9667 | GenTree* fgMorphMultiregStructArg(GenTree* arg, fgArgTabEntry* fgEntryPtr); |
9668 | |
9669 | bool killGCRefs(GenTree* tree); |
9670 | |
9671 | }; // end of class Compiler |
9672 | |
9673 | //--------------------------------------------------------------------------------------------------------------------- |
9674 | // GenTreeVisitor: a flexible tree walker implemented using the curiosly-recurring-template pattern. |
9675 | // |
9676 | // This class implements a configurable walker for IR trees. There are five configuration options (defaults values are |
9677 | // shown in parentheses): |
9678 | // |
9679 | // - ComputeStack (false): when true, the walker will push each node onto the `m_ancestors` stack. "Ancestors" is a bit |
9680 | // of a misnomer, as the first entry will always be the current node. |
9681 | // |
9682 | // - DoPreOrder (false): when true, the walker will invoke `TVisitor::PreOrderVisit` with the current node as an |
9683 | // argument before visiting the node's operands. |
9684 | // |
9685 | // - DoPostOrder (false): when true, the walker will invoke `TVisitor::PostOrderVisit` with the current node as an |
9686 | // argument after visiting the node's operands. |
9687 | // |
9688 | // - DoLclVarsOnly (false): when true, the walker will only invoke `TVisitor::PreOrderVisit` for lclVar nodes. |
9689 | // `DoPreOrder` must be true if this option is true. |
9690 | // |
9691 | // - UseExecutionOrder (false): when true, then walker will visit a node's operands in execution order (e.g. if a |
9692 | // binary operator has the `GTF_REVERSE_OPS` flag set, the second operand will be |
9693 | // visited before the first). |
9694 | // |
9695 | // At least one of `DoPreOrder` and `DoPostOrder` must be specified. |
9696 | // |
9697 | // A simple pre-order visitor might look something like the following: |
9698 | // |
9699 | // class CountingVisitor final : public GenTreeVisitor<CountingVisitor> |
9700 | // { |
9701 | // public: |
9702 | // enum |
9703 | // { |
9704 | // DoPreOrder = true |
9705 | // }; |
9706 | // |
9707 | // unsigned m_count; |
9708 | // |
9709 | // CountingVisitor(Compiler* compiler) |
9710 | // : GenTreeVisitor<CountingVisitor>(compiler), m_count(0) |
9711 | // { |
9712 | // } |
9713 | // |
9714 | // Compiler::fgWalkResult PreOrderVisit(GenTree* node) |
9715 | // { |
9716 | // m_count++; |
9717 | // } |
9718 | // }; |
9719 | // |
9720 | // This visitor would then be used like so: |
9721 | // |
9722 | // CountingVisitor countingVisitor(compiler); |
9723 | // countingVisitor.WalkTree(root); |
9724 | // |
9725 | template <typename TVisitor> |
9726 | class GenTreeVisitor |
9727 | { |
9728 | protected: |
9729 | typedef Compiler::fgWalkResult fgWalkResult; |
9730 | |
9731 | enum |
9732 | { |
9733 | ComputeStack = false, |
9734 | DoPreOrder = false, |
9735 | DoPostOrder = false, |
9736 | DoLclVarsOnly = false, |
9737 | UseExecutionOrder = false, |
9738 | }; |
9739 | |
9740 | Compiler* m_compiler; |
9741 | ArrayStack<GenTree*> m_ancestors; |
9742 | |
9743 | GenTreeVisitor(Compiler* compiler) : m_compiler(compiler), m_ancestors(compiler->getAllocator(CMK_ArrayStack)) |
9744 | { |
9745 | assert(compiler != nullptr); |
9746 | |
9747 | static_assert_no_msg(TVisitor::DoPreOrder || TVisitor::DoPostOrder); |
9748 | static_assert_no_msg(!TVisitor::DoLclVarsOnly || TVisitor::DoPreOrder); |
9749 | } |
9750 | |
9751 | fgWalkResult PreOrderVisit(GenTree** use, GenTree* user) |
9752 | { |
9753 | return fgWalkResult::WALK_CONTINUE; |
9754 | } |
9755 | |
9756 | fgWalkResult PostOrderVisit(GenTree** use, GenTree* user) |
9757 | { |
9758 | return fgWalkResult::WALK_CONTINUE; |
9759 | } |
9760 | |
9761 | public: |
9762 | fgWalkResult WalkTree(GenTree** use, GenTree* user) |
9763 | { |
9764 | assert(use != nullptr); |
9765 | |
9766 | GenTree* node = *use; |
9767 | |
9768 | if (TVisitor::ComputeStack) |
9769 | { |
9770 | m_ancestors.Push(node); |
9771 | } |
9772 | |
9773 | fgWalkResult result = fgWalkResult::WALK_CONTINUE; |
9774 | if (TVisitor::DoPreOrder && !TVisitor::DoLclVarsOnly) |
9775 | { |
9776 | result = reinterpret_cast<TVisitor*>(this)->PreOrderVisit(use, user); |
9777 | if (result == fgWalkResult::WALK_ABORT) |
9778 | { |
9779 | return result; |
9780 | } |
9781 | |
9782 | node = *use; |
9783 | if ((node == nullptr) || (result == fgWalkResult::WALK_SKIP_SUBTREES)) |
9784 | { |
9785 | goto DONE; |
9786 | } |
9787 | } |
9788 | |
9789 | switch (node->OperGet()) |
9790 | { |
9791 | // Leaf lclVars |
9792 | case GT_LCL_VAR: |
9793 | case GT_LCL_FLD: |
9794 | case GT_LCL_VAR_ADDR: |
9795 | case GT_LCL_FLD_ADDR: |
9796 | if (TVisitor::DoLclVarsOnly) |
9797 | { |
9798 | result = reinterpret_cast<TVisitor*>(this)->PreOrderVisit(use, user); |
9799 | if (result == fgWalkResult::WALK_ABORT) |
9800 | { |
9801 | return result; |
9802 | } |
9803 | } |
9804 | __fallthrough; |
9805 | |
9806 | // Leaf nodes |
9807 | case GT_CATCH_ARG: |
9808 | case GT_LABEL: |
9809 | case GT_FTN_ADDR: |
9810 | case GT_RET_EXPR: |
9811 | case GT_CNS_INT: |
9812 | case GT_CNS_LNG: |
9813 | case GT_CNS_DBL: |
9814 | case GT_CNS_STR: |
9815 | case GT_MEMORYBARRIER: |
9816 | case GT_JMP: |
9817 | case GT_JCC: |
9818 | case GT_SETCC: |
9819 | case GT_NO_OP: |
9820 | case GT_START_NONGC: |
9821 | case GT_PROF_HOOK: |
9822 | #if !FEATURE_EH_FUNCLETS |
9823 | case GT_END_LFIN: |
9824 | #endif // !FEATURE_EH_FUNCLETS |
9825 | case GT_PHI_ARG: |
9826 | case GT_JMPTABLE: |
9827 | case GT_CLS_VAR: |
9828 | case GT_CLS_VAR_ADDR: |
9829 | case GT_ARGPLACE: |
9830 | case GT_PHYSREG: |
9831 | case GT_EMITNOP: |
9832 | case GT_PINVOKE_PROLOG: |
9833 | case GT_PINVOKE_EPILOG: |
9834 | case GT_IL_OFFSET: |
9835 | break; |
9836 | |
9837 | // Lclvar unary operators |
9838 | case GT_STORE_LCL_VAR: |
9839 | case GT_STORE_LCL_FLD: |
9840 | if (TVisitor::DoLclVarsOnly) |
9841 | { |
9842 | result = reinterpret_cast<TVisitor*>(this)->PreOrderVisit(use, user); |
9843 | if (result == fgWalkResult::WALK_ABORT) |
9844 | { |
9845 | return result; |
9846 | } |
9847 | } |
9848 | __fallthrough; |
9849 | |
9850 | // Standard unary operators |
9851 | case GT_NOT: |
9852 | case GT_NEG: |
9853 | case GT_BSWAP: |
9854 | case GT_BSWAP16: |
9855 | case GT_COPY: |
9856 | case GT_RELOAD: |
9857 | case GT_ARR_LENGTH: |
9858 | case GT_CAST: |
9859 | case GT_BITCAST: |
9860 | case GT_CKFINITE: |
9861 | case GT_LCLHEAP: |
9862 | case GT_ADDR: |
9863 | case GT_IND: |
9864 | case GT_OBJ: |
9865 | case GT_BLK: |
9866 | case GT_BOX: |
9867 | case GT_ALLOCOBJ: |
9868 | case GT_INIT_VAL: |
9869 | case GT_JTRUE: |
9870 | case GT_SWITCH: |
9871 | case GT_NULLCHECK: |
9872 | case GT_PUTARG_REG: |
9873 | case GT_PUTARG_STK: |
9874 | case GT_RETURNTRAP: |
9875 | case GT_NOP: |
9876 | case GT_RETURN: |
9877 | case GT_RETFILT: |
9878 | case GT_PHI: |
9879 | case GT_RUNTIMELOOKUP: |
9880 | { |
9881 | GenTreeUnOp* const unOp = node->AsUnOp(); |
9882 | if (unOp->gtOp1 != nullptr) |
9883 | { |
9884 | result = WalkTree(&unOp->gtOp1, unOp); |
9885 | if (result == fgWalkResult::WALK_ABORT) |
9886 | { |
9887 | return result; |
9888 | } |
9889 | } |
9890 | break; |
9891 | } |
9892 | |
9893 | // Special nodes |
9894 | case GT_CMPXCHG: |
9895 | { |
9896 | GenTreeCmpXchg* const cmpXchg = node->AsCmpXchg(); |
9897 | |
9898 | result = WalkTree(&cmpXchg->gtOpLocation, cmpXchg); |
9899 | if (result == fgWalkResult::WALK_ABORT) |
9900 | { |
9901 | return result; |
9902 | } |
9903 | result = WalkTree(&cmpXchg->gtOpValue, cmpXchg); |
9904 | if (result == fgWalkResult::WALK_ABORT) |
9905 | { |
9906 | return result; |
9907 | } |
9908 | result = WalkTree(&cmpXchg->gtOpComparand, cmpXchg); |
9909 | if (result == fgWalkResult::WALK_ABORT) |
9910 | { |
9911 | return result; |
9912 | } |
9913 | break; |
9914 | } |
9915 | |
9916 | case GT_ARR_BOUNDS_CHECK: |
9917 | #ifdef FEATURE_SIMD |
9918 | case GT_SIMD_CHK: |
9919 | #endif // FEATURE_SIMD |
9920 | #ifdef FEATURE_HW_INTRINSICS |
9921 | case GT_HW_INTRINSIC_CHK: |
9922 | #endif // FEATURE_HW_INTRINSICS |
9923 | { |
9924 | GenTreeBoundsChk* const boundsChk = node->AsBoundsChk(); |
9925 | |
9926 | result = WalkTree(&boundsChk->gtIndex, boundsChk); |
9927 | if (result == fgWalkResult::WALK_ABORT) |
9928 | { |
9929 | return result; |
9930 | } |
9931 | result = WalkTree(&boundsChk->gtArrLen, boundsChk); |
9932 | if (result == fgWalkResult::WALK_ABORT) |
9933 | { |
9934 | return result; |
9935 | } |
9936 | break; |
9937 | } |
9938 | |
9939 | case GT_FIELD: |
9940 | { |
9941 | GenTreeField* const field = node->AsField(); |
9942 | |
9943 | if (field->gtFldObj != nullptr) |
9944 | { |
9945 | result = WalkTree(&field->gtFldObj, field); |
9946 | if (result == fgWalkResult::WALK_ABORT) |
9947 | { |
9948 | return result; |
9949 | } |
9950 | } |
9951 | break; |
9952 | } |
9953 | |
9954 | case GT_ARR_ELEM: |
9955 | { |
9956 | GenTreeArrElem* const arrElem = node->AsArrElem(); |
9957 | |
9958 | result = WalkTree(&arrElem->gtArrObj, arrElem); |
9959 | if (result == fgWalkResult::WALK_ABORT) |
9960 | { |
9961 | return result; |
9962 | } |
9963 | |
9964 | const unsigned rank = arrElem->gtArrRank; |
9965 | for (unsigned dim = 0; dim < rank; dim++) |
9966 | { |
9967 | result = WalkTree(&arrElem->gtArrInds[dim], arrElem); |
9968 | if (result == fgWalkResult::WALK_ABORT) |
9969 | { |
9970 | return result; |
9971 | } |
9972 | } |
9973 | break; |
9974 | } |
9975 | |
9976 | case GT_ARR_OFFSET: |
9977 | { |
9978 | GenTreeArrOffs* const arrOffs = node->AsArrOffs(); |
9979 | |
9980 | result = WalkTree(&arrOffs->gtOffset, arrOffs); |
9981 | if (result == fgWalkResult::WALK_ABORT) |
9982 | { |
9983 | return result; |
9984 | } |
9985 | result = WalkTree(&arrOffs->gtIndex, arrOffs); |
9986 | if (result == fgWalkResult::WALK_ABORT) |
9987 | { |
9988 | return result; |
9989 | } |
9990 | result = WalkTree(&arrOffs->gtArrObj, arrOffs); |
9991 | if (result == fgWalkResult::WALK_ABORT) |
9992 | { |
9993 | return result; |
9994 | } |
9995 | break; |
9996 | } |
9997 | |
9998 | case GT_DYN_BLK: |
9999 | { |
10000 | GenTreeDynBlk* const dynBlock = node->AsDynBlk(); |
10001 | |
10002 | GenTree** op1Use = &dynBlock->gtOp1; |
10003 | GenTree** op2Use = &dynBlock->gtDynamicSize; |
10004 | |
10005 | if (TVisitor::UseExecutionOrder && dynBlock->gtEvalSizeFirst) |
10006 | { |
10007 | std::swap(op1Use, op2Use); |
10008 | } |
10009 | |
10010 | result = WalkTree(op1Use, dynBlock); |
10011 | if (result == fgWalkResult::WALK_ABORT) |
10012 | { |
10013 | return result; |
10014 | } |
10015 | result = WalkTree(op2Use, dynBlock); |
10016 | if (result == fgWalkResult::WALK_ABORT) |
10017 | { |
10018 | return result; |
10019 | } |
10020 | break; |
10021 | } |
10022 | |
10023 | case GT_STORE_DYN_BLK: |
10024 | { |
10025 | GenTreeDynBlk* const dynBlock = node->AsDynBlk(); |
10026 | |
10027 | GenTree** op1Use = &dynBlock->gtOp1; |
10028 | GenTree** op2Use = &dynBlock->gtOp2; |
10029 | GenTree** op3Use = &dynBlock->gtDynamicSize; |
10030 | |
10031 | if (TVisitor::UseExecutionOrder) |
10032 | { |
10033 | if (dynBlock->IsReverseOp()) |
10034 | { |
10035 | std::swap(op1Use, op2Use); |
10036 | } |
10037 | if (dynBlock->gtEvalSizeFirst) |
10038 | { |
10039 | std::swap(op3Use, op2Use); |
10040 | std::swap(op2Use, op1Use); |
10041 | } |
10042 | } |
10043 | |
10044 | result = WalkTree(op1Use, dynBlock); |
10045 | if (result == fgWalkResult::WALK_ABORT) |
10046 | { |
10047 | return result; |
10048 | } |
10049 | result = WalkTree(op2Use, dynBlock); |
10050 | if (result == fgWalkResult::WALK_ABORT) |
10051 | { |
10052 | return result; |
10053 | } |
10054 | result = WalkTree(op3Use, dynBlock); |
10055 | if (result == fgWalkResult::WALK_ABORT) |
10056 | { |
10057 | return result; |
10058 | } |
10059 | break; |
10060 | } |
10061 | |
10062 | case GT_CALL: |
10063 | { |
10064 | GenTreeCall* const call = node->AsCall(); |
10065 | |
10066 | if (call->gtCallObjp != nullptr) |
10067 | { |
10068 | result = WalkTree(&call->gtCallObjp, call); |
10069 | if (result == fgWalkResult::WALK_ABORT) |
10070 | { |
10071 | return result; |
10072 | } |
10073 | } |
10074 | |
10075 | for (GenTreeArgList* args = call->gtCallArgs; args != nullptr; args = args->Rest()) |
10076 | { |
10077 | result = WalkTree(args->pCurrent(), call); |
10078 | if (result == fgWalkResult::WALK_ABORT) |
10079 | { |
10080 | return result; |
10081 | } |
10082 | } |
10083 | |
10084 | for (GenTreeArgList* args = call->gtCallLateArgs; args != nullptr; args = args->Rest()) |
10085 | { |
10086 | result = WalkTree(args->pCurrent(), call); |
10087 | if (result == fgWalkResult::WALK_ABORT) |
10088 | { |
10089 | return result; |
10090 | } |
10091 | } |
10092 | |
10093 | if (call->gtCallType == CT_INDIRECT) |
10094 | { |
10095 | if (call->gtCallCookie != nullptr) |
10096 | { |
10097 | result = WalkTree(&call->gtCallCookie, call); |
10098 | if (result == fgWalkResult::WALK_ABORT) |
10099 | { |
10100 | return result; |
10101 | } |
10102 | } |
10103 | |
10104 | result = WalkTree(&call->gtCallAddr, call); |
10105 | if (result == fgWalkResult::WALK_ABORT) |
10106 | { |
10107 | return result; |
10108 | } |
10109 | } |
10110 | |
10111 | if (call->gtControlExpr != nullptr) |
10112 | { |
10113 | result = WalkTree(&call->gtControlExpr, call); |
10114 | if (result == fgWalkResult::WALK_ABORT) |
10115 | { |
10116 | return result; |
10117 | } |
10118 | } |
10119 | |
10120 | break; |
10121 | } |
10122 | |
10123 | // Binary nodes |
10124 | default: |
10125 | { |
10126 | assert(node->OperIsBinary()); |
10127 | |
10128 | GenTreeOp* const op = node->AsOp(); |
10129 | |
10130 | GenTree** op1Use = &op->gtOp1; |
10131 | GenTree** op2Use = &op->gtOp2; |
10132 | |
10133 | if (TVisitor::UseExecutionOrder && node->IsReverseOp()) |
10134 | { |
10135 | std::swap(op1Use, op2Use); |
10136 | } |
10137 | |
10138 | if (*op1Use != nullptr) |
10139 | { |
10140 | result = WalkTree(op1Use, op); |
10141 | if (result == fgWalkResult::WALK_ABORT) |
10142 | { |
10143 | return result; |
10144 | } |
10145 | } |
10146 | |
10147 | if (*op2Use != nullptr) |
10148 | { |
10149 | result = WalkTree(op2Use, op); |
10150 | if (result == fgWalkResult::WALK_ABORT) |
10151 | { |
10152 | return result; |
10153 | } |
10154 | } |
10155 | break; |
10156 | } |
10157 | } |
10158 | |
10159 | DONE: |
10160 | // Finally, visit the current node |
10161 | if (TVisitor::DoPostOrder) |
10162 | { |
10163 | result = reinterpret_cast<TVisitor*>(this)->PostOrderVisit(use, user); |
10164 | } |
10165 | |
10166 | if (TVisitor::ComputeStack) |
10167 | { |
10168 | m_ancestors.Pop(); |
10169 | } |
10170 | |
10171 | return result; |
10172 | } |
10173 | }; |
10174 | |
10175 | template <bool computeStack, bool doPreOrder, bool doPostOrder, bool doLclVarsOnly, bool useExecutionOrder> |
10176 | class GenericTreeWalker final |
10177 | : public GenTreeVisitor<GenericTreeWalker<computeStack, doPreOrder, doPostOrder, doLclVarsOnly, useExecutionOrder>> |
10178 | { |
10179 | public: |
10180 | enum |
10181 | { |
10182 | ComputeStack = computeStack, |
10183 | DoPreOrder = doPreOrder, |
10184 | DoPostOrder = doPostOrder, |
10185 | DoLclVarsOnly = doLclVarsOnly, |
10186 | UseExecutionOrder = useExecutionOrder, |
10187 | }; |
10188 | |
10189 | private: |
10190 | Compiler::fgWalkData* m_walkData; |
10191 | |
10192 | public: |
10193 | GenericTreeWalker(Compiler::fgWalkData* walkData) |
10194 | : GenTreeVisitor<GenericTreeWalker<computeStack, doPreOrder, doPostOrder, doLclVarsOnly, useExecutionOrder>>( |
10195 | walkData->compiler) |
10196 | , m_walkData(walkData) |
10197 | { |
10198 | assert(walkData != nullptr); |
10199 | |
10200 | if (computeStack) |
10201 | { |
10202 | walkData->parentStack = &this->m_ancestors; |
10203 | } |
10204 | } |
10205 | |
10206 | Compiler::fgWalkResult PreOrderVisit(GenTree** use, GenTree* user) |
10207 | { |
10208 | m_walkData->parent = user; |
10209 | return m_walkData->wtprVisitorFn(use, m_walkData); |
10210 | } |
10211 | |
10212 | Compiler::fgWalkResult PostOrderVisit(GenTree** use, GenTree* user) |
10213 | { |
10214 | m_walkData->parent = user; |
10215 | return m_walkData->wtpoVisitorFn(use, m_walkData); |
10216 | } |
10217 | }; |
10218 | |
10219 | /* |
10220 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
10221 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
10222 | XX XX |
10223 | XX Miscellaneous Compiler stuff XX |
10224 | XX XX |
10225 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
10226 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
10227 | */ |
10228 | |
10229 | // Values used to mark the types a stack slot is used for |
10230 | |
10231 | const unsigned TYPE_REF_INT = 0x01; // slot used as a 32-bit int |
10232 | const unsigned TYPE_REF_LNG = 0x02; // slot used as a 64-bit long |
10233 | const unsigned TYPE_REF_FLT = 0x04; // slot used as a 32-bit float |
10234 | const unsigned TYPE_REF_DBL = 0x08; // slot used as a 64-bit float |
10235 | const unsigned TYPE_REF_PTR = 0x10; // slot used as a 32-bit pointer |
10236 | const unsigned TYPE_REF_BYR = 0x20; // slot used as a byref pointer |
10237 | const unsigned TYPE_REF_STC = 0x40; // slot used as a struct |
10238 | const unsigned TYPE_REF_TYPEMASK = 0x7F; // bits that represent the type |
10239 | |
10240 | // const unsigned TYPE_REF_ADDR_TAKEN = 0x80; // slots address was taken |
10241 | |
10242 | /***************************************************************************** |
10243 | * |
10244 | * Variables to keep track of total code amounts. |
10245 | */ |
10246 | |
10247 | #if DISPLAY_SIZES |
10248 | |
10249 | extern size_t grossVMsize; |
10250 | extern size_t grossNCsize; |
10251 | extern size_t totalNCsize; |
10252 | |
10253 | extern unsigned genMethodICnt; |
10254 | extern unsigned genMethodNCnt; |
10255 | extern size_t gcHeaderISize; |
10256 | extern size_t gcPtrMapISize; |
10257 | extern size_t gcHeaderNSize; |
10258 | extern size_t gcPtrMapNSize; |
10259 | |
10260 | #endif // DISPLAY_SIZES |
10261 | |
10262 | /***************************************************************************** |
10263 | * |
10264 | * Variables to keep track of basic block counts (more data on 1 BB methods) |
10265 | */ |
10266 | |
10267 | #if COUNT_BASIC_BLOCKS |
10268 | extern Histogram bbCntTable; |
10269 | extern Histogram bbOneBBSizeTable; |
10270 | #endif |
10271 | |
10272 | /***************************************************************************** |
10273 | * |
10274 | * Used by optFindNaturalLoops to gather statistical information such as |
10275 | * - total number of natural loops |
10276 | * - number of loops with 1, 2, ... exit conditions |
10277 | * - number of loops that have an iterator (for like) |
10278 | * - number of loops that have a constant iterator |
10279 | */ |
10280 | |
10281 | #if COUNT_LOOPS |
10282 | |
10283 | extern unsigned totalLoopMethods; // counts the total number of methods that have natural loops |
10284 | extern unsigned maxLoopsPerMethod; // counts the maximum number of loops a method has |
10285 | extern unsigned totalLoopOverflows; // # of methods that identified more loops than we can represent |
10286 | extern unsigned totalLoopCount; // counts the total number of natural loops |
10287 | extern unsigned totalUnnatLoopCount; // counts the total number of (not-necessarily natural) loops |
10288 | extern unsigned totalUnnatLoopOverflows; // # of methods that identified more unnatural loops than we can represent |
10289 | extern unsigned iterLoopCount; // counts the # of loops with an iterator (for like) |
10290 | extern unsigned simpleTestLoopCount; // counts the # of loops with an iterator and a simple loop condition (iter < |
10291 | // const) |
10292 | extern unsigned constIterLoopCount; // counts the # of loops with a constant iterator (for like) |
10293 | extern bool hasMethodLoops; // flag to keep track if we already counted a method as having loops |
10294 | extern unsigned loopsThisMethod; // counts the number of loops in the current method |
10295 | extern bool loopOverflowThisMethod; // True if we exceeded the max # of loops in the method. |
10296 | extern Histogram loopCountTable; // Histogram of loop counts |
10297 | extern Histogram loopExitCountTable; // Histogram of loop exit counts |
10298 | |
10299 | #endif // COUNT_LOOPS |
10300 | |
10301 | /***************************************************************************** |
10302 | * variables to keep track of how many iterations we go in a dataflow pass |
10303 | */ |
10304 | |
10305 | #if DATAFLOW_ITER |
10306 | |
10307 | extern unsigned CSEiterCount; // counts the # of iteration for the CSE dataflow |
10308 | extern unsigned CFiterCount; // counts the # of iteration for the Const Folding dataflow |
10309 | |
10310 | #endif // DATAFLOW_ITER |
10311 | |
10312 | #if MEASURE_BLOCK_SIZE |
10313 | extern size_t genFlowNodeSize; |
10314 | extern size_t genFlowNodeCnt; |
10315 | #endif // MEASURE_BLOCK_SIZE |
10316 | |
10317 | #if MEASURE_NODE_SIZE |
10318 | struct NodeSizeStats |
10319 | { |
10320 | void Init() |
10321 | { |
10322 | genTreeNodeCnt = 0; |
10323 | genTreeNodeSize = 0; |
10324 | genTreeNodeActualSize = 0; |
10325 | } |
10326 | |
10327 | // Count of tree nodes allocated. |
10328 | unsigned __int64 genTreeNodeCnt; |
10329 | |
10330 | // The size we allocate. |
10331 | unsigned __int64 genTreeNodeSize; |
10332 | |
10333 | // The actual size of the node. Note that the actual size will likely be smaller |
10334 | // than the allocated size, but we sometimes use SetOper()/ChangeOper() to change |
10335 | // a smaller node to a larger one. TODO-Cleanup: add stats on |
10336 | // SetOper()/ChangeOper() usage to quantify this. |
10337 | unsigned __int64 genTreeNodeActualSize; |
10338 | }; |
10339 | extern NodeSizeStats genNodeSizeStats; // Total node size stats |
10340 | extern NodeSizeStats genNodeSizeStatsPerFunc; // Per-function node size stats |
10341 | extern Histogram genTreeNcntHist; |
10342 | extern Histogram genTreeNsizHist; |
10343 | #endif // MEASURE_NODE_SIZE |
10344 | |
10345 | /***************************************************************************** |
10346 | * Count fatal errors (including noway_asserts). |
10347 | */ |
10348 | |
10349 | #if MEASURE_FATAL |
10350 | extern unsigned fatal_badCode; |
10351 | extern unsigned fatal_noWay; |
10352 | extern unsigned fatal_NOMEM; |
10353 | extern unsigned fatal_noWayAssertBody; |
10354 | #ifdef DEBUG |
10355 | extern unsigned fatal_noWayAssertBodyArgs; |
10356 | #endif // DEBUG |
10357 | extern unsigned fatal_NYI; |
10358 | #endif // MEASURE_FATAL |
10359 | |
10360 | /***************************************************************************** |
10361 | * Codegen |
10362 | */ |
10363 | |
10364 | #ifdef _TARGET_XARCH_ |
10365 | |
10366 | const instruction INS_SHIFT_LEFT_LOGICAL = INS_shl; |
10367 | const instruction INS_SHIFT_RIGHT_LOGICAL = INS_shr; |
10368 | const instruction INS_SHIFT_RIGHT_ARITHM = INS_sar; |
10369 | |
10370 | const instruction INS_AND = INS_and; |
10371 | const instruction INS_OR = INS_or; |
10372 | const instruction INS_XOR = INS_xor; |
10373 | const instruction INS_NEG = INS_neg; |
10374 | const instruction INS_TEST = INS_test; |
10375 | const instruction INS_MUL = INS_imul; |
10376 | const instruction INS_SIGNED_DIVIDE = INS_idiv; |
10377 | const instruction INS_UNSIGNED_DIVIDE = INS_div; |
10378 | const instruction INS_BREAKPOINT = INS_int3; |
10379 | const instruction INS_ADDC = INS_adc; |
10380 | const instruction INS_SUBC = INS_sbb; |
10381 | const instruction INS_NOT = INS_not; |
10382 | |
10383 | #endif // _TARGET_XARCH_ |
10384 | |
10385 | #ifdef _TARGET_ARM_ |
10386 | |
10387 | const instruction INS_SHIFT_LEFT_LOGICAL = INS_lsl; |
10388 | const instruction INS_SHIFT_RIGHT_LOGICAL = INS_lsr; |
10389 | const instruction INS_SHIFT_RIGHT_ARITHM = INS_asr; |
10390 | |
10391 | const instruction INS_AND = INS_and; |
10392 | const instruction INS_OR = INS_orr; |
10393 | const instruction INS_XOR = INS_eor; |
10394 | const instruction INS_NEG = INS_rsb; |
10395 | const instruction INS_TEST = INS_tst; |
10396 | const instruction INS_MUL = INS_mul; |
10397 | const instruction INS_MULADD = INS_mla; |
10398 | const instruction INS_SIGNED_DIVIDE = INS_sdiv; |
10399 | const instruction INS_UNSIGNED_DIVIDE = INS_udiv; |
10400 | const instruction INS_BREAKPOINT = INS_bkpt; |
10401 | const instruction INS_ADDC = INS_adc; |
10402 | const instruction INS_SUBC = INS_sbc; |
10403 | const instruction INS_NOT = INS_mvn; |
10404 | |
10405 | const instruction INS_ABS = INS_vabs; |
10406 | const instruction INS_SQRT = INS_vsqrt; |
10407 | |
10408 | #endif // _TARGET_ARM_ |
10409 | |
10410 | #ifdef _TARGET_ARM64_ |
10411 | |
10412 | const instruction INS_MULADD = INS_madd; |
10413 | const instruction INS_BREAKPOINT = INS_bkpt; |
10414 | |
10415 | const instruction INS_ABS = INS_fabs; |
10416 | const instruction INS_SQRT = INS_fsqrt; |
10417 | |
10418 | #endif // _TARGET_ARM64_ |
10419 | |
10420 | /*****************************************************************************/ |
10421 | |
10422 | extern const BYTE genTypeSizes[]; |
10423 | extern const BYTE genTypeAlignments[]; |
10424 | extern const BYTE genTypeStSzs[]; |
10425 | extern const BYTE genActualTypes[]; |
10426 | |
10427 | /*****************************************************************************/ |
10428 | |
10429 | // VERY_LARGE_FRAME_SIZE_REG_MASK is the set of registers we need to use for |
10430 | // the probing loop generated for very large stack frames (see `getVeryLargeFrameSize`). |
10431 | |
10432 | #ifdef _TARGET_ARM_ |
10433 | #define VERY_LARGE_FRAME_SIZE_REG_MASK (RBM_R4 | RBM_R5 | RBM_R6) |
10434 | #elif defined(_TARGET_ARM64_) |
10435 | #define VERY_LARGE_FRAME_SIZE_REG_MASK (RBM_R9 | RBM_R10 | RBM_R11) |
10436 | #endif |
10437 | |
10438 | /*****************************************************************************/ |
10439 | |
10440 | extern BasicBlock dummyBB; |
10441 | |
10442 | /*****************************************************************************/ |
10443 | /*****************************************************************************/ |
10444 | |
10445 | // foreach_treenode_execution_order: An iterator that iterates through all the tree |
10446 | // nodes of a statement in execution order. |
10447 | // __stmt: a GT_STMT type GenTree* |
10448 | // __node: a GenTree*, already declared, that gets updated with each node in the statement, in execution order |
10449 | |
10450 | #define foreach_treenode_execution_order(__node, __stmt) \ |
10451 | for ((__node) = (__stmt)->gtStmt.gtStmtList; (__node); (__node) = (__node)->gtNext) |
10452 | |
10453 | // foreach_block: An iterator over all blocks in the function. |
10454 | // __compiler: the Compiler* object |
10455 | // __block : a BasicBlock*, already declared, that gets updated each iteration. |
10456 | |
10457 | #define foreach_block(__compiler, __block) \ |
10458 | for ((__block) = (__compiler)->fgFirstBB; (__block); (__block) = (__block)->bbNext) |
10459 | |
10460 | /*****************************************************************************/ |
10461 | /*****************************************************************************/ |
10462 | |
10463 | #ifdef DEBUG |
10464 | |
10465 | void dumpConvertedVarSet(Compiler* comp, VARSET_VALARG_TP vars); |
10466 | |
10467 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
10468 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
10469 | XX XX |
10470 | XX Debugging helpers XX |
10471 | XX XX |
10472 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
10473 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
10474 | */ |
10475 | |
10476 | /*****************************************************************************/ |
10477 | /* The following functions are intended to be called from the debugger, to dump |
10478 | * various data structures. The can be used in the debugger Watch or Quick Watch |
10479 | * windows. They are designed to be short to type and take as few arguments as |
10480 | * possible. The 'c' versions take a Compiler*, whereas the 'd' versions use the TlsCompiler. |
10481 | * See the function definition comment for more details. |
10482 | */ |
10483 | |
10484 | void cBlock(Compiler* comp, BasicBlock* block); |
10485 | void cBlocks(Compiler* comp); |
10486 | void cBlocksV(Compiler* comp); |
10487 | void cTree(Compiler* comp, GenTree* tree); |
10488 | void cTrees(Compiler* comp); |
10489 | void cEH(Compiler* comp); |
10490 | void cVar(Compiler* comp, unsigned lclNum); |
10491 | void cVarDsc(Compiler* comp, LclVarDsc* varDsc); |
10492 | void cVars(Compiler* comp); |
10493 | void cVarsFinal(Compiler* comp); |
10494 | void cBlockPreds(Compiler* comp, BasicBlock* block); |
10495 | void cReach(Compiler* comp); |
10496 | void cDoms(Compiler* comp); |
10497 | void cLiveness(Compiler* comp); |
10498 | void cCVarSet(Compiler* comp, VARSET_VALARG_TP vars); |
10499 | |
10500 | void cFuncIR(Compiler* comp); |
10501 | void cBlockIR(Compiler* comp, BasicBlock* block); |
10502 | void cLoopIR(Compiler* comp, Compiler::LoopDsc* loop); |
10503 | void cTreeIR(Compiler* comp, GenTree* tree); |
10504 | int cTreeTypeIR(Compiler* comp, GenTree* tree); |
10505 | int cTreeKindsIR(Compiler* comp, GenTree* tree); |
10506 | int cTreeFlagsIR(Compiler* comp, GenTree* tree); |
10507 | int cOperandIR(Compiler* comp, GenTree* operand); |
10508 | int cLeafIR(Compiler* comp, GenTree* tree); |
10509 | int cIndirIR(Compiler* comp, GenTree* tree); |
10510 | int cListIR(Compiler* comp, GenTree* list); |
10511 | int cSsaNumIR(Compiler* comp, GenTree* tree); |
10512 | int cValNumIR(Compiler* comp, GenTree* tree); |
10513 | int cDependsIR(Compiler* comp, GenTree* comma, bool* first); |
10514 | |
10515 | void dBlock(BasicBlock* block); |
10516 | void dBlocks(); |
10517 | void dBlocksV(); |
10518 | void dTree(GenTree* tree); |
10519 | void dTrees(); |
10520 | void dEH(); |
10521 | void dVar(unsigned lclNum); |
10522 | void dVarDsc(LclVarDsc* varDsc); |
10523 | void dVars(); |
10524 | void dVarsFinal(); |
10525 | void dBlockPreds(BasicBlock* block); |
10526 | void dReach(); |
10527 | void dDoms(); |
10528 | void dLiveness(); |
10529 | void dCVarSet(VARSET_VALARG_TP vars); |
10530 | |
10531 | void dRegMask(regMaskTP mask); |
10532 | |
10533 | void dFuncIR(); |
10534 | void dBlockIR(BasicBlock* block); |
10535 | void dTreeIR(GenTree* tree); |
10536 | void dLoopIR(Compiler::LoopDsc* loop); |
10537 | void dLoopNumIR(unsigned loopNum); |
10538 | int dTabStopIR(int curr, int tabstop); |
10539 | int dTreeTypeIR(GenTree* tree); |
10540 | int dTreeKindsIR(GenTree* tree); |
10541 | int dTreeFlagsIR(GenTree* tree); |
10542 | int dOperandIR(GenTree* operand); |
10543 | int dLeafIR(GenTree* tree); |
10544 | int dIndirIR(GenTree* tree); |
10545 | int dListIR(GenTree* list); |
10546 | int dSsaNumIR(GenTree* tree); |
10547 | int dValNumIR(GenTree* tree); |
10548 | int dDependsIR(GenTree* comma); |
10549 | void dFormatIR(); |
10550 | |
10551 | GenTree* dFindTree(GenTree* tree, unsigned id); |
10552 | GenTree* dFindTree(unsigned id); |
10553 | GenTreeStmt* dFindStmt(unsigned id); |
10554 | BasicBlock* dFindBlock(unsigned bbNum); |
10555 | |
10556 | #endif // DEBUG |
10557 | |
10558 | #include "compiler.hpp" // All the shared inline functions |
10559 | |
10560 | /*****************************************************************************/ |
10561 | #endif //_COMPILER_H_ |
10562 | /*****************************************************************************/ |
10563 | |