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 GenTree XX |
9 | XX XX |
10 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
11 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
12 | */ |
13 | |
14 | #include "jitpch.h" |
15 | #include "hwintrinsic.h" |
16 | #include "simd.h" |
17 | |
18 | #ifdef _MSC_VER |
19 | #pragma hdrstop |
20 | #endif |
21 | |
22 | /*****************************************************************************/ |
23 | |
24 | const unsigned short GenTree::gtOperKindTable[] = { |
25 | #define GTNODE(en, st, cm, ok) ok + GTK_COMMUTE *cm, |
26 | #include "gtlist.h" |
27 | }; |
28 | |
29 | /***************************************************************************** |
30 | * |
31 | * The types of different GenTree nodes |
32 | */ |
33 | |
34 | #ifdef DEBUG |
35 | |
36 | #define INDENT_SIZE 3 |
37 | |
38 | //-------------------------------------------- |
39 | // |
40 | // IndentStack: This struct is used, along with its related enums and strings, |
41 | // to control both the indendtation and the printing of arcs. |
42 | // |
43 | // Notes: |
44 | // The mode of printing is set in the Constructor, using its 'compiler' argument. |
45 | // Currently it only prints arcs when fgOrder == fgOrderLinear. |
46 | // The type of arc to print is specified by the IndentInfo enum, and is controlled |
47 | // by the caller of the Push() method. |
48 | |
49 | enum IndentChars |
50 | { |
51 | ICVertical, |
52 | ICBottom, |
53 | ICTop, |
54 | ICMiddle, |
55 | ICDash, |
56 | ICEmbedded, |
57 | ICTerminal, |
58 | ICError, |
59 | IndentCharCount |
60 | }; |
61 | |
62 | // clang-format off |
63 | // Sets of strings for different dumping options vert bot top mid dash embedded terminal error |
64 | static const char* emptyIndents[IndentCharCount] = { " " , " " , " " , " " , " " , "{" , "" , "?" }; |
65 | static const char* asciiIndents[IndentCharCount] = { "|" , "\\" , "/" , "+" , "-" , "{" , "*" , "?" }; |
66 | static const char* unicodeIndents[IndentCharCount] = { "\xe2\x94\x82" , "\xe2\x94\x94" , "\xe2\x94\x8c" , "\xe2\x94\x9c" , "\xe2\x94\x80" , "{" , "\xe2\x96\x8c" , "?" }; |
67 | // clang-format on |
68 | |
69 | typedef ArrayStack<Compiler::IndentInfo> IndentInfoStack; |
70 | struct IndentStack |
71 | { |
72 | IndentInfoStack stack; |
73 | const char** indents; |
74 | |
75 | // Constructor for IndentStack. Uses 'compiler' to determine the mode of printing. |
76 | IndentStack(Compiler* compiler) : stack(compiler->getAllocator(CMK_DebugOnly)) |
77 | { |
78 | if (compiler->asciiTrees) |
79 | { |
80 | indents = asciiIndents; |
81 | } |
82 | else |
83 | { |
84 | indents = unicodeIndents; |
85 | } |
86 | } |
87 | |
88 | // Return the depth of the current indentation. |
89 | unsigned Depth() |
90 | { |
91 | return stack.Height(); |
92 | } |
93 | |
94 | // Push a new indentation onto the stack, of the given type. |
95 | void Push(Compiler::IndentInfo info) |
96 | { |
97 | stack.Push(info); |
98 | } |
99 | |
100 | // Pop the most recent indentation type off the stack. |
101 | Compiler::IndentInfo Pop() |
102 | { |
103 | return stack.Pop(); |
104 | } |
105 | |
106 | // Print the current indentation and arcs. |
107 | void print() |
108 | { |
109 | unsigned indentCount = Depth(); |
110 | for (unsigned i = 0; i < indentCount; i++) |
111 | { |
112 | unsigned index = indentCount - 1 - i; |
113 | switch (stack.Index(index)) |
114 | { |
115 | case Compiler::IndentInfo::IINone: |
116 | printf(" " ); |
117 | break; |
118 | case Compiler::IndentInfo::IIEmbedded: |
119 | printf("%s " , indents[ICEmbedded]); |
120 | break; |
121 | case Compiler::IndentInfo::IIArc: |
122 | if (index == 0) |
123 | { |
124 | printf("%s%s%s" , indents[ICMiddle], indents[ICDash], indents[ICDash]); |
125 | } |
126 | else |
127 | { |
128 | printf("%s " , indents[ICVertical]); |
129 | } |
130 | break; |
131 | case Compiler::IndentInfo::IIArcBottom: |
132 | printf("%s%s%s" , indents[ICBottom], indents[ICDash], indents[ICDash]); |
133 | break; |
134 | case Compiler::IndentInfo::IIArcTop: |
135 | printf("%s%s%s" , indents[ICTop], indents[ICDash], indents[ICDash]); |
136 | break; |
137 | case Compiler::IndentInfo::IIError: |
138 | printf("%s%s%s" , indents[ICError], indents[ICDash], indents[ICDash]); |
139 | break; |
140 | default: |
141 | unreached(); |
142 | } |
143 | } |
144 | printf("%s" , indents[ICTerminal]); |
145 | } |
146 | }; |
147 | |
148 | //------------------------------------------------------------------------ |
149 | // printIndent: This is a static method which simply invokes the 'print' |
150 | // method on its 'indentStack' argument. |
151 | // |
152 | // Arguments: |
153 | // indentStack - specifies the information for the indentation & arcs to be printed |
154 | // |
155 | // Notes: |
156 | // This method exists to localize the checking for the case where indentStack is null. |
157 | |
158 | static void printIndent(IndentStack* indentStack) |
159 | { |
160 | if (indentStack == nullptr) |
161 | { |
162 | return; |
163 | } |
164 | indentStack->print(); |
165 | } |
166 | |
167 | #endif |
168 | |
169 | #if defined(DEBUG) || NODEBASH_STATS || MEASURE_NODE_SIZE || COUNT_AST_OPERS |
170 | |
171 | static const char* opNames[] = { |
172 | #define GTNODE(en, st, cm, ok) #en, |
173 | #include "gtlist.h" |
174 | }; |
175 | |
176 | const char* GenTree::OpName(genTreeOps op) |
177 | { |
178 | assert((unsigned)op < _countof(opNames)); |
179 | |
180 | return opNames[op]; |
181 | } |
182 | |
183 | #endif |
184 | |
185 | #if MEASURE_NODE_SIZE && SMALL_TREE_NODES |
186 | |
187 | static const char* opStructNames[] = { |
188 | #define GTNODE(en, st, cm, ok) #st, |
189 | #include "gtlist.h" |
190 | }; |
191 | |
192 | const char* GenTree::OpStructName(genTreeOps op) |
193 | { |
194 | assert((unsigned)op < _countof(opStructNames)); |
195 | |
196 | return opStructNames[op]; |
197 | } |
198 | |
199 | #endif |
200 | |
201 | /***************************************************************************** |
202 | * |
203 | * When 'SMALL_TREE_NODES' is enabled, we allocate tree nodes in 2 different |
204 | * sizes: 'TREE_NODE_SZ_SMALL' for most nodes and 'TREE_NODE_SZ_LARGE' for the |
205 | * few nodes (such as calls) that have more fields and take up a lot more space. |
206 | */ |
207 | |
208 | #if SMALL_TREE_NODES |
209 | |
210 | /* GT_COUNT'th oper is overloaded as 'undefined oper', so allocate storage for GT_COUNT'th oper also */ |
211 | /* static */ |
212 | unsigned char GenTree::s_gtNodeSizes[GT_COUNT + 1]; |
213 | |
214 | #if NODEBASH_STATS || MEASURE_NODE_SIZE || COUNT_AST_OPERS |
215 | |
216 | unsigned char GenTree::s_gtTrueSizes[GT_COUNT + 1]{ |
217 | #define GTNODE(en, st, cm, ok) sizeof(st), |
218 | #include "gtlist.h" |
219 | }; |
220 | |
221 | #endif // NODEBASH_STATS || MEASURE_NODE_SIZE || COUNT_AST_OPERS |
222 | |
223 | #if COUNT_AST_OPERS |
224 | LONG GenTree::s_gtNodeCounts[GT_COUNT + 1] = {0}; |
225 | #endif // COUNT_AST_OPERS |
226 | |
227 | /* static */ |
228 | void GenTree::InitNodeSize() |
229 | { |
230 | /* Set all sizes to 'small' first */ |
231 | |
232 | for (unsigned op = 0; op <= GT_COUNT; op++) |
233 | { |
234 | GenTree::s_gtNodeSizes[op] = TREE_NODE_SZ_SMALL; |
235 | } |
236 | |
237 | // Now set all of the appropriate entries to 'large' |
238 | CLANG_FORMAT_COMMENT_ANCHOR; |
239 | |
240 | // clang-format off |
241 | #if defined(FEATURE_HFA) || defined(UNIX_AMD64_ABI) |
242 | // On ARM32, ARM64 and System V for struct returning |
243 | // there is code that does GT_ASG-tree.CopyObj call. |
244 | // CopyObj is a large node and the GT_ASG is small, which triggers an exception. |
245 | GenTree::s_gtNodeSizes[GT_ASG] = TREE_NODE_SZ_LARGE; |
246 | GenTree::s_gtNodeSizes[GT_RETURN] = TREE_NODE_SZ_LARGE; |
247 | #endif // defined(FEATURE_HFA) || defined(UNIX_AMD64_ABI) |
248 | |
249 | GenTree::s_gtNodeSizes[GT_CALL] = TREE_NODE_SZ_LARGE; |
250 | GenTree::s_gtNodeSizes[GT_CAST] = TREE_NODE_SZ_LARGE; |
251 | GenTree::s_gtNodeSizes[GT_FTN_ADDR] = TREE_NODE_SZ_LARGE; |
252 | GenTree::s_gtNodeSizes[GT_BOX] = TREE_NODE_SZ_LARGE; |
253 | GenTree::s_gtNodeSizes[GT_INDEX] = TREE_NODE_SZ_LARGE; |
254 | GenTree::s_gtNodeSizes[GT_INDEX_ADDR] = TREE_NODE_SZ_LARGE; |
255 | GenTree::s_gtNodeSizes[GT_ARR_BOUNDS_CHECK] = TREE_NODE_SZ_LARGE; |
256 | #ifdef FEATURE_SIMD |
257 | GenTree::s_gtNodeSizes[GT_SIMD_CHK] = TREE_NODE_SZ_LARGE; |
258 | #endif // FEATURE_SIMD |
259 | #ifdef FEATURE_HW_INTRINSICS |
260 | GenTree::s_gtNodeSizes[GT_HW_INTRINSIC_CHK] = TREE_NODE_SZ_LARGE; |
261 | #endif // FEATURE_HW_INTRINSICS |
262 | |
263 | GenTree::s_gtNodeSizes[GT_ARR_ELEM] = TREE_NODE_SZ_LARGE; |
264 | GenTree::s_gtNodeSizes[GT_ARR_INDEX] = TREE_NODE_SZ_LARGE; |
265 | GenTree::s_gtNodeSizes[GT_ARR_OFFSET] = TREE_NODE_SZ_LARGE; |
266 | GenTree::s_gtNodeSizes[GT_RET_EXPR] = TREE_NODE_SZ_LARGE; |
267 | GenTree::s_gtNodeSizes[GT_OBJ] = TREE_NODE_SZ_LARGE; |
268 | GenTree::s_gtNodeSizes[GT_FIELD] = TREE_NODE_SZ_LARGE; |
269 | GenTree::s_gtNodeSizes[GT_STMT] = TREE_NODE_SZ_LARGE; |
270 | GenTree::s_gtNodeSizes[GT_CMPXCHG] = TREE_NODE_SZ_LARGE; |
271 | GenTree::s_gtNodeSizes[GT_QMARK] = TREE_NODE_SZ_LARGE; |
272 | GenTree::s_gtNodeSizes[GT_LEA] = TREE_NODE_SZ_LARGE; |
273 | GenTree::s_gtNodeSizes[GT_STORE_OBJ] = TREE_NODE_SZ_LARGE; |
274 | GenTree::s_gtNodeSizes[GT_DYN_BLK] = TREE_NODE_SZ_LARGE; |
275 | GenTree::s_gtNodeSizes[GT_STORE_DYN_BLK] = TREE_NODE_SZ_LARGE; |
276 | GenTree::s_gtNodeSizes[GT_INTRINSIC] = TREE_NODE_SZ_LARGE; |
277 | GenTree::s_gtNodeSizes[GT_ALLOCOBJ] = TREE_NODE_SZ_LARGE; |
278 | #if USE_HELPERS_FOR_INT_DIV |
279 | GenTree::s_gtNodeSizes[GT_DIV] = TREE_NODE_SZ_LARGE; |
280 | GenTree::s_gtNodeSizes[GT_UDIV] = TREE_NODE_SZ_LARGE; |
281 | GenTree::s_gtNodeSizes[GT_MOD] = TREE_NODE_SZ_LARGE; |
282 | GenTree::s_gtNodeSizes[GT_UMOD] = TREE_NODE_SZ_LARGE; |
283 | #endif |
284 | #ifdef FEATURE_PUT_STRUCT_ARG_STK |
285 | // TODO-Throughput: This should not need to be a large node. The object info should be |
286 | // obtained from the child node. |
287 | GenTree::s_gtNodeSizes[GT_PUTARG_STK] = TREE_NODE_SZ_LARGE; |
288 | #if FEATURE_ARG_SPLIT |
289 | GenTree::s_gtNodeSizes[GT_PUTARG_SPLIT] = TREE_NODE_SZ_LARGE; |
290 | #endif // FEATURE_ARG_SPLIT |
291 | #endif // FEATURE_PUT_STRUCT_ARG_STK |
292 | |
293 | assert(GenTree::s_gtNodeSizes[GT_RETURN] == GenTree::s_gtNodeSizes[GT_ASG]); |
294 | |
295 | // This list of assertions should come to contain all GenTree subtypes that are declared |
296 | // "small". |
297 | assert(sizeof(GenTreeLclFld) <= GenTree::s_gtNodeSizes[GT_LCL_FLD]); |
298 | assert(sizeof(GenTreeLclVar) <= GenTree::s_gtNodeSizes[GT_LCL_VAR]); |
299 | |
300 | static_assert_no_msg(sizeof(GenTree) <= TREE_NODE_SZ_SMALL); |
301 | static_assert_no_msg(sizeof(GenTreeUnOp) <= TREE_NODE_SZ_SMALL); |
302 | static_assert_no_msg(sizeof(GenTreeOp) <= TREE_NODE_SZ_SMALL); |
303 | static_assert_no_msg(sizeof(GenTreeVal) <= TREE_NODE_SZ_SMALL); |
304 | static_assert_no_msg(sizeof(GenTreeIntConCommon) <= TREE_NODE_SZ_SMALL); |
305 | static_assert_no_msg(sizeof(GenTreePhysReg) <= TREE_NODE_SZ_SMALL); |
306 | static_assert_no_msg(sizeof(GenTreeJumpTable) <= TREE_NODE_SZ_SMALL); |
307 | static_assert_no_msg(sizeof(GenTreeIntCon) <= TREE_NODE_SZ_SMALL); |
308 | static_assert_no_msg(sizeof(GenTreeLngCon) <= TREE_NODE_SZ_SMALL); |
309 | static_assert_no_msg(sizeof(GenTreeDblCon) <= TREE_NODE_SZ_SMALL); |
310 | static_assert_no_msg(sizeof(GenTreeStrCon) <= TREE_NODE_SZ_SMALL); |
311 | static_assert_no_msg(sizeof(GenTreeLclVarCommon) <= TREE_NODE_SZ_SMALL); |
312 | static_assert_no_msg(sizeof(GenTreeLclVar) <= TREE_NODE_SZ_SMALL); |
313 | static_assert_no_msg(sizeof(GenTreeLclFld) <= TREE_NODE_SZ_SMALL); |
314 | static_assert_no_msg(sizeof(GenTreeCC) <= TREE_NODE_SZ_SMALL); |
315 | static_assert_no_msg(sizeof(GenTreeCast) <= TREE_NODE_SZ_LARGE); // *** large node |
316 | static_assert_no_msg(sizeof(GenTreeBox) <= TREE_NODE_SZ_LARGE); // *** large node |
317 | static_assert_no_msg(sizeof(GenTreeField) <= TREE_NODE_SZ_LARGE); // *** large node |
318 | static_assert_no_msg(sizeof(GenTreeArgList) <= TREE_NODE_SZ_SMALL); |
319 | static_assert_no_msg(sizeof(GenTreeFieldList) <= TREE_NODE_SZ_SMALL); |
320 | static_assert_no_msg(sizeof(GenTreeColon) <= TREE_NODE_SZ_SMALL); |
321 | static_assert_no_msg(sizeof(GenTreeCall) <= TREE_NODE_SZ_LARGE); // *** large node |
322 | static_assert_no_msg(sizeof(GenTreeCmpXchg) <= TREE_NODE_SZ_LARGE); // *** large node |
323 | static_assert_no_msg(sizeof(GenTreeFptrVal) <= TREE_NODE_SZ_LARGE); // *** large node |
324 | static_assert_no_msg(sizeof(GenTreeQmark) <= TREE_NODE_SZ_LARGE); // *** large node |
325 | static_assert_no_msg(sizeof(GenTreeIntrinsic) <= TREE_NODE_SZ_LARGE); // *** large node |
326 | static_assert_no_msg(sizeof(GenTreeIndex) <= TREE_NODE_SZ_LARGE); // *** large node |
327 | static_assert_no_msg(sizeof(GenTreeArrLen) <= TREE_NODE_SZ_LARGE); // *** large node |
328 | static_assert_no_msg(sizeof(GenTreeBoundsChk) <= TREE_NODE_SZ_LARGE); // *** large node |
329 | static_assert_no_msg(sizeof(GenTreeArrElem) <= TREE_NODE_SZ_LARGE); // *** large node |
330 | static_assert_no_msg(sizeof(GenTreeArrIndex) <= TREE_NODE_SZ_LARGE); // *** large node |
331 | static_assert_no_msg(sizeof(GenTreeArrOffs) <= TREE_NODE_SZ_LARGE); // *** large node |
332 | static_assert_no_msg(sizeof(GenTreeIndir) <= TREE_NODE_SZ_SMALL); |
333 | static_assert_no_msg(sizeof(GenTreeStoreInd) <= TREE_NODE_SZ_SMALL); |
334 | static_assert_no_msg(sizeof(GenTreeAddrMode) <= TREE_NODE_SZ_SMALL); |
335 | static_assert_no_msg(sizeof(GenTreeObj) <= TREE_NODE_SZ_LARGE); // *** large node |
336 | static_assert_no_msg(sizeof(GenTreeBlk) <= TREE_NODE_SZ_SMALL); |
337 | static_assert_no_msg(sizeof(GenTreeRetExpr) <= TREE_NODE_SZ_LARGE); // *** large node |
338 | static_assert_no_msg(sizeof(GenTreeStmt) <= TREE_NODE_SZ_LARGE); // *** large node |
339 | static_assert_no_msg(sizeof(GenTreeClsVar) <= TREE_NODE_SZ_SMALL); |
340 | static_assert_no_msg(sizeof(GenTreeArgPlace) <= TREE_NODE_SZ_SMALL); |
341 | static_assert_no_msg(sizeof(GenTreeLabel) <= TREE_NODE_SZ_SMALL); |
342 | static_assert_no_msg(sizeof(GenTreePhiArg) <= TREE_NODE_SZ_SMALL); |
343 | static_assert_no_msg(sizeof(GenTreeAllocObj) <= TREE_NODE_SZ_LARGE); // *** large node |
344 | #ifndef FEATURE_PUT_STRUCT_ARG_STK |
345 | static_assert_no_msg(sizeof(GenTreePutArgStk) <= TREE_NODE_SZ_SMALL); |
346 | #else // FEATURE_PUT_STRUCT_ARG_STK |
347 | // TODO-Throughput: This should not need to be a large node. The object info should be |
348 | // obtained from the child node. |
349 | static_assert_no_msg(sizeof(GenTreePutArgStk) <= TREE_NODE_SZ_LARGE); |
350 | #if FEATURE_ARG_SPLIT |
351 | static_assert_no_msg(sizeof(GenTreePutArgSplit) <= TREE_NODE_SZ_LARGE); |
352 | #endif // FEATURE_ARG_SPLIT |
353 | #endif // FEATURE_PUT_STRUCT_ARG_STK |
354 | |
355 | #ifdef FEATURE_SIMD |
356 | static_assert_no_msg(sizeof(GenTreeSIMD) <= TREE_NODE_SZ_SMALL); |
357 | #endif // FEATURE_SIMD |
358 | |
359 | #ifdef FEATURE_HW_INTRINSICS |
360 | static_assert_no_msg(sizeof(GenTreeHWIntrinsic) <= TREE_NODE_SZ_SMALL); |
361 | #endif // FEATURE_HW_INTRINSICS |
362 | // clang-format on |
363 | } |
364 | |
365 | size_t GenTree::GetNodeSize() const |
366 | { |
367 | return GenTree::s_gtNodeSizes[gtOper]; |
368 | } |
369 | |
370 | #ifdef DEBUG |
371 | bool GenTree::IsNodeProperlySized() const |
372 | { |
373 | size_t size; |
374 | |
375 | if (gtDebugFlags & GTF_DEBUG_NODE_SMALL) |
376 | { |
377 | size = TREE_NODE_SZ_SMALL; |
378 | } |
379 | else |
380 | { |
381 | assert(gtDebugFlags & GTF_DEBUG_NODE_LARGE); |
382 | size = TREE_NODE_SZ_LARGE; |
383 | } |
384 | |
385 | return GenTree::s_gtNodeSizes[gtOper] <= size; |
386 | } |
387 | #endif |
388 | |
389 | #if SMALL_TREE_NODES |
390 | //------------------------------------------------------------------------ |
391 | // ReplaceWith: replace this with the src node. The source must be an isolated node |
392 | // and cannot be used after the replacement. |
393 | // |
394 | // Arguments: |
395 | // src - source tree, that replaces this. |
396 | // comp - the compiler instance to transfer annotations for arrays. |
397 | // |
398 | void GenTree::ReplaceWith(GenTree* src, Compiler* comp) |
399 | { |
400 | // The source may be big only if the target is also a big node |
401 | assert((gtDebugFlags & GTF_DEBUG_NODE_LARGE) || GenTree::s_gtNodeSizes[src->gtOper] == TREE_NODE_SZ_SMALL); |
402 | |
403 | // The check is effective only if nodes have been already threaded. |
404 | assert((src->gtPrev == nullptr) && (src->gtNext == nullptr)); |
405 | |
406 | RecordOperBashing(OperGet(), src->OperGet()); // nop unless NODEBASH_STATS is enabled |
407 | |
408 | GenTree* prev = gtPrev; |
409 | GenTree* next = gtNext; |
410 | // The VTable pointer is copied intentionally here |
411 | memcpy((void*)this, (void*)src, src->GetNodeSize()); |
412 | this->gtPrev = prev; |
413 | this->gtNext = next; |
414 | |
415 | #ifdef DEBUG |
416 | gtSeqNum = 0; |
417 | #endif |
418 | // Transfer any annotations. |
419 | if (src->OperGet() == GT_IND && src->gtFlags & GTF_IND_ARR_INDEX) |
420 | { |
421 | ArrayInfo arrInfo; |
422 | bool b = comp->GetArrayInfoMap()->Lookup(src, &arrInfo); |
423 | assert(b); |
424 | comp->GetArrayInfoMap()->Set(this, arrInfo); |
425 | } |
426 | DEBUG_DESTROY_NODE(src); |
427 | } |
428 | |
429 | #endif |
430 | |
431 | /***************************************************************************** |
432 | * |
433 | * When 'NODEBASH_STATS' is enabled in "jit.h" we record all instances of |
434 | * an existing GenTree node having its operator changed. This can be useful |
435 | * for two (related) things - to see what is being bashed (and what isn't), |
436 | * and to verify that the existing choices for what nodes are marked 'large' |
437 | * are reasonable (to minimize "wasted" space). |
438 | * |
439 | * And yes, the hash function / logic is simplistic, but it is conflict-free |
440 | * and transparent for what we need. |
441 | */ |
442 | |
443 | #if NODEBASH_STATS |
444 | |
445 | #define BASH_HASH_SIZE 211 |
446 | |
447 | inline unsigned hashme(genTreeOps op1, genTreeOps op2) |
448 | { |
449 | return ((op1 * 104729) ^ (op2 * 56569)) % BASH_HASH_SIZE; |
450 | } |
451 | |
452 | struct BashHashDsc |
453 | { |
454 | unsigned __int32 bhFullHash; // the hash value (unique for all old->new pairs) |
455 | unsigned __int32 bhCount; // the same old->new bashings seen so far |
456 | unsigned __int8 bhOperOld; // original gtOper |
457 | unsigned __int8 bhOperNew; // new gtOper |
458 | }; |
459 | |
460 | static BashHashDsc BashHash[BASH_HASH_SIZE]; |
461 | |
462 | void GenTree::RecordOperBashing(genTreeOps operOld, genTreeOps operNew) |
463 | { |
464 | unsigned hash = hashme(operOld, operNew); |
465 | BashHashDsc* desc = BashHash + hash; |
466 | |
467 | if (desc->bhFullHash != hash) |
468 | { |
469 | noway_assert(desc->bhCount == 0); // if this ever fires, need fix the hash fn |
470 | desc->bhFullHash = hash; |
471 | } |
472 | |
473 | desc->bhCount += 1; |
474 | desc->bhOperOld = operOld; |
475 | desc->bhOperNew = operNew; |
476 | } |
477 | |
478 | void GenTree::ReportOperBashing(FILE* f) |
479 | { |
480 | unsigned total = 0; |
481 | |
482 | fflush(f); |
483 | |
484 | fprintf(f, "\n" ); |
485 | fprintf(f, "Bashed gtOper stats:\n" ); |
486 | fprintf(f, "\n" ); |
487 | fprintf(f, " Old operator New operator #bytes old->new Count\n" ); |
488 | fprintf(f, " ---------------------------------------------------------------\n" ); |
489 | |
490 | for (unsigned h = 0; h < BASH_HASH_SIZE; h++) |
491 | { |
492 | unsigned count = BashHash[h].bhCount; |
493 | if (count == 0) |
494 | continue; |
495 | |
496 | unsigned opOld = BashHash[h].bhOperOld; |
497 | unsigned opNew = BashHash[h].bhOperNew; |
498 | |
499 | fprintf(f, " GT_%-13s -> GT_%-13s [size: %3u->%3u] %c %7u\n" , OpName((genTreeOps)opOld), |
500 | OpName((genTreeOps)opNew), s_gtTrueSizes[opOld], s_gtTrueSizes[opNew], |
501 | (s_gtTrueSizes[opOld] < s_gtTrueSizes[opNew]) ? 'X' : ' ', count); |
502 | total += count; |
503 | } |
504 | fprintf(f, "\n" ); |
505 | fprintf(f, "Total bashings: %u\n" , total); |
506 | fprintf(f, "\n" ); |
507 | |
508 | fflush(f); |
509 | } |
510 | |
511 | #endif // NODEBASH_STATS |
512 | |
513 | #else // SMALL_TREE_NODES |
514 | |
515 | #ifdef DEBUG |
516 | bool GenTree::IsNodeProperlySized() const |
517 | { |
518 | return true; |
519 | } |
520 | #endif |
521 | |
522 | #endif // SMALL_TREE_NODES |
523 | |
524 | /*****************************************************************************/ |
525 | |
526 | #if MEASURE_NODE_SIZE |
527 | |
528 | void GenTree::DumpNodeSizes(FILE* fp) |
529 | { |
530 | // Dump the sizes of the various GenTree flavors |
531 | |
532 | #if SMALL_TREE_NODES |
533 | fprintf(fp, "Small tree node size = %3u bytes\n" , TREE_NODE_SZ_SMALL); |
534 | #endif |
535 | fprintf(fp, "Large tree node size = %3u bytes\n" , TREE_NODE_SZ_LARGE); |
536 | fprintf(fp, "\n" ); |
537 | |
538 | #if SMALL_TREE_NODES |
539 | |
540 | // Verify that node sizes are set kosherly and dump sizes |
541 | for (unsigned op = GT_NONE + 1; op < GT_COUNT; op++) |
542 | { |
543 | unsigned needSize = s_gtTrueSizes[op]; |
544 | unsigned nodeSize = s_gtNodeSizes[op]; |
545 | |
546 | const char* structNm = OpStructName((genTreeOps)op); |
547 | const char* operName = OpName((genTreeOps)op); |
548 | |
549 | bool repeated = false; |
550 | |
551 | // Have we seen this struct flavor before? |
552 | for (unsigned mop = GT_NONE + 1; mop < op; mop++) |
553 | { |
554 | if (strcmp(structNm, OpStructName((genTreeOps)mop)) == 0) |
555 | { |
556 | repeated = true; |
557 | break; |
558 | } |
559 | } |
560 | |
561 | // Don't repeat the same GenTree flavor unless we have an error |
562 | if (!repeated || needSize > nodeSize) |
563 | { |
564 | unsigned sizeChar = '?'; |
565 | |
566 | if (nodeSize == TREE_NODE_SZ_SMALL) |
567 | sizeChar = 'S'; |
568 | else if (nodeSize == TREE_NODE_SZ_LARGE) |
569 | sizeChar = 'L'; |
570 | |
571 | fprintf(fp, "GT_%-16s ... %-19s = %3u bytes (%c)" , operName, structNm, needSize, sizeChar); |
572 | if (needSize > nodeSize) |
573 | { |
574 | fprintf(fp, " -- ERROR -- allocation is only %u bytes!" , nodeSize); |
575 | } |
576 | else if (needSize <= TREE_NODE_SZ_SMALL && nodeSize == TREE_NODE_SZ_LARGE) |
577 | { |
578 | fprintf(fp, " ... could be small" ); |
579 | } |
580 | |
581 | fprintf(fp, "\n" ); |
582 | } |
583 | } |
584 | |
585 | #endif |
586 | } |
587 | |
588 | #endif // MEASURE_NODE_SIZE |
589 | |
590 | /***************************************************************************** |
591 | * |
592 | * Walk all basic blocks and call the given function pointer for all tree |
593 | * nodes contained therein. |
594 | */ |
595 | |
596 | void Compiler::fgWalkAllTreesPre(fgWalkPreFn* visitor, void* pCallBackData) |
597 | { |
598 | BasicBlock* block; |
599 | |
600 | for (block = fgFirstBB; block; block = block->bbNext) |
601 | { |
602 | GenTree* tree; |
603 | |
604 | for (tree = block->bbTreeList; tree; tree = tree->gtNext) |
605 | { |
606 | assert(tree->gtOper == GT_STMT); |
607 | |
608 | fgWalkTreePre(&tree->gtStmt.gtStmtExpr, visitor, pCallBackData); |
609 | } |
610 | } |
611 | } |
612 | |
613 | //----------------------------------------------------------- |
614 | // CopyReg: Copy the _gtRegNum/gtRegTag fields. |
615 | // |
616 | // Arguments: |
617 | // from - GenTree node from which to copy |
618 | // |
619 | // Return Value: |
620 | // None |
621 | void GenTree::CopyReg(GenTree* from) |
622 | { |
623 | _gtRegNum = from->_gtRegNum; |
624 | INDEBUG(gtRegTag = from->gtRegTag;) |
625 | |
626 | // Also copy multi-reg state if this is a call node |
627 | if (IsCall()) |
628 | { |
629 | assert(from->IsCall()); |
630 | this->AsCall()->CopyOtherRegs(from->AsCall()); |
631 | } |
632 | else if (IsCopyOrReload()) |
633 | { |
634 | this->AsCopyOrReload()->CopyOtherRegs(from->AsCopyOrReload()); |
635 | } |
636 | } |
637 | |
638 | //------------------------------------------------------------------ |
639 | // gtHasReg: Whether node beeen assigned a register by LSRA |
640 | // |
641 | // Arguments: |
642 | // None |
643 | // |
644 | // Return Value: |
645 | // Returns true if the node was assigned a register. |
646 | // |
647 | // In case of multi-reg call nodes, it is considered |
648 | // having a reg if regs are allocated for all its |
649 | // return values. |
650 | // |
651 | // In case of GT_COPY or GT_RELOAD of a multi-reg call, |
652 | // GT_COPY/GT_RELOAD is considered having a reg if it |
653 | // has a reg assigned to any of its positions. |
654 | // |
655 | // Assumption: |
656 | // In order for this to work properly, gtClearReg must be called |
657 | // prior to setting the register value. |
658 | // |
659 | bool GenTree::gtHasReg() const |
660 | { |
661 | bool hasReg; |
662 | |
663 | if (IsMultiRegCall()) |
664 | { |
665 | // Have to cast away const-ness because GetReturnTypeDesc() is a non-const method |
666 | GenTree* tree = const_cast<GenTree*>(this); |
667 | GenTreeCall* call = tree->AsCall(); |
668 | unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); |
669 | hasReg = false; |
670 | |
671 | // A Multi-reg call node is said to have regs, if it has |
672 | // reg assigned to each of its result registers. |
673 | for (unsigned i = 0; i < regCount; ++i) |
674 | { |
675 | hasReg = (call->GetRegNumByIdx(i) != REG_NA); |
676 | if (!hasReg) |
677 | { |
678 | break; |
679 | } |
680 | } |
681 | } |
682 | else if (IsCopyOrReloadOfMultiRegCall()) |
683 | { |
684 | GenTree* tree = const_cast<GenTree*>(this); |
685 | GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload(); |
686 | GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall(); |
687 | unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); |
688 | hasReg = false; |
689 | |
690 | // A Multi-reg copy or reload node is said to have regs, |
691 | // if it has valid regs in any of the positions. |
692 | for (unsigned i = 0; i < regCount; ++i) |
693 | { |
694 | hasReg = (copyOrReload->GetRegNumByIdx(i) != REG_NA); |
695 | if (hasReg) |
696 | { |
697 | break; |
698 | } |
699 | } |
700 | } |
701 | else |
702 | { |
703 | hasReg = (gtRegNum != REG_NA); |
704 | } |
705 | |
706 | return hasReg; |
707 | } |
708 | |
709 | //----------------------------------------------------------------------------- |
710 | // GetRegisterDstCount: Get the number of registers defined by the node. |
711 | // |
712 | // Arguments: |
713 | // None |
714 | // |
715 | // Return Value: |
716 | // The number of registers that this node defines. |
717 | // |
718 | // Notes: |
719 | // This should not be called on a contained node. |
720 | // This does not look at the actual register assignments, if any, and so |
721 | // is valid after Lowering. |
722 | // |
723 | int GenTree::GetRegisterDstCount() const |
724 | { |
725 | assert(!isContained()); |
726 | if (!IsMultiRegNode()) |
727 | { |
728 | return (IsValue()) ? 1 : 0; |
729 | } |
730 | else if (IsMultiRegCall()) |
731 | { |
732 | // temporarily cast away const-ness as AsCall() method is not declared const |
733 | GenTree* temp = const_cast<GenTree*>(this); |
734 | return temp->AsCall()->GetReturnTypeDesc()->GetReturnRegCount(); |
735 | } |
736 | else if (IsCopyOrReload()) |
737 | { |
738 | return gtGetOp1()->GetRegisterDstCount(); |
739 | } |
740 | #if FEATURE_ARG_SPLIT |
741 | else if (OperIsPutArgSplit()) |
742 | { |
743 | return (const_cast<GenTree*>(this))->AsPutArgSplit()->gtNumRegs; |
744 | } |
745 | #endif |
746 | #if !defined(_TARGET_64BIT_) |
747 | else if (OperIsMultiRegOp()) |
748 | { |
749 | // A MultiRegOp is a GT_MUL_LONG, GT_PUTARG_REG, or GT_BITCAST. |
750 | // For the latter two (ARM-only), they only have multiple registers if they produce a long value |
751 | // (GT_MUL_LONG always produces a long value). |
752 | CLANG_FORMAT_COMMENT_ANCHOR; |
753 | #ifdef _TARGET_ARM_ |
754 | return (TypeGet() == TYP_LONG) ? 2 : 1; |
755 | #else |
756 | assert(OperIs(GT_MUL_LONG)); |
757 | return 2; |
758 | #endif |
759 | } |
760 | #endif |
761 | assert(!"Unexpected multi-reg node" ); |
762 | return 0; |
763 | } |
764 | |
765 | //--------------------------------------------------------------- |
766 | // gtGetRegMask: Get the reg mask of the node. |
767 | // |
768 | // Arguments: |
769 | // None |
770 | // |
771 | // Return Value: |
772 | // Reg Mask of GenTree node. |
773 | // |
774 | regMaskTP GenTree::gtGetRegMask() const |
775 | { |
776 | regMaskTP resultMask; |
777 | |
778 | if (IsMultiRegCall()) |
779 | { |
780 | // temporarily cast away const-ness as AsCall() method is not declared const |
781 | resultMask = genRegMask(gtRegNum); |
782 | GenTree* temp = const_cast<GenTree*>(this); |
783 | resultMask |= temp->AsCall()->GetOtherRegMask(); |
784 | } |
785 | else if (IsCopyOrReloadOfMultiRegCall()) |
786 | { |
787 | // A multi-reg copy or reload, will have valid regs for only those |
788 | // positions that need to be copied or reloaded. Hence we need |
789 | // to consider only those registers for computing reg mask. |
790 | |
791 | GenTree* tree = const_cast<GenTree*>(this); |
792 | GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload(); |
793 | GenTreeCall* call = copyOrReload->gtGetOp1()->AsCall(); |
794 | unsigned regCount = call->GetReturnTypeDesc()->GetReturnRegCount(); |
795 | |
796 | resultMask = RBM_NONE; |
797 | for (unsigned i = 0; i < regCount; ++i) |
798 | { |
799 | regNumber reg = copyOrReload->GetRegNumByIdx(i); |
800 | if (reg != REG_NA) |
801 | { |
802 | resultMask |= genRegMask(reg); |
803 | } |
804 | } |
805 | } |
806 | #if FEATURE_ARG_SPLIT |
807 | else if (OperIsPutArgSplit()) |
808 | { |
809 | GenTree* tree = const_cast<GenTree*>(this); |
810 | GenTreePutArgSplit* splitArg = tree->AsPutArgSplit(); |
811 | unsigned regCount = splitArg->gtNumRegs; |
812 | |
813 | resultMask = RBM_NONE; |
814 | for (unsigned i = 0; i < regCount; ++i) |
815 | { |
816 | regNumber reg = splitArg->GetRegNumByIdx(i); |
817 | assert(reg != REG_NA); |
818 | resultMask |= genRegMask(reg); |
819 | } |
820 | } |
821 | #endif // FEATURE_ARG_SPLIT |
822 | else |
823 | { |
824 | resultMask = genRegMask(gtRegNum); |
825 | } |
826 | |
827 | return resultMask; |
828 | } |
829 | |
830 | //--------------------------------------------------------------- |
831 | // GetOtherRegMask: Get the reg mask of gtOtherRegs of call node |
832 | // |
833 | // Arguments: |
834 | // None |
835 | // |
836 | // Return Value: |
837 | // Reg mask of gtOtherRegs of call node. |
838 | // |
839 | regMaskTP GenTreeCall::GetOtherRegMask() const |
840 | { |
841 | regMaskTP resultMask = RBM_NONE; |
842 | |
843 | #if FEATURE_MULTIREG_RET |
844 | for (unsigned i = 0; i < MAX_RET_REG_COUNT - 1; ++i) |
845 | { |
846 | if (gtOtherRegs[i] != REG_NA) |
847 | { |
848 | resultMask |= genRegMask((regNumber)gtOtherRegs[i]); |
849 | continue; |
850 | } |
851 | break; |
852 | } |
853 | #endif |
854 | |
855 | return resultMask; |
856 | } |
857 | |
858 | //------------------------------------------------------------------------- |
859 | // IsPure: |
860 | // Returns true if this call is pure. For now, this uses the same |
861 | // definition of "pure" that is that used by HelperCallProperties: a |
862 | // pure call does not read or write any aliased (e.g. heap) memory or |
863 | // have other global side effects (e.g. class constructors, finalizers), |
864 | // but is allowed to throw an exception. |
865 | // |
866 | // NOTE: this call currently only returns true if the call target is a |
867 | // helper method that is known to be pure. No other analysis is |
868 | // performed. |
869 | // |
870 | // Arguments: |
871 | // Copiler - the compiler context. |
872 | // |
873 | // Returns: |
874 | // True if the call is pure; false otherwise. |
875 | // |
876 | bool GenTreeCall::IsPure(Compiler* compiler) const |
877 | { |
878 | return (gtCallType == CT_HELPER) && |
879 | compiler->s_helperCallProperties.IsPure(compiler->eeGetHelperNum(gtCallMethHnd)); |
880 | } |
881 | |
882 | //------------------------------------------------------------------------- |
883 | // HasSideEffects: |
884 | // Returns true if this call has any side effects. All non-helpers are considered to have side-effects. Only helpers |
885 | // that do not mutate the heap, do not run constructors, may not throw, and are either a) pure or b) non-finalizing |
886 | // allocation functions are considered side-effect-free. |
887 | // |
888 | // Arguments: |
889 | // compiler - the compiler instance |
890 | // ignoreExceptions - when `true`, ignores exception side effects |
891 | // ignoreCctors - when `true`, ignores class constructor side effects |
892 | // |
893 | // Return Value: |
894 | // true if this call has any side-effects; false otherwise. |
895 | bool GenTreeCall::HasSideEffects(Compiler* compiler, bool ignoreExceptions, bool ignoreCctors) const |
896 | { |
897 | // Generally all GT_CALL nodes are considered to have side-effects, but we may have extra information about helper |
898 | // calls that can prove them side-effect-free. |
899 | if (gtCallType != CT_HELPER) |
900 | { |
901 | return true; |
902 | } |
903 | |
904 | CorInfoHelpFunc helper = compiler->eeGetHelperNum(gtCallMethHnd); |
905 | HelperCallProperties& helperProperties = compiler->s_helperCallProperties; |
906 | |
907 | // We definitely care about the side effects if MutatesHeap is true |
908 | if (helperProperties.MutatesHeap(helper)) |
909 | { |
910 | return true; |
911 | } |
912 | |
913 | // Unless we have been instructed to ignore cctors (CSE, for example, ignores cctors), consider them side effects. |
914 | if (!ignoreCctors && helperProperties.MayRunCctor(helper)) |
915 | { |
916 | return true; |
917 | } |
918 | |
919 | // If we also care about exceptions then check if the helper can throw |
920 | if (!ignoreExceptions && !helperProperties.NoThrow(helper)) |
921 | { |
922 | return true; |
923 | } |
924 | |
925 | // If this is not a Pure helper call or an allocator (that will not need to run a finalizer) |
926 | // then this call has side effects. |
927 | return !helperProperties.IsPure(helper) && |
928 | (!helperProperties.IsAllocator(helper) || ((gtCallMoreFlags & GTF_CALL_M_ALLOC_SIDE_EFFECTS) != 0)); |
929 | } |
930 | |
931 | //------------------------------------------------------------------------- |
932 | // HasNonStandardAddedArgs: Return true if the method has non-standard args added to the call |
933 | // argument list during argument morphing (fgMorphArgs), e.g., passed in R10 or R11 on AMD64. |
934 | // See also GetNonStandardAddedArgCount(). |
935 | // |
936 | // Arguments: |
937 | // compiler - the compiler instance |
938 | // |
939 | // Return Value: |
940 | // true if there are any such args, false otherwise. |
941 | // |
942 | bool GenTreeCall::HasNonStandardAddedArgs(Compiler* compiler) const |
943 | { |
944 | return GetNonStandardAddedArgCount(compiler) != 0; |
945 | } |
946 | |
947 | //------------------------------------------------------------------------- |
948 | // GetNonStandardAddedArgCount: Get the count of non-standard arguments that have been added |
949 | // during call argument morphing (fgMorphArgs). Do not count non-standard args that are already |
950 | // counted in the argument list prior to morphing. |
951 | // |
952 | // This function is used to help map the caller and callee arguments during tail call setup. |
953 | // |
954 | // Arguments: |
955 | // compiler - the compiler instance |
956 | // |
957 | // Return Value: |
958 | // The count of args, as described. |
959 | // |
960 | // Notes: |
961 | // It would be more general to have fgMorphArgs set a bit on the call node when such |
962 | // args are added to a call, and a bit on each such arg, and then have this code loop |
963 | // over the call args when the special call bit is set, counting the args with the special |
964 | // arg bit. This seems pretty heavyweight, though. Instead, this logic needs to be kept |
965 | // in sync with fgMorphArgs. |
966 | // |
967 | int GenTreeCall::GetNonStandardAddedArgCount(Compiler* compiler) const |
968 | { |
969 | if (IsUnmanaged() && !compiler->opts.ShouldUsePInvokeHelpers()) |
970 | { |
971 | // R11 = PInvoke cookie param |
972 | return 1; |
973 | } |
974 | else if (IsVirtualStub()) |
975 | { |
976 | // R11 = Virtual stub param |
977 | return 1; |
978 | } |
979 | else if ((gtCallType == CT_INDIRECT) && (gtCallCookie != nullptr)) |
980 | { |
981 | // R10 = PInvoke target param |
982 | // R11 = PInvoke cookie param |
983 | return 2; |
984 | } |
985 | return 0; |
986 | } |
987 | |
988 | //------------------------------------------------------------------------- |
989 | // TreatAsHasRetBufArg: |
990 | // |
991 | // Arguments: |
992 | // compiler, the compiler instance so that we can call eeGetHelperNum |
993 | // |
994 | // Return Value: |
995 | // Returns true if we treat the call as if it has a retBuf argument |
996 | // This method may actually have a retBuf argument |
997 | // or it could be a JIT helper that we are still transforming during |
998 | // the importer phase. |
999 | // |
1000 | // Notes: |
1001 | // On ARM64 marking the method with the GTF_CALL_M_RETBUFFARG flag |
1002 | // will make HasRetBufArg() return true, but will also force the |
1003 | // use of register x8 to pass the RetBuf argument. |
1004 | // |
1005 | // These two Jit Helpers that we handle here by returning true |
1006 | // aren't actually defined to return a struct, so they don't expect |
1007 | // their RetBuf to be passed in x8, instead they expect it in x0. |
1008 | // |
1009 | bool GenTreeCall::TreatAsHasRetBufArg(Compiler* compiler) const |
1010 | { |
1011 | if (HasRetBufArg()) |
1012 | { |
1013 | return true; |
1014 | } |
1015 | else |
1016 | { |
1017 | // If we see a Jit helper call that returns a TYP_STRUCT we will |
1018 | // transform it as if it has a Return Buffer Argument |
1019 | // |
1020 | if (IsHelperCall() && (gtReturnType == TYP_STRUCT)) |
1021 | { |
1022 | // There are two possible helper calls that use this path: |
1023 | // CORINFO_HELP_GETFIELDSTRUCT and CORINFO_HELP_UNBOX_NULLABLE |
1024 | // |
1025 | CorInfoHelpFunc helpFunc = compiler->eeGetHelperNum(gtCallMethHnd); |
1026 | |
1027 | if (helpFunc == CORINFO_HELP_GETFIELDSTRUCT) |
1028 | { |
1029 | return true; |
1030 | } |
1031 | else if (helpFunc == CORINFO_HELP_UNBOX_NULLABLE) |
1032 | { |
1033 | return true; |
1034 | } |
1035 | else |
1036 | { |
1037 | assert(!"Unexpected JIT helper in TreatAsHasRetBufArg" ); |
1038 | } |
1039 | } |
1040 | } |
1041 | return false; |
1042 | } |
1043 | |
1044 | //------------------------------------------------------------------------- |
1045 | // IsHelperCall: Determine if this GT_CALL node is a specific helper call. |
1046 | // |
1047 | // Arguments: |
1048 | // compiler - the compiler instance so that we can call eeFindHelper |
1049 | // |
1050 | // Return Value: |
1051 | // Returns true if this GT_CALL node is a call to the specified helper. |
1052 | // |
1053 | bool GenTreeCall::IsHelperCall(Compiler* compiler, unsigned helper) const |
1054 | { |
1055 | return IsHelperCall(compiler->eeFindHelper(helper)); |
1056 | } |
1057 | |
1058 | //------------------------------------------------------------------------ |
1059 | // GenTreeCall::ReplaceCallOperand: |
1060 | // Replaces a given operand to a call node and updates the call |
1061 | // argument table if necessary. |
1062 | // |
1063 | // Arguments: |
1064 | // useEdge - the use edge that points to the operand to be replaced. |
1065 | // replacement - the replacement node. |
1066 | // |
1067 | void GenTreeCall::ReplaceCallOperand(GenTree** useEdge, GenTree* replacement) |
1068 | { |
1069 | assert(useEdge != nullptr); |
1070 | assert(replacement != nullptr); |
1071 | assert(TryGetUse(*useEdge, &useEdge)); |
1072 | |
1073 | GenTree* originalOperand = *useEdge; |
1074 | *useEdge = replacement; |
1075 | |
1076 | const bool isArgument = |
1077 | (replacement != gtControlExpr) && |
1078 | ((gtCallType != CT_INDIRECT) || ((replacement != gtCallCookie) && (replacement != gtCallAddr))); |
1079 | |
1080 | if (isArgument) |
1081 | { |
1082 | if ((originalOperand->gtFlags & GTF_LATE_ARG) != 0) |
1083 | { |
1084 | replacement->gtFlags |= GTF_LATE_ARG; |
1085 | } |
1086 | else |
1087 | { |
1088 | assert((replacement->gtFlags & GTF_LATE_ARG) == 0); |
1089 | |
1090 | fgArgTabEntry* fp = Compiler::gtArgEntryByNode(this, originalOperand); |
1091 | assert(fp->node == originalOperand); |
1092 | fp->node = replacement; |
1093 | } |
1094 | } |
1095 | } |
1096 | |
1097 | //------------------------------------------------------------------------- |
1098 | // AreArgsComplete: Determine if this GT_CALL node's arguments have been processed. |
1099 | // |
1100 | // Return Value: |
1101 | // Returns true if fgMorphArgs has processed the arguments. |
1102 | // |
1103 | bool GenTreeCall::AreArgsComplete() const |
1104 | { |
1105 | if (fgArgInfo == nullptr) |
1106 | { |
1107 | return false; |
1108 | } |
1109 | if (fgArgInfo->AreArgsComplete()) |
1110 | { |
1111 | assert((gtCallLateArgs != nullptr) || !fgArgInfo->HasRegArgs()); |
1112 | return true; |
1113 | } |
1114 | assert(gtCallArgs == nullptr); |
1115 | return false; |
1116 | } |
1117 | |
1118 | #if !defined(FEATURE_PUT_STRUCT_ARG_STK) |
1119 | unsigned GenTreePutArgStk::getArgSize() |
1120 | { |
1121 | return genTypeSize(genActualType(gtOp1->gtType)); |
1122 | } |
1123 | #endif // !defined(FEATURE_PUT_STRUCT_ARG_STK) |
1124 | |
1125 | /***************************************************************************** |
1126 | * |
1127 | * Returns non-zero if the two trees are identical. |
1128 | */ |
1129 | |
1130 | bool GenTree::Compare(GenTree* op1, GenTree* op2, bool swapOK) |
1131 | { |
1132 | genTreeOps oper; |
1133 | unsigned kind; |
1134 | |
1135 | // printf("tree1:\n"); gtDispTree(op1); |
1136 | // printf("tree2:\n"); gtDispTree(op2); |
1137 | |
1138 | AGAIN: |
1139 | |
1140 | if (op1 == nullptr) |
1141 | { |
1142 | return (op2 == nullptr); |
1143 | } |
1144 | if (op2 == nullptr) |
1145 | { |
1146 | return false; |
1147 | } |
1148 | if (op1 == op2) |
1149 | { |
1150 | return true; |
1151 | } |
1152 | |
1153 | assert(op1->gtOper != GT_STMT); |
1154 | assert(op2->gtOper != GT_STMT); |
1155 | |
1156 | oper = op1->OperGet(); |
1157 | |
1158 | /* The operators must be equal */ |
1159 | |
1160 | if (oper != op2->gtOper) |
1161 | { |
1162 | return false; |
1163 | } |
1164 | |
1165 | /* The types must be equal */ |
1166 | |
1167 | if (op1->gtType != op2->gtType) |
1168 | { |
1169 | return false; |
1170 | } |
1171 | |
1172 | /* Overflow must be equal */ |
1173 | if (op1->gtOverflowEx() != op2->gtOverflowEx()) |
1174 | { |
1175 | return false; |
1176 | } |
1177 | |
1178 | /* Sensible flags must be equal */ |
1179 | if ((op1->gtFlags & (GTF_UNSIGNED)) != (op2->gtFlags & (GTF_UNSIGNED))) |
1180 | { |
1181 | return false; |
1182 | } |
1183 | |
1184 | /* Figure out what kind of nodes we're comparing */ |
1185 | |
1186 | kind = op1->OperKind(); |
1187 | |
1188 | /* Is this a constant node? */ |
1189 | |
1190 | if (kind & GTK_CONST) |
1191 | { |
1192 | switch (oper) |
1193 | { |
1194 | case GT_CNS_INT: |
1195 | if (op1->gtIntCon.gtIconVal == op2->gtIntCon.gtIconVal) |
1196 | { |
1197 | return true; |
1198 | } |
1199 | break; |
1200 | #if 0 |
1201 | // TODO-CQ: Enable this in the future |
1202 | case GT_CNS_LNG: |
1203 | if (op1->gtLngCon.gtLconVal == op2->gtLngCon.gtLconVal) |
1204 | return true; |
1205 | break; |
1206 | |
1207 | case GT_CNS_DBL: |
1208 | if (op1->gtDblCon.gtDconVal == op2->gtDblCon.gtDconVal) |
1209 | return true; |
1210 | break; |
1211 | #endif |
1212 | default: |
1213 | break; |
1214 | } |
1215 | |
1216 | return false; |
1217 | } |
1218 | |
1219 | /* Is this a leaf node? */ |
1220 | |
1221 | if (kind & GTK_LEAF) |
1222 | { |
1223 | switch (oper) |
1224 | { |
1225 | case GT_LCL_VAR: |
1226 | if (op1->gtLclVarCommon.gtLclNum != op2->gtLclVarCommon.gtLclNum) |
1227 | { |
1228 | break; |
1229 | } |
1230 | |
1231 | return true; |
1232 | |
1233 | case GT_LCL_FLD: |
1234 | if (op1->gtLclFld.gtLclNum != op2->gtLclFld.gtLclNum || |
1235 | op1->gtLclFld.gtLclOffs != op2->gtLclFld.gtLclOffs) |
1236 | { |
1237 | break; |
1238 | } |
1239 | |
1240 | return true; |
1241 | |
1242 | case GT_CLS_VAR: |
1243 | if (op1->gtClsVar.gtClsVarHnd != op2->gtClsVar.gtClsVarHnd) |
1244 | { |
1245 | break; |
1246 | } |
1247 | |
1248 | return true; |
1249 | |
1250 | case GT_LABEL: |
1251 | return true; |
1252 | |
1253 | case GT_ARGPLACE: |
1254 | if ((op1->gtType == TYP_STRUCT) && |
1255 | (op1->gtArgPlace.gtArgPlaceClsHnd != op2->gtArgPlace.gtArgPlaceClsHnd)) |
1256 | { |
1257 | break; |
1258 | } |
1259 | return true; |
1260 | |
1261 | default: |
1262 | break; |
1263 | } |
1264 | |
1265 | return false; |
1266 | } |
1267 | |
1268 | /* Is it a 'simple' unary/binary operator? */ |
1269 | |
1270 | if (kind & GTK_UNOP) |
1271 | { |
1272 | if (IsExOp(kind)) |
1273 | { |
1274 | // ExOp operators extend unary operator with extra, non-GenTree* members. In many cases, |
1275 | // these should be included in the comparison. |
1276 | switch (oper) |
1277 | { |
1278 | case GT_ARR_LENGTH: |
1279 | if (op1->gtArrLen.ArrLenOffset() != op2->gtArrLen.ArrLenOffset()) |
1280 | { |
1281 | return false; |
1282 | } |
1283 | break; |
1284 | case GT_CAST: |
1285 | if (op1->gtCast.gtCastType != op2->gtCast.gtCastType) |
1286 | { |
1287 | return false; |
1288 | } |
1289 | break; |
1290 | case GT_OBJ: |
1291 | if (op1->AsObj()->gtClass != op2->AsObj()->gtClass) |
1292 | { |
1293 | return false; |
1294 | } |
1295 | break; |
1296 | |
1297 | // For the ones below no extra argument matters for comparison. |
1298 | case GT_BOX: |
1299 | case GT_RUNTIMELOOKUP: |
1300 | break; |
1301 | |
1302 | default: |
1303 | assert(!"unexpected unary ExOp operator" ); |
1304 | } |
1305 | } |
1306 | return Compare(op1->gtOp.gtOp1, op2->gtOp.gtOp1); |
1307 | } |
1308 | |
1309 | if (kind & GTK_BINOP) |
1310 | { |
1311 | if (IsExOp(kind)) |
1312 | { |
1313 | // ExOp operators extend unary operator with extra, non-GenTree* members. In many cases, |
1314 | // these should be included in the hash code. |
1315 | switch (oper) |
1316 | { |
1317 | case GT_INTRINSIC: |
1318 | if (op1->gtIntrinsic.gtIntrinsicId != op2->gtIntrinsic.gtIntrinsicId) |
1319 | { |
1320 | return false; |
1321 | } |
1322 | break; |
1323 | case GT_LEA: |
1324 | if (op1->gtAddrMode.gtScale != op2->gtAddrMode.gtScale) |
1325 | { |
1326 | return false; |
1327 | } |
1328 | if (op1->gtAddrMode.Offset() != op2->gtAddrMode.Offset()) |
1329 | { |
1330 | return false; |
1331 | } |
1332 | break; |
1333 | case GT_INDEX: |
1334 | if (op1->gtIndex.gtIndElemSize != op2->gtIndex.gtIndElemSize) |
1335 | { |
1336 | return false; |
1337 | } |
1338 | break; |
1339 | case GT_INDEX_ADDR: |
1340 | if (op1->AsIndexAddr()->gtElemSize != op2->AsIndexAddr()->gtElemSize) |
1341 | { |
1342 | return false; |
1343 | } |
1344 | break; |
1345 | #ifdef FEATURE_SIMD |
1346 | case GT_SIMD: |
1347 | if ((op1->AsSIMD()->gtSIMDIntrinsicID != op2->AsSIMD()->gtSIMDIntrinsicID) || |
1348 | (op1->AsSIMD()->gtSIMDBaseType != op2->AsSIMD()->gtSIMDBaseType) || |
1349 | (op1->AsSIMD()->gtSIMDSize != op2->AsSIMD()->gtSIMDSize)) |
1350 | { |
1351 | return false; |
1352 | } |
1353 | break; |
1354 | #endif // FEATURE_SIMD |
1355 | |
1356 | #ifdef FEATURE_HW_INTRINSICS |
1357 | case GT_HWIntrinsic: |
1358 | if ((op1->AsHWIntrinsic()->gtHWIntrinsicId != op2->AsHWIntrinsic()->gtHWIntrinsicId) || |
1359 | (op1->AsHWIntrinsic()->gtSIMDBaseType != op2->AsHWIntrinsic()->gtSIMDBaseType) || |
1360 | (op1->AsHWIntrinsic()->gtSIMDSize != op2->AsHWIntrinsic()->gtSIMDSize) || |
1361 | (op1->AsHWIntrinsic()->gtIndexBaseType != op2->AsHWIntrinsic()->gtIndexBaseType)) |
1362 | { |
1363 | return false; |
1364 | } |
1365 | break; |
1366 | #endif |
1367 | |
1368 | // For the ones below no extra argument matters for comparison. |
1369 | case GT_QMARK: |
1370 | break; |
1371 | |
1372 | default: |
1373 | assert(!"unexpected binary ExOp operator" ); |
1374 | } |
1375 | } |
1376 | |
1377 | if (op1->gtOp.gtOp2) |
1378 | { |
1379 | if (!Compare(op1->gtOp.gtOp1, op2->gtOp.gtOp1, swapOK)) |
1380 | { |
1381 | if (swapOK && OperIsCommutative(oper) && |
1382 | ((op1->gtOp.gtOp1->gtFlags | op1->gtOp.gtOp2->gtFlags | op2->gtOp.gtOp1->gtFlags | |
1383 | op2->gtOp.gtOp2->gtFlags) & |
1384 | GTF_ALL_EFFECT) == 0) |
1385 | { |
1386 | if (Compare(op1->gtOp.gtOp1, op2->gtOp.gtOp2, swapOK)) |
1387 | { |
1388 | op1 = op1->gtOp.gtOp2; |
1389 | op2 = op2->gtOp.gtOp1; |
1390 | goto AGAIN; |
1391 | } |
1392 | } |
1393 | |
1394 | return false; |
1395 | } |
1396 | |
1397 | op1 = op1->gtOp.gtOp2; |
1398 | op2 = op2->gtOp.gtOp2; |
1399 | |
1400 | goto AGAIN; |
1401 | } |
1402 | else |
1403 | { |
1404 | |
1405 | op1 = op1->gtOp.gtOp1; |
1406 | op2 = op2->gtOp.gtOp1; |
1407 | |
1408 | if (!op1) |
1409 | { |
1410 | return (op2 == nullptr); |
1411 | } |
1412 | if (!op2) |
1413 | { |
1414 | return false; |
1415 | } |
1416 | |
1417 | goto AGAIN; |
1418 | } |
1419 | } |
1420 | |
1421 | /* See what kind of a special operator we have here */ |
1422 | |
1423 | switch (oper) |
1424 | { |
1425 | case GT_FIELD: |
1426 | if (op1->gtField.gtFldHnd != op2->gtField.gtFldHnd) |
1427 | { |
1428 | break; |
1429 | } |
1430 | |
1431 | op1 = op1->gtField.gtFldObj; |
1432 | op2 = op2->gtField.gtFldObj; |
1433 | |
1434 | if (op1 || op2) |
1435 | { |
1436 | if (op1 && op2) |
1437 | { |
1438 | goto AGAIN; |
1439 | } |
1440 | } |
1441 | |
1442 | return true; |
1443 | |
1444 | case GT_CALL: |
1445 | |
1446 | if (op1->gtCall.gtCallType != op2->gtCall.gtCallType) |
1447 | { |
1448 | return false; |
1449 | } |
1450 | |
1451 | if (op1->gtCall.gtCallType != CT_INDIRECT) |
1452 | { |
1453 | if (op1->gtCall.gtCallMethHnd != op2->gtCall.gtCallMethHnd) |
1454 | { |
1455 | return false; |
1456 | } |
1457 | |
1458 | #ifdef FEATURE_READYTORUN_COMPILER |
1459 | if (op1->gtCall.gtEntryPoint.addr != op2->gtCall.gtEntryPoint.addr) |
1460 | { |
1461 | return false; |
1462 | } |
1463 | #endif |
1464 | } |
1465 | else |
1466 | { |
1467 | if (!Compare(op1->gtCall.gtCallAddr, op2->gtCall.gtCallAddr)) |
1468 | { |
1469 | return false; |
1470 | } |
1471 | } |
1472 | |
1473 | if (Compare(op1->gtCall.gtCallLateArgs, op2->gtCall.gtCallLateArgs) && |
1474 | Compare(op1->gtCall.gtCallArgs, op2->gtCall.gtCallArgs) && |
1475 | Compare(op1->gtCall.gtControlExpr, op2->gtCall.gtControlExpr) && |
1476 | Compare(op1->gtCall.gtCallObjp, op2->gtCall.gtCallObjp)) |
1477 | { |
1478 | return true; |
1479 | } |
1480 | break; |
1481 | |
1482 | case GT_ARR_ELEM: |
1483 | |
1484 | if (op1->gtArrElem.gtArrRank != op2->gtArrElem.gtArrRank) |
1485 | { |
1486 | return false; |
1487 | } |
1488 | |
1489 | // NOTE: gtArrElemSize may need to be handled |
1490 | |
1491 | unsigned dim; |
1492 | for (dim = 0; dim < op1->gtArrElem.gtArrRank; dim++) |
1493 | { |
1494 | if (!Compare(op1->gtArrElem.gtArrInds[dim], op2->gtArrElem.gtArrInds[dim])) |
1495 | { |
1496 | return false; |
1497 | } |
1498 | } |
1499 | |
1500 | op1 = op1->gtArrElem.gtArrObj; |
1501 | op2 = op2->gtArrElem.gtArrObj; |
1502 | goto AGAIN; |
1503 | |
1504 | case GT_ARR_OFFSET: |
1505 | if (op1->gtArrOffs.gtCurrDim != op2->gtArrOffs.gtCurrDim || |
1506 | op1->gtArrOffs.gtArrRank != op2->gtArrOffs.gtArrRank) |
1507 | { |
1508 | return false; |
1509 | } |
1510 | return (Compare(op1->gtArrOffs.gtOffset, op2->gtArrOffs.gtOffset) && |
1511 | Compare(op1->gtArrOffs.gtIndex, op2->gtArrOffs.gtIndex) && |
1512 | Compare(op1->gtArrOffs.gtArrObj, op2->gtArrOffs.gtArrObj)); |
1513 | |
1514 | case GT_CMPXCHG: |
1515 | return Compare(op1->gtCmpXchg.gtOpLocation, op2->gtCmpXchg.gtOpLocation) && |
1516 | Compare(op1->gtCmpXchg.gtOpValue, op2->gtCmpXchg.gtOpValue) && |
1517 | Compare(op1->gtCmpXchg.gtOpComparand, op2->gtCmpXchg.gtOpComparand); |
1518 | |
1519 | case GT_ARR_BOUNDS_CHECK: |
1520 | #ifdef FEATURE_SIMD |
1521 | case GT_SIMD_CHK: |
1522 | #endif // FEATURE_SIMD |
1523 | #ifdef FEATURE_HW_INTRINSICS |
1524 | case GT_HW_INTRINSIC_CHK: |
1525 | #endif // FEATURE_HW_INTRINSICS |
1526 | return Compare(op1->gtBoundsChk.gtIndex, op2->gtBoundsChk.gtIndex) && |
1527 | Compare(op1->gtBoundsChk.gtArrLen, op2->gtBoundsChk.gtArrLen) && |
1528 | (op1->gtBoundsChk.gtThrowKind == op2->gtBoundsChk.gtThrowKind); |
1529 | |
1530 | case GT_STORE_DYN_BLK: |
1531 | case GT_DYN_BLK: |
1532 | return Compare(op1->gtDynBlk.Addr(), op2->gtDynBlk.Addr()) && |
1533 | Compare(op1->gtDynBlk.Data(), op2->gtDynBlk.Data()) && |
1534 | Compare(op1->gtDynBlk.gtDynamicSize, op2->gtDynBlk.gtDynamicSize); |
1535 | |
1536 | default: |
1537 | assert(!"unexpected operator" ); |
1538 | } |
1539 | |
1540 | return false; |
1541 | } |
1542 | |
1543 | /***************************************************************************** |
1544 | * |
1545 | * Returns non-zero if the given tree contains a use of a local #lclNum. |
1546 | */ |
1547 | |
1548 | bool Compiler::gtHasRef(GenTree* tree, ssize_t lclNum, bool defOnly) |
1549 | { |
1550 | genTreeOps oper; |
1551 | unsigned kind; |
1552 | |
1553 | AGAIN: |
1554 | |
1555 | assert(tree); |
1556 | |
1557 | oper = tree->OperGet(); |
1558 | kind = tree->OperKind(); |
1559 | |
1560 | assert(oper != GT_STMT); |
1561 | |
1562 | /* Is this a constant node? */ |
1563 | |
1564 | if (kind & GTK_CONST) |
1565 | { |
1566 | return false; |
1567 | } |
1568 | |
1569 | /* Is this a leaf node? */ |
1570 | |
1571 | if (kind & GTK_LEAF) |
1572 | { |
1573 | if (oper == GT_LCL_VAR) |
1574 | { |
1575 | if (tree->gtLclVarCommon.gtLclNum == (unsigned)lclNum) |
1576 | { |
1577 | if (!defOnly) |
1578 | { |
1579 | return true; |
1580 | } |
1581 | } |
1582 | } |
1583 | else if (oper == GT_RET_EXPR) |
1584 | { |
1585 | return gtHasRef(tree->gtRetExpr.gtInlineCandidate, lclNum, defOnly); |
1586 | } |
1587 | |
1588 | return false; |
1589 | } |
1590 | |
1591 | /* Is it a 'simple' unary/binary operator? */ |
1592 | |
1593 | if (kind & GTK_SMPOP) |
1594 | { |
1595 | if (tree->gtGetOp2IfPresent()) |
1596 | { |
1597 | if (gtHasRef(tree->gtOp.gtOp1, lclNum, defOnly)) |
1598 | { |
1599 | return true; |
1600 | } |
1601 | |
1602 | tree = tree->gtOp.gtOp2; |
1603 | goto AGAIN; |
1604 | } |
1605 | else |
1606 | { |
1607 | tree = tree->gtOp.gtOp1; |
1608 | |
1609 | if (!tree) |
1610 | { |
1611 | return false; |
1612 | } |
1613 | |
1614 | if (oper == GT_ASG) |
1615 | { |
1616 | // 'tree' is the gtOp1 of an assignment node. So we can handle |
1617 | // the case where defOnly is either true or false. |
1618 | |
1619 | if (tree->gtOper == GT_LCL_VAR && tree->gtLclVarCommon.gtLclNum == (unsigned)lclNum) |
1620 | { |
1621 | return true; |
1622 | } |
1623 | else if (tree->gtOper == GT_FIELD && lclNum == (ssize_t)tree->gtField.gtFldHnd) |
1624 | { |
1625 | return true; |
1626 | } |
1627 | } |
1628 | |
1629 | goto AGAIN; |
1630 | } |
1631 | } |
1632 | |
1633 | /* See what kind of a special operator we have here */ |
1634 | |
1635 | switch (oper) |
1636 | { |
1637 | case GT_FIELD: |
1638 | if (lclNum == (ssize_t)tree->gtField.gtFldHnd) |
1639 | { |
1640 | if (!defOnly) |
1641 | { |
1642 | return true; |
1643 | } |
1644 | } |
1645 | |
1646 | tree = tree->gtField.gtFldObj; |
1647 | if (tree) |
1648 | { |
1649 | goto AGAIN; |
1650 | } |
1651 | break; |
1652 | |
1653 | case GT_CALL: |
1654 | |
1655 | if (tree->gtCall.gtCallObjp) |
1656 | { |
1657 | if (gtHasRef(tree->gtCall.gtCallObjp, lclNum, defOnly)) |
1658 | { |
1659 | return true; |
1660 | } |
1661 | } |
1662 | |
1663 | if (tree->gtCall.gtCallArgs) |
1664 | { |
1665 | if (gtHasRef(tree->gtCall.gtCallArgs, lclNum, defOnly)) |
1666 | { |
1667 | return true; |
1668 | } |
1669 | } |
1670 | |
1671 | if (tree->gtCall.gtCallLateArgs) |
1672 | { |
1673 | if (gtHasRef(tree->gtCall.gtCallLateArgs, lclNum, defOnly)) |
1674 | { |
1675 | return true; |
1676 | } |
1677 | } |
1678 | |
1679 | if (tree->gtCall.gtControlExpr) |
1680 | { |
1681 | if (gtHasRef(tree->gtCall.gtControlExpr, lclNum, defOnly)) |
1682 | { |
1683 | return true; |
1684 | } |
1685 | } |
1686 | |
1687 | if (tree->gtCall.gtCallType == CT_INDIRECT) |
1688 | { |
1689 | // pinvoke-calli cookie is a constant, or constant indirection |
1690 | assert(tree->gtCall.gtCallCookie == nullptr || tree->gtCall.gtCallCookie->gtOper == GT_CNS_INT || |
1691 | tree->gtCall.gtCallCookie->gtOper == GT_IND); |
1692 | |
1693 | tree = tree->gtCall.gtCallAddr; |
1694 | } |
1695 | else |
1696 | { |
1697 | tree = nullptr; |
1698 | } |
1699 | |
1700 | if (tree) |
1701 | { |
1702 | goto AGAIN; |
1703 | } |
1704 | |
1705 | break; |
1706 | |
1707 | case GT_ARR_ELEM: |
1708 | if (gtHasRef(tree->gtArrElem.gtArrObj, lclNum, defOnly)) |
1709 | { |
1710 | return true; |
1711 | } |
1712 | |
1713 | unsigned dim; |
1714 | for (dim = 0; dim < tree->gtArrElem.gtArrRank; dim++) |
1715 | { |
1716 | if (gtHasRef(tree->gtArrElem.gtArrInds[dim], lclNum, defOnly)) |
1717 | { |
1718 | return true; |
1719 | } |
1720 | } |
1721 | |
1722 | break; |
1723 | |
1724 | case GT_ARR_OFFSET: |
1725 | if (gtHasRef(tree->gtArrOffs.gtOffset, lclNum, defOnly) || |
1726 | gtHasRef(tree->gtArrOffs.gtIndex, lclNum, defOnly) || |
1727 | gtHasRef(tree->gtArrOffs.gtArrObj, lclNum, defOnly)) |
1728 | { |
1729 | return true; |
1730 | } |
1731 | break; |
1732 | |
1733 | case GT_CMPXCHG: |
1734 | if (gtHasRef(tree->gtCmpXchg.gtOpLocation, lclNum, defOnly)) |
1735 | { |
1736 | return true; |
1737 | } |
1738 | if (gtHasRef(tree->gtCmpXchg.gtOpValue, lclNum, defOnly)) |
1739 | { |
1740 | return true; |
1741 | } |
1742 | if (gtHasRef(tree->gtCmpXchg.gtOpComparand, lclNum, defOnly)) |
1743 | { |
1744 | return true; |
1745 | } |
1746 | break; |
1747 | |
1748 | case GT_ARR_BOUNDS_CHECK: |
1749 | #ifdef FEATURE_SIMD |
1750 | case GT_SIMD_CHK: |
1751 | #endif // FEATURE_SIMD |
1752 | #ifdef FEATURE_HW_INTRINSICS |
1753 | case GT_HW_INTRINSIC_CHK: |
1754 | #endif // FEATURE_HW_INTRINSICS |
1755 | if (gtHasRef(tree->gtBoundsChk.gtIndex, lclNum, defOnly)) |
1756 | { |
1757 | return true; |
1758 | } |
1759 | if (gtHasRef(tree->gtBoundsChk.gtArrLen, lclNum, defOnly)) |
1760 | { |
1761 | return true; |
1762 | } |
1763 | break; |
1764 | |
1765 | case GT_STORE_DYN_BLK: |
1766 | if (gtHasRef(tree->gtDynBlk.Data(), lclNum, defOnly)) |
1767 | { |
1768 | return true; |
1769 | } |
1770 | __fallthrough; |
1771 | case GT_DYN_BLK: |
1772 | if (gtHasRef(tree->gtDynBlk.Addr(), lclNum, defOnly)) |
1773 | { |
1774 | return true; |
1775 | } |
1776 | if (gtHasRef(tree->gtDynBlk.gtDynamicSize, lclNum, defOnly)) |
1777 | { |
1778 | return true; |
1779 | } |
1780 | break; |
1781 | |
1782 | default: |
1783 | #ifdef DEBUG |
1784 | gtDispTree(tree); |
1785 | #endif |
1786 | assert(!"unexpected operator" ); |
1787 | } |
1788 | |
1789 | return false; |
1790 | } |
1791 | |
1792 | struct AddrTakenDsc |
1793 | { |
1794 | Compiler* comp; |
1795 | bool hasAddrTakenLcl; |
1796 | }; |
1797 | |
1798 | /* static */ |
1799 | Compiler::fgWalkResult Compiler::gtHasLocalsWithAddrOpCB(GenTree** pTree, fgWalkData* data) |
1800 | { |
1801 | GenTree* tree = *pTree; |
1802 | Compiler* comp = data->compiler; |
1803 | |
1804 | if (tree->gtOper == GT_LCL_VAR) |
1805 | { |
1806 | unsigned lclNum = tree->gtLclVarCommon.gtLclNum; |
1807 | LclVarDsc* varDsc = &comp->lvaTable[lclNum]; |
1808 | |
1809 | if (varDsc->lvHasLdAddrOp || varDsc->lvAddrExposed) |
1810 | { |
1811 | ((AddrTakenDsc*)data->pCallbackData)->hasAddrTakenLcl = true; |
1812 | return WALK_ABORT; |
1813 | } |
1814 | } |
1815 | |
1816 | return WALK_CONTINUE; |
1817 | } |
1818 | |
1819 | /***************************************************************************** |
1820 | * |
1821 | * Return true if this tree contains locals with lvHasLdAddrOp or lvAddrExposed |
1822 | * flag(s) set. |
1823 | */ |
1824 | |
1825 | bool Compiler::gtHasLocalsWithAddrOp(GenTree* tree) |
1826 | { |
1827 | AddrTakenDsc desc; |
1828 | |
1829 | desc.comp = this; |
1830 | desc.hasAddrTakenLcl = false; |
1831 | |
1832 | fgWalkTreePre(&tree, gtHasLocalsWithAddrOpCB, &desc); |
1833 | |
1834 | return desc.hasAddrTakenLcl; |
1835 | } |
1836 | |
1837 | #ifdef DEBUG |
1838 | |
1839 | /***************************************************************************** |
1840 | * |
1841 | * Helper used to compute hash values for trees. |
1842 | */ |
1843 | |
1844 | inline unsigned genTreeHashAdd(unsigned old, unsigned add) |
1845 | { |
1846 | return (old + old / 2) ^ add; |
1847 | } |
1848 | |
1849 | inline unsigned genTreeHashAdd(unsigned old, void* add) |
1850 | { |
1851 | return genTreeHashAdd(old, (unsigned)(size_t)add); |
1852 | } |
1853 | |
1854 | /***************************************************************************** |
1855 | * |
1856 | * Given an arbitrary expression tree, compute a hash value for it. |
1857 | */ |
1858 | |
1859 | unsigned Compiler::gtHashValue(GenTree* tree) |
1860 | { |
1861 | genTreeOps oper; |
1862 | unsigned kind; |
1863 | |
1864 | unsigned hash = 0; |
1865 | |
1866 | GenTree* temp; |
1867 | |
1868 | AGAIN: |
1869 | assert(tree); |
1870 | assert(tree->gtOper != GT_STMT); |
1871 | |
1872 | /* Figure out what kind of a node we have */ |
1873 | |
1874 | oper = tree->OperGet(); |
1875 | kind = tree->OperKind(); |
1876 | |
1877 | /* Include the operator value in the hash */ |
1878 | |
1879 | hash = genTreeHashAdd(hash, oper); |
1880 | |
1881 | /* Is this a constant or leaf node? */ |
1882 | |
1883 | if (kind & (GTK_CONST | GTK_LEAF)) |
1884 | { |
1885 | size_t add; |
1886 | |
1887 | switch (oper) |
1888 | { |
1889 | UINT64 bits; |
1890 | case GT_LCL_VAR: |
1891 | add = tree->gtLclVar.gtLclNum; |
1892 | break; |
1893 | case GT_LCL_FLD: |
1894 | hash = genTreeHashAdd(hash, tree->gtLclFld.gtLclNum); |
1895 | add = tree->gtLclFld.gtLclOffs; |
1896 | break; |
1897 | |
1898 | case GT_CNS_INT: |
1899 | add = tree->gtIntCon.gtIconVal; |
1900 | break; |
1901 | case GT_CNS_LNG: |
1902 | bits = (UINT64)tree->gtLngCon.gtLconVal; |
1903 | #ifdef _HOST_64BIT_ |
1904 | add = bits; |
1905 | #else // 32-bit host |
1906 | add = genTreeHashAdd(uhi32(bits), ulo32(bits)); |
1907 | #endif |
1908 | break; |
1909 | case GT_CNS_DBL: |
1910 | bits = *(UINT64*)(&tree->gtDblCon.gtDconVal); |
1911 | #ifdef _HOST_64BIT_ |
1912 | add = bits; |
1913 | #else // 32-bit host |
1914 | add = genTreeHashAdd(uhi32(bits), ulo32(bits)); |
1915 | #endif |
1916 | break; |
1917 | case GT_CNS_STR: |
1918 | add = tree->gtStrCon.gtSconCPX; |
1919 | break; |
1920 | |
1921 | case GT_JMP: |
1922 | add = tree->gtVal.gtVal1; |
1923 | break; |
1924 | |
1925 | default: |
1926 | add = 0; |
1927 | break; |
1928 | } |
1929 | |
1930 | // clang-format off |
1931 | // narrow 'add' into a 32-bit 'val' |
1932 | unsigned val; |
1933 | #ifdef _HOST_64BIT_ |
1934 | val = genTreeHashAdd(uhi32(add), ulo32(add)); |
1935 | #else // 32-bit host |
1936 | val = add; |
1937 | #endif |
1938 | // clang-format on |
1939 | |
1940 | hash = genTreeHashAdd(hash, val); |
1941 | goto DONE; |
1942 | } |
1943 | |
1944 | /* Is it a 'simple' unary/binary operator? */ |
1945 | |
1946 | GenTree* op1; |
1947 | |
1948 | if (kind & GTK_UNOP) |
1949 | { |
1950 | op1 = tree->gtOp.gtOp1; |
1951 | /* Special case: no sub-operand at all */ |
1952 | |
1953 | if (GenTree::IsExOp(kind)) |
1954 | { |
1955 | // ExOp operators extend operators with extra, non-GenTree* members. In many cases, |
1956 | // these should be included in the hash code. |
1957 | switch (oper) |
1958 | { |
1959 | case GT_ARR_LENGTH: |
1960 | hash += tree->gtArrLen.ArrLenOffset(); |
1961 | break; |
1962 | case GT_CAST: |
1963 | hash ^= tree->gtCast.gtCastType; |
1964 | break; |
1965 | case GT_INDEX: |
1966 | hash += tree->gtIndex.gtIndElemSize; |
1967 | break; |
1968 | case GT_INDEX_ADDR: |
1969 | hash += tree->AsIndexAddr()->gtElemSize; |
1970 | break; |
1971 | case GT_ALLOCOBJ: |
1972 | hash = genTreeHashAdd(hash, static_cast<unsigned>( |
1973 | reinterpret_cast<uintptr_t>(tree->gtAllocObj.gtAllocObjClsHnd))); |
1974 | hash = genTreeHashAdd(hash, tree->gtAllocObj.gtNewHelper); |
1975 | break; |
1976 | case GT_RUNTIMELOOKUP: |
1977 | hash = |
1978 | genTreeHashAdd(hash, |
1979 | static_cast<unsigned>(reinterpret_cast<uintptr_t>(tree->gtRuntimeLookup.gtHnd))); |
1980 | break; |
1981 | |
1982 | case GT_OBJ: |
1983 | hash = |
1984 | genTreeHashAdd(hash, static_cast<unsigned>(reinterpret_cast<uintptr_t>(tree->gtObj.gtClass))); |
1985 | break; |
1986 | // For the ones below no extra argument matters for comparison. |
1987 | case GT_BOX: |
1988 | break; |
1989 | |
1990 | default: |
1991 | assert(!"unexpected unary ExOp operator" ); |
1992 | } |
1993 | } |
1994 | |
1995 | if (!op1) |
1996 | { |
1997 | goto DONE; |
1998 | } |
1999 | |
2000 | tree = op1; |
2001 | goto AGAIN; |
2002 | } |
2003 | |
2004 | if (kind & GTK_BINOP) |
2005 | { |
2006 | if (GenTree::IsExOp(kind)) |
2007 | { |
2008 | // ExOp operators extend operators with extra, non-GenTree* members. In many cases, |
2009 | // these should be included in the hash code. |
2010 | switch (oper) |
2011 | { |
2012 | case GT_INTRINSIC: |
2013 | hash += tree->gtIntrinsic.gtIntrinsicId; |
2014 | break; |
2015 | case GT_LEA: |
2016 | hash += static_cast<unsigned>(tree->gtAddrMode.Offset() << 3) + tree->gtAddrMode.gtScale; |
2017 | break; |
2018 | |
2019 | case GT_BLK: |
2020 | case GT_STORE_BLK: |
2021 | hash += tree->gtBlk.gtBlkSize; |
2022 | break; |
2023 | |
2024 | case GT_OBJ: |
2025 | case GT_STORE_OBJ: |
2026 | hash ^= PtrToUlong(tree->AsObj()->gtClass); |
2027 | break; |
2028 | |
2029 | case GT_DYN_BLK: |
2030 | case GT_STORE_DYN_BLK: |
2031 | hash += gtHashValue(tree->AsDynBlk()->gtDynamicSize); |
2032 | break; |
2033 | |
2034 | // For the ones below no extra argument matters for comparison. |
2035 | case GT_ARR_INDEX: |
2036 | case GT_QMARK: |
2037 | case GT_INDEX: |
2038 | case GT_INDEX_ADDR: |
2039 | break; |
2040 | |
2041 | #ifdef FEATURE_SIMD |
2042 | case GT_SIMD: |
2043 | hash += tree->gtSIMD.gtSIMDIntrinsicID; |
2044 | hash += tree->gtSIMD.gtSIMDBaseType; |
2045 | hash += tree->gtSIMD.gtSIMDSize; |
2046 | break; |
2047 | #endif // FEATURE_SIMD |
2048 | |
2049 | #ifdef FEATURE_HW_INTRINSICS |
2050 | case GT_HWIntrinsic: |
2051 | hash += tree->gtHWIntrinsic.gtHWIntrinsicId; |
2052 | hash += tree->gtHWIntrinsic.gtSIMDBaseType; |
2053 | hash += tree->gtHWIntrinsic.gtSIMDSize; |
2054 | hash += tree->gtHWIntrinsic.gtIndexBaseType; |
2055 | break; |
2056 | #endif // FEATURE_HW_INTRINSICS |
2057 | |
2058 | default: |
2059 | assert(!"unexpected binary ExOp operator" ); |
2060 | } |
2061 | } |
2062 | |
2063 | op1 = tree->gtOp.gtOp1; |
2064 | GenTree* op2 = tree->gtOp.gtOp2; |
2065 | |
2066 | /* Is there a second sub-operand? */ |
2067 | |
2068 | if (!op2) |
2069 | { |
2070 | /* Special case: no sub-operands at all */ |
2071 | |
2072 | if (!op1) |
2073 | { |
2074 | goto DONE; |
2075 | } |
2076 | |
2077 | /* This is a unary operator */ |
2078 | |
2079 | tree = op1; |
2080 | goto AGAIN; |
2081 | } |
2082 | |
2083 | /* This is a binary operator */ |
2084 | |
2085 | unsigned hsh1 = gtHashValue(op1); |
2086 | |
2087 | /* Add op1's hash to the running value and continue with op2 */ |
2088 | |
2089 | hash = genTreeHashAdd(hash, hsh1); |
2090 | |
2091 | tree = op2; |
2092 | goto AGAIN; |
2093 | } |
2094 | |
2095 | /* See what kind of a special operator we have here */ |
2096 | switch (tree->gtOper) |
2097 | { |
2098 | case GT_FIELD: |
2099 | if (tree->gtField.gtFldObj) |
2100 | { |
2101 | temp = tree->gtField.gtFldObj; |
2102 | assert(temp); |
2103 | hash = genTreeHashAdd(hash, gtHashValue(temp)); |
2104 | } |
2105 | break; |
2106 | |
2107 | case GT_STMT: |
2108 | temp = tree->gtStmt.gtStmtExpr; |
2109 | assert(temp); |
2110 | hash = genTreeHashAdd(hash, gtHashValue(temp)); |
2111 | break; |
2112 | |
2113 | case GT_ARR_ELEM: |
2114 | |
2115 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtArrElem.gtArrObj)); |
2116 | |
2117 | unsigned dim; |
2118 | for (dim = 0; dim < tree->gtArrElem.gtArrRank; dim++) |
2119 | { |
2120 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtArrElem.gtArrInds[dim])); |
2121 | } |
2122 | |
2123 | break; |
2124 | |
2125 | case GT_ARR_OFFSET: |
2126 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtArrOffs.gtOffset)); |
2127 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtArrOffs.gtIndex)); |
2128 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtArrOffs.gtArrObj)); |
2129 | break; |
2130 | |
2131 | case GT_CALL: |
2132 | |
2133 | if (tree->gtCall.gtCallObjp && tree->gtCall.gtCallObjp->gtOper != GT_NOP) |
2134 | { |
2135 | temp = tree->gtCall.gtCallObjp; |
2136 | assert(temp); |
2137 | hash = genTreeHashAdd(hash, gtHashValue(temp)); |
2138 | } |
2139 | |
2140 | if (tree->gtCall.gtCallArgs) |
2141 | { |
2142 | temp = tree->gtCall.gtCallArgs; |
2143 | assert(temp); |
2144 | hash = genTreeHashAdd(hash, gtHashValue(temp)); |
2145 | } |
2146 | |
2147 | if (tree->gtCall.gtCallType == CT_INDIRECT) |
2148 | { |
2149 | temp = tree->gtCall.gtCallAddr; |
2150 | assert(temp); |
2151 | hash = genTreeHashAdd(hash, gtHashValue(temp)); |
2152 | } |
2153 | else |
2154 | { |
2155 | hash = genTreeHashAdd(hash, tree->gtCall.gtCallMethHnd); |
2156 | } |
2157 | |
2158 | if (tree->gtCall.gtCallLateArgs) |
2159 | { |
2160 | temp = tree->gtCall.gtCallLateArgs; |
2161 | assert(temp); |
2162 | hash = genTreeHashAdd(hash, gtHashValue(temp)); |
2163 | } |
2164 | break; |
2165 | |
2166 | case GT_CMPXCHG: |
2167 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtCmpXchg.gtOpLocation)); |
2168 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtCmpXchg.gtOpValue)); |
2169 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtCmpXchg.gtOpComparand)); |
2170 | break; |
2171 | |
2172 | case GT_ARR_BOUNDS_CHECK: |
2173 | #ifdef FEATURE_SIMD |
2174 | case GT_SIMD_CHK: |
2175 | #endif // FEATURE_SIMD |
2176 | #ifdef FEATURE_HW_INTRINSICS |
2177 | case GT_HW_INTRINSIC_CHK: |
2178 | #endif // FEATURE_HW_INTRINSICS |
2179 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtBoundsChk.gtIndex)); |
2180 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtBoundsChk.gtArrLen)); |
2181 | hash = genTreeHashAdd(hash, tree->gtBoundsChk.gtThrowKind); |
2182 | break; |
2183 | |
2184 | case GT_STORE_DYN_BLK: |
2185 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtDynBlk.Data())); |
2186 | __fallthrough; |
2187 | case GT_DYN_BLK: |
2188 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtDynBlk.Addr())); |
2189 | hash = genTreeHashAdd(hash, gtHashValue(tree->gtDynBlk.gtDynamicSize)); |
2190 | break; |
2191 | |
2192 | default: |
2193 | #ifdef DEBUG |
2194 | gtDispTree(tree); |
2195 | #endif |
2196 | assert(!"unexpected operator" ); |
2197 | break; |
2198 | } |
2199 | |
2200 | DONE: |
2201 | |
2202 | return hash; |
2203 | } |
2204 | |
2205 | #endif // DEBUG |
2206 | |
2207 | /***************************************************************************** |
2208 | * |
2209 | * Return a relational operator that is the reverse of the given one. |
2210 | */ |
2211 | |
2212 | /* static */ |
2213 | genTreeOps GenTree::ReverseRelop(genTreeOps relop) |
2214 | { |
2215 | static const genTreeOps reverseOps[] = { |
2216 | GT_NE, // GT_EQ |
2217 | GT_EQ, // GT_NE |
2218 | GT_GE, // GT_LT |
2219 | GT_GT, // GT_LE |
2220 | GT_LT, // GT_GE |
2221 | GT_LE, // GT_GT |
2222 | GT_TEST_NE, // GT_TEST_EQ |
2223 | GT_TEST_EQ, // GT_TEST_NE |
2224 | }; |
2225 | |
2226 | assert(reverseOps[GT_EQ - GT_EQ] == GT_NE); |
2227 | assert(reverseOps[GT_NE - GT_EQ] == GT_EQ); |
2228 | |
2229 | assert(reverseOps[GT_LT - GT_EQ] == GT_GE); |
2230 | assert(reverseOps[GT_LE - GT_EQ] == GT_GT); |
2231 | assert(reverseOps[GT_GE - GT_EQ] == GT_LT); |
2232 | assert(reverseOps[GT_GT - GT_EQ] == GT_LE); |
2233 | |
2234 | assert(reverseOps[GT_TEST_EQ - GT_EQ] == GT_TEST_NE); |
2235 | assert(reverseOps[GT_TEST_NE - GT_EQ] == GT_TEST_EQ); |
2236 | |
2237 | assert(OperIsCompare(relop)); |
2238 | assert(relop >= GT_EQ && (unsigned)(relop - GT_EQ) < sizeof(reverseOps)); |
2239 | |
2240 | return reverseOps[relop - GT_EQ]; |
2241 | } |
2242 | |
2243 | /***************************************************************************** |
2244 | * |
2245 | * Return a relational operator that will work for swapped operands. |
2246 | */ |
2247 | |
2248 | /* static */ |
2249 | genTreeOps GenTree::SwapRelop(genTreeOps relop) |
2250 | { |
2251 | static const genTreeOps swapOps[] = { |
2252 | GT_EQ, // GT_EQ |
2253 | GT_NE, // GT_NE |
2254 | GT_GT, // GT_LT |
2255 | GT_GE, // GT_LE |
2256 | GT_LE, // GT_GE |
2257 | GT_LT, // GT_GT |
2258 | GT_TEST_EQ, // GT_TEST_EQ |
2259 | GT_TEST_NE, // GT_TEST_NE |
2260 | }; |
2261 | |
2262 | assert(swapOps[GT_EQ - GT_EQ] == GT_EQ); |
2263 | assert(swapOps[GT_NE - GT_EQ] == GT_NE); |
2264 | |
2265 | assert(swapOps[GT_LT - GT_EQ] == GT_GT); |
2266 | assert(swapOps[GT_LE - GT_EQ] == GT_GE); |
2267 | assert(swapOps[GT_GE - GT_EQ] == GT_LE); |
2268 | assert(swapOps[GT_GT - GT_EQ] == GT_LT); |
2269 | |
2270 | assert(swapOps[GT_TEST_EQ - GT_EQ] == GT_TEST_EQ); |
2271 | assert(swapOps[GT_TEST_NE - GT_EQ] == GT_TEST_NE); |
2272 | |
2273 | assert(OperIsCompare(relop)); |
2274 | assert(relop >= GT_EQ && (unsigned)(relop - GT_EQ) < sizeof(swapOps)); |
2275 | |
2276 | return swapOps[relop - GT_EQ]; |
2277 | } |
2278 | |
2279 | /***************************************************************************** |
2280 | * |
2281 | * Reverse the meaning of the given test condition. |
2282 | */ |
2283 | |
2284 | GenTree* Compiler::gtReverseCond(GenTree* tree) |
2285 | { |
2286 | if (tree->OperIsCompare()) |
2287 | { |
2288 | tree->SetOper(GenTree::ReverseRelop(tree->OperGet())); |
2289 | |
2290 | // Flip the GTF_RELOP_NAN_UN bit |
2291 | // a ord b === (a != NaN && b != NaN) |
2292 | // a unord b === (a == NaN || b == NaN) |
2293 | // => !(a ord b) === (a unord b) |
2294 | if (varTypeIsFloating(tree->gtOp.gtOp1->TypeGet())) |
2295 | { |
2296 | tree->gtFlags ^= GTF_RELOP_NAN_UN; |
2297 | } |
2298 | } |
2299 | else if (tree->OperIs(GT_JCC, GT_SETCC)) |
2300 | { |
2301 | GenTreeCC* cc = tree->AsCC(); |
2302 | cc->gtCondition = GenTree::ReverseRelop(cc->gtCondition); |
2303 | } |
2304 | else if (tree->OperIs(GT_JCMP)) |
2305 | { |
2306 | // Flip the GTF_JCMP_EQ |
2307 | // |
2308 | // This causes switching |
2309 | // cbz <=> cbnz |
2310 | // tbz <=> tbnz |
2311 | tree->gtFlags ^= GTF_JCMP_EQ; |
2312 | } |
2313 | else |
2314 | { |
2315 | tree = gtNewOperNode(GT_NOT, TYP_INT, tree); |
2316 | } |
2317 | |
2318 | return tree; |
2319 | } |
2320 | |
2321 | /*****************************************************************************/ |
2322 | |
2323 | #ifdef DEBUG |
2324 | |
2325 | bool GenTree::gtIsValid64RsltMul() |
2326 | { |
2327 | if ((gtOper != GT_MUL) || !(gtFlags & GTF_MUL_64RSLT)) |
2328 | { |
2329 | return false; |
2330 | } |
2331 | |
2332 | GenTree* op1 = gtOp.gtOp1; |
2333 | GenTree* op2 = gtOp.gtOp2; |
2334 | |
2335 | if (TypeGet() != TYP_LONG || op1->TypeGet() != TYP_LONG || op2->TypeGet() != TYP_LONG) |
2336 | { |
2337 | return false; |
2338 | } |
2339 | |
2340 | if (gtOverflow()) |
2341 | { |
2342 | return false; |
2343 | } |
2344 | |
2345 | // op1 has to be conv.i8(i4Expr) |
2346 | if ((op1->gtOper != GT_CAST) || (genActualType(op1->CastFromType()) != TYP_INT)) |
2347 | { |
2348 | return false; |
2349 | } |
2350 | |
2351 | // op2 has to be conv.i8(i4Expr) |
2352 | if ((op2->gtOper != GT_CAST) || (genActualType(op2->CastFromType()) != TYP_INT)) |
2353 | { |
2354 | return false; |
2355 | } |
2356 | |
2357 | // The signedness of both casts must be the same |
2358 | if (((op1->gtFlags & GTF_UNSIGNED) != 0) != ((op2->gtFlags & GTF_UNSIGNED) != 0)) |
2359 | { |
2360 | return false; |
2361 | } |
2362 | |
2363 | // Do unsigned mul iff both the casts are unsigned |
2364 | if (((op1->gtFlags & GTF_UNSIGNED) != 0) != ((gtFlags & GTF_UNSIGNED) != 0)) |
2365 | { |
2366 | return false; |
2367 | } |
2368 | |
2369 | return true; |
2370 | } |
2371 | |
2372 | #endif // DEBUG |
2373 | |
2374 | //------------------------------------------------------------------------------ |
2375 | // gtSetListOrder : Figure out the evaluation order for a list of values. |
2376 | // |
2377 | // |
2378 | // Arguments: |
2379 | // list - List to figure out the evaluation order for |
2380 | // isListCallArgs - True iff the list is a list of call arguments |
2381 | // callArgsInRegs - True iff the list is a list of call arguments and they are passed in registers |
2382 | // |
2383 | // Return Value: |
2384 | // True if the operation can be a root of a bitwise rotation tree; false otherwise. |
2385 | |
2386 | unsigned Compiler::gtSetListOrder(GenTree* list, bool isListCallArgs, bool callArgsInRegs) |
2387 | { |
2388 | assert((list != nullptr) && list->OperIsAnyList()); |
2389 | assert(!callArgsInRegs || isListCallArgs); |
2390 | |
2391 | ArrayStack<GenTree*> listNodes(getAllocator(CMK_ArrayStack)); |
2392 | |
2393 | do |
2394 | { |
2395 | listNodes.Push(list); |
2396 | list = list->gtOp.gtOp2; |
2397 | } while ((list != nullptr) && (list->OperIsAnyList())); |
2398 | |
2399 | unsigned nxtlvl = (list == nullptr) ? 0 : gtSetEvalOrder(list); |
2400 | while (!listNodes.Empty()) |
2401 | { |
2402 | list = listNodes.Pop(); |
2403 | assert(list && list->OperIsAnyList()); |
2404 | GenTree* next = list->gtOp.gtOp2; |
2405 | |
2406 | unsigned level = 0; |
2407 | |
2408 | // TODO: Do we have to compute costs differently for argument lists and |
2409 | // all other lists? |
2410 | // https://github.com/dotnet/coreclr/issues/7095 |
2411 | unsigned costSz = (isListCallArgs || (next == nullptr)) ? 0 : 1; |
2412 | unsigned costEx = (isListCallArgs || (next == nullptr)) ? 0 : 1; |
2413 | |
2414 | if (next != nullptr) |
2415 | { |
2416 | if (isListCallArgs) |
2417 | { |
2418 | if (level < nxtlvl) |
2419 | { |
2420 | level = nxtlvl; |
2421 | } |
2422 | } |
2423 | costEx += next->gtCostEx; |
2424 | costSz += next->gtCostSz; |
2425 | } |
2426 | |
2427 | GenTree* op1 = list->gtOp.gtOp1; |
2428 | unsigned lvl = gtSetEvalOrder(op1); |
2429 | |
2430 | // Swap the level counts |
2431 | if (list->gtFlags & GTF_REVERSE_OPS) |
2432 | { |
2433 | unsigned tmpl; |
2434 | |
2435 | tmpl = lvl; |
2436 | lvl = nxtlvl; |
2437 | nxtlvl = tmpl; |
2438 | } |
2439 | |
2440 | // TODO: Do we have to compute levels differently for argument lists and |
2441 | // all other lists? |
2442 | // https://github.com/dotnet/coreclr/issues/7095 |
2443 | if (isListCallArgs) |
2444 | { |
2445 | if (level < lvl) |
2446 | { |
2447 | level = lvl; |
2448 | } |
2449 | } |
2450 | else |
2451 | { |
2452 | if (lvl < 1) |
2453 | { |
2454 | level = nxtlvl; |
2455 | } |
2456 | else if (lvl == nxtlvl) |
2457 | { |
2458 | level = lvl + 1; |
2459 | } |
2460 | else |
2461 | { |
2462 | level = lvl; |
2463 | } |
2464 | } |
2465 | |
2466 | if (op1->gtCostEx != 0) |
2467 | { |
2468 | costEx += op1->gtCostEx; |
2469 | costEx += (callArgsInRegs || !isListCallArgs) ? 0 : IND_COST_EX; |
2470 | } |
2471 | |
2472 | if (op1->gtCostSz != 0) |
2473 | { |
2474 | costSz += op1->gtCostSz; |
2475 | #ifdef _TARGET_XARCH_ |
2476 | if (callArgsInRegs) // push is smaller than mov to reg |
2477 | #endif |
2478 | { |
2479 | costSz += 1; |
2480 | } |
2481 | } |
2482 | |
2483 | list->SetCosts(costEx, costSz); |
2484 | |
2485 | nxtlvl = level; |
2486 | } |
2487 | |
2488 | return nxtlvl; |
2489 | } |
2490 | |
2491 | //----------------------------------------------------------------------------- |
2492 | // gtWalkOp: Traverse and mark an address expression |
2493 | // |
2494 | // Arguments: |
2495 | // op1WB - An out parameter which is either the address expression, or one |
2496 | // of its operands. |
2497 | // op2WB - An out parameter which starts as either null or one of the operands |
2498 | // of the address expression. |
2499 | // base - The base address of the addressing mode, or null if 'constOnly' is false |
2500 | // constOnly - True if we will only traverse into ADDs with constant op2. |
2501 | // |
2502 | // This routine is a helper routine for gtSetEvalOrder() and is used to identify the |
2503 | // base and index nodes, which will be validated against those identified by |
2504 | // genCreateAddrMode(). |
2505 | // It also marks the ADD nodes involved in the address expression with the |
2506 | // GTF_ADDRMODE_NO_CSE flag which prevents them from being considered for CSE's. |
2507 | // |
2508 | // Its two output parameters are modified under the following conditions: |
2509 | // |
2510 | // It is called once with the original address expression as 'op1WB', and |
2511 | // with 'constOnly' set to false. On this first invocation, *op1WB is always |
2512 | // an ADD node, and it will consider the operands of the ADD even if its op2 is |
2513 | // not a constant. However, when it encounters a non-constant or the base in the |
2514 | // op2 position, it stops iterating. That operand is returned in the 'op2WB' out |
2515 | // parameter, and will be considered on the third invocation of this method if |
2516 | // it is an ADD. |
2517 | // |
2518 | // It is called the second time with the two operands of the original expression, in |
2519 | // the original order, and the third time in reverse order. For these invocations |
2520 | // 'constOnly' is true, so it will only traverse cascaded ADD nodes if they have a |
2521 | // constant op2. |
2522 | // |
2523 | // The result, after three invocations, is that the values of the two out parameters |
2524 | // correspond to the base and index in some fashion. This method doesn't attempt |
2525 | // to determine or validate the scale or offset, if any. |
2526 | // |
2527 | // Assumptions (presumed to be ensured by genCreateAddrMode()): |
2528 | // If an ADD has a constant operand, it is in the op2 position. |
2529 | // |
2530 | // Notes: |
2531 | // This method, and its invocation sequence, are quite confusing, and since they |
2532 | // were not originally well-documented, this specification is a possibly-imperfect |
2533 | // reconstruction. |
2534 | // The motivation for the handling of the NOP case is unclear. |
2535 | // Note that 'op2WB' is only modified in the initial (!constOnly) case, |
2536 | // or if a NOP is encountered in the op1 position. |
2537 | // |
2538 | void Compiler::gtWalkOp(GenTree** op1WB, GenTree** op2WB, GenTree* base, bool constOnly) |
2539 | { |
2540 | GenTree* op1 = *op1WB; |
2541 | GenTree* op2 = *op2WB; |
2542 | |
2543 | op1 = op1->gtEffectiveVal(); |
2544 | |
2545 | // Now we look for op1's with non-overflow GT_ADDs [of constants] |
2546 | while ((op1->gtOper == GT_ADD) && (!op1->gtOverflow()) && (!constOnly || (op1->gtOp.gtOp2->IsCnsIntOrI()))) |
2547 | { |
2548 | // mark it with GTF_ADDRMODE_NO_CSE |
2549 | op1->gtFlags |= GTF_ADDRMODE_NO_CSE; |
2550 | |
2551 | if (!constOnly) |
2552 | { |
2553 | op2 = op1->gtOp.gtOp2; |
2554 | } |
2555 | op1 = op1->gtOp.gtOp1; |
2556 | |
2557 | // If op1 is a GT_NOP then swap op1 and op2. |
2558 | // (Why? Also, presumably op2 is not a GT_NOP in this case?) |
2559 | if (op1->gtOper == GT_NOP) |
2560 | { |
2561 | GenTree* tmp; |
2562 | |
2563 | tmp = op1; |
2564 | op1 = op2; |
2565 | op2 = tmp; |
2566 | } |
2567 | |
2568 | if (!constOnly && ((op2 == base) || (!op2->IsCnsIntOrI()))) |
2569 | { |
2570 | break; |
2571 | } |
2572 | |
2573 | op1 = op1->gtEffectiveVal(); |
2574 | } |
2575 | |
2576 | *op1WB = op1; |
2577 | *op2WB = op2; |
2578 | } |
2579 | |
2580 | #ifdef DEBUG |
2581 | /***************************************************************************** |
2582 | * This is a workaround. It is to help implement an assert in gtSetEvalOrder() that the values |
2583 | * gtWalkOp() leaves in op1 and op2 correspond with the values of adr, idx, mul, and cns |
2584 | * that are returned by genCreateAddrMode(). It's essentially impossible to determine |
2585 | * what gtWalkOp() *should* return for all possible trees. This simply loosens one assert |
2586 | * to handle the following case: |
2587 | |
2588 | indir int |
2589 | const(h) int 4 field |
2590 | + byref |
2591 | lclVar byref V00 this <-- op2 |
2592 | comma byref <-- adr (base) |
2593 | indir byte |
2594 | lclVar byref V00 this |
2595 | + byref |
2596 | const int 2 <-- mul == 4 |
2597 | << int <-- op1 |
2598 | lclVar int V01 arg1 <-- idx |
2599 | |
2600 | * Here, we are planning to generate the address mode [edx+4*eax], where eax = idx and edx = the GT_COMMA expression. |
2601 | * To check adr equivalence with op2, we need to walk down the GT_ADD tree just like gtWalkOp() does. |
2602 | */ |
2603 | GenTree* Compiler::gtWalkOpEffectiveVal(GenTree* op) |
2604 | { |
2605 | for (;;) |
2606 | { |
2607 | op = op->gtEffectiveVal(); |
2608 | |
2609 | if ((op->gtOper != GT_ADD) || op->gtOverflow() || !op->gtOp.gtOp2->IsCnsIntOrI()) |
2610 | { |
2611 | break; |
2612 | } |
2613 | |
2614 | op = op->gtOp.gtOp1; |
2615 | } |
2616 | |
2617 | return op; |
2618 | } |
2619 | #endif // DEBUG |
2620 | |
2621 | /***************************************************************************** |
2622 | * |
2623 | * Given a tree, set the gtCostEx and gtCostSz fields which |
2624 | * are used to measure the relative costs of the codegen of the tree |
2625 | * |
2626 | */ |
2627 | |
2628 | void Compiler::gtPrepareCost(GenTree* tree) |
2629 | { |
2630 | gtSetEvalOrder(tree); |
2631 | } |
2632 | |
2633 | bool Compiler::gtIsLikelyRegVar(GenTree* tree) |
2634 | { |
2635 | if (tree->gtOper != GT_LCL_VAR) |
2636 | { |
2637 | return false; |
2638 | } |
2639 | |
2640 | assert(tree->gtLclVar.gtLclNum < lvaTableCnt); |
2641 | LclVarDsc* varDsc = lvaTable + tree->gtLclVar.gtLclNum; |
2642 | |
2643 | if (varDsc->lvDoNotEnregister) |
2644 | { |
2645 | return false; |
2646 | } |
2647 | |
2648 | // Be pessimistic if ref counts are not yet set up. |
2649 | // |
2650 | // Perhaps we should be optimistic though. |
2651 | // See notes in GitHub issue 18969. |
2652 | if (!lvaLocalVarRefCounted()) |
2653 | { |
2654 | return false; |
2655 | } |
2656 | |
2657 | if (varDsc->lvRefCntWtd() < (BB_UNITY_WEIGHT * 3)) |
2658 | { |
2659 | return false; |
2660 | } |
2661 | |
2662 | #ifdef _TARGET_X86_ |
2663 | if (varTypeIsFloating(tree->TypeGet())) |
2664 | return false; |
2665 | if (varTypeIsLong(tree->TypeGet())) |
2666 | return false; |
2667 | #endif |
2668 | |
2669 | return true; |
2670 | } |
2671 | |
2672 | //------------------------------------------------------------------------ |
2673 | // gtCanSwapOrder: Returns true iff the secondNode can be swapped with firstNode. |
2674 | // |
2675 | // Arguments: |
2676 | // firstNode - An operand of a tree that can have GTF_REVERSE_OPS set. |
2677 | // secondNode - The other operand of the tree. |
2678 | // |
2679 | // Return Value: |
2680 | // Returns a boolean indicating whether it is safe to reverse the execution |
2681 | // order of the two trees, considering any exception, global effects, or |
2682 | // ordering constraints. |
2683 | // |
2684 | bool Compiler::gtCanSwapOrder(GenTree* firstNode, GenTree* secondNode) |
2685 | { |
2686 | // Relative of order of global / side effects can't be swapped. |
2687 | |
2688 | bool canSwap = true; |
2689 | |
2690 | if (optValnumCSE_phase) |
2691 | { |
2692 | canSwap = optCSE_canSwap(firstNode, secondNode); |
2693 | } |
2694 | |
2695 | // We cannot swap in the presence of special side effects such as GT_CATCH_ARG. |
2696 | |
2697 | if (canSwap && (firstNode->gtFlags & GTF_ORDER_SIDEEFF)) |
2698 | { |
2699 | canSwap = false; |
2700 | } |
2701 | |
2702 | // When strict side effect order is disabled we allow GTF_REVERSE_OPS to be set |
2703 | // when one or both sides contains a GTF_CALL or GTF_EXCEPT. |
2704 | // Currently only the C and C++ languages allow non strict side effect order. |
2705 | |
2706 | unsigned strictEffects = GTF_GLOB_EFFECT; |
2707 | |
2708 | if (canSwap && (firstNode->gtFlags & strictEffects)) |
2709 | { |
2710 | // op1 has side efects that can't be reordered. |
2711 | // Check for some special cases where we still may be able to swap. |
2712 | |
2713 | if (secondNode->gtFlags & strictEffects) |
2714 | { |
2715 | // op2 has also has non reorderable side effects - can't swap. |
2716 | canSwap = false; |
2717 | } |
2718 | else |
2719 | { |
2720 | // No side effects in op2 - we can swap iff op1 has no way of modifying op2, |
2721 | // i.e. through byref assignments or calls or op2 is a constant. |
2722 | |
2723 | if (firstNode->gtFlags & strictEffects & GTF_PERSISTENT_SIDE_EFFECTS) |
2724 | { |
2725 | // We have to be conservative - can swap iff op2 is constant. |
2726 | if (!secondNode->OperIsConst()) |
2727 | { |
2728 | canSwap = false; |
2729 | } |
2730 | } |
2731 | } |
2732 | } |
2733 | return canSwap; |
2734 | } |
2735 | |
2736 | /***************************************************************************** |
2737 | * |
2738 | * Given a tree, figure out the order in which its sub-operands should be |
2739 | * evaluated. If the second operand of a binary operator is more expensive |
2740 | * than the first operand, then try to swap the operand trees. Updates the |
2741 | * GTF_REVERSE_OPS bit if necessary in this case. |
2742 | * |
2743 | * Returns the Sethi 'complexity' estimate for this tree (the higher |
2744 | * the number, the higher is the tree's resources requirement). |
2745 | * |
2746 | * This function sets: |
2747 | * 1. gtCostEx to the execution complexity estimate |
2748 | * 2. gtCostSz to the code size estimate |
2749 | * 3. Sometimes sets GTF_ADDRMODE_NO_CSE on nodes in the tree. |
2750 | * 4. DEBUG-only: clears GTF_DEBUG_NODE_MORPHED. |
2751 | */ |
2752 | |
2753 | #ifdef _PREFAST_ |
2754 | #pragma warning(push) |
2755 | #pragma warning(disable : 21000) // Suppress PREFast warning about overly large function |
2756 | #endif |
2757 | unsigned Compiler::gtSetEvalOrder(GenTree* tree) |
2758 | { |
2759 | assert(tree); |
2760 | assert(tree->gtOper != GT_STMT); |
2761 | |
2762 | #ifdef DEBUG |
2763 | /* Clear the GTF_DEBUG_NODE_MORPHED flag as well */ |
2764 | tree->gtDebugFlags &= ~GTF_DEBUG_NODE_MORPHED; |
2765 | #endif |
2766 | |
2767 | /* Is this a FP value? */ |
2768 | |
2769 | bool isflt = varTypeIsFloating(tree->TypeGet()); |
2770 | |
2771 | /* Figure out what kind of a node we have */ |
2772 | |
2773 | const genTreeOps oper = tree->OperGet(); |
2774 | const unsigned kind = tree->OperKind(); |
2775 | |
2776 | /* Assume no fixed registers will be trashed */ |
2777 | |
2778 | unsigned level; |
2779 | int costEx; |
2780 | int costSz; |
2781 | |
2782 | #ifdef DEBUG |
2783 | costEx = -1; |
2784 | costSz = -1; |
2785 | #endif |
2786 | |
2787 | /* Is this a constant or a leaf node? */ |
2788 | |
2789 | if (kind & (GTK_LEAF | GTK_CONST)) |
2790 | { |
2791 | switch (oper) |
2792 | { |
2793 | #ifdef _TARGET_ARM_ |
2794 | case GT_CNS_LNG: |
2795 | costSz = 9; |
2796 | costEx = 4; |
2797 | goto COMMON_CNS; |
2798 | |
2799 | case GT_CNS_STR: |
2800 | // Uses movw/movt |
2801 | costSz = 7; |
2802 | costEx = 3; |
2803 | goto COMMON_CNS; |
2804 | |
2805 | case GT_CNS_INT: |
2806 | { |
2807 | // If the constant is a handle then it will need to have a relocation |
2808 | // applied to it. |
2809 | // Any constant that requires a reloc must use the movw/movt sequence |
2810 | // |
2811 | GenTreeIntConCommon* con = tree->AsIntConCommon(); |
2812 | |
2813 | if (con->ImmedValNeedsReloc(this) || |
2814 | !codeGen->validImmForInstr(INS_mov, (target_ssize_t)tree->gtIntCon.gtIconVal)) |
2815 | { |
2816 | // Uses movw/movt |
2817 | costSz = 7; |
2818 | costEx = 3; |
2819 | } |
2820 | else if (((unsigned)tree->gtIntCon.gtIconVal) <= 0x00ff) |
2821 | { |
2822 | // mov Rd, <const8> |
2823 | costSz = 1; |
2824 | costEx = 1; |
2825 | } |
2826 | else |
2827 | { |
2828 | // Uses movw/mvn |
2829 | costSz = 3; |
2830 | costEx = 1; |
2831 | } |
2832 | goto COMMON_CNS; |
2833 | } |
2834 | |
2835 | #elif defined _TARGET_XARCH_ |
2836 | |
2837 | case GT_CNS_LNG: |
2838 | costSz = 10; |
2839 | costEx = 3; |
2840 | goto COMMON_CNS; |
2841 | |
2842 | case GT_CNS_STR: |
2843 | costSz = 4; |
2844 | costEx = 1; |
2845 | goto COMMON_CNS; |
2846 | |
2847 | case GT_CNS_INT: |
2848 | { |
2849 | // If the constant is a handle then it will need to have a relocation |
2850 | // applied to it. |
2851 | // |
2852 | GenTreeIntConCommon* con = tree->AsIntConCommon(); |
2853 | |
2854 | bool iconNeedsReloc = con->ImmedValNeedsReloc(this); |
2855 | |
2856 | if (!iconNeedsReloc && con->FitsInI8()) |
2857 | { |
2858 | costSz = 1; |
2859 | costEx = 1; |
2860 | } |
2861 | #if defined(_TARGET_AMD64_) |
2862 | else if (iconNeedsReloc || !con->FitsInI32()) |
2863 | { |
2864 | costSz = 10; |
2865 | costEx = 3; |
2866 | } |
2867 | #endif // _TARGET_AMD64_ |
2868 | else |
2869 | { |
2870 | costSz = 4; |
2871 | costEx = 1; |
2872 | } |
2873 | goto COMMON_CNS; |
2874 | } |
2875 | |
2876 | #elif defined(_TARGET_ARM64_) |
2877 | case GT_CNS_LNG: |
2878 | case GT_CNS_STR: |
2879 | case GT_CNS_INT: |
2880 | // TODO-ARM64-NYI: Need cost estimates. |
2881 | costSz = 1; |
2882 | costEx = 1; |
2883 | goto COMMON_CNS; |
2884 | |
2885 | #else |
2886 | case GT_CNS_LNG: |
2887 | case GT_CNS_STR: |
2888 | case GT_CNS_INT: |
2889 | #error "Unknown _TARGET_" |
2890 | #endif |
2891 | |
2892 | COMMON_CNS: |
2893 | /* |
2894 | Note that some code below depends on constants always getting |
2895 | moved to be the second operand of a binary operator. This is |
2896 | easily accomplished by giving constants a level of 0, which |
2897 | we do on the next line. If you ever decide to change this, be |
2898 | aware that unless you make other arrangements for integer |
2899 | constants to be moved, stuff will break. |
2900 | */ |
2901 | |
2902 | level = 0; |
2903 | break; |
2904 | |
2905 | case GT_CNS_DBL: |
2906 | level = 0; |
2907 | /* We use fldz and fld1 to load 0.0 and 1.0, but all other */ |
2908 | /* floating point constants are loaded using an indirection */ |
2909 | if ((*((__int64*)&(tree->gtDblCon.gtDconVal)) == 0) || |
2910 | (*((__int64*)&(tree->gtDblCon.gtDconVal)) == I64(0x3ff0000000000000))) |
2911 | { |
2912 | costEx = 1; |
2913 | costSz = 1; |
2914 | } |
2915 | else |
2916 | { |
2917 | costEx = IND_COST_EX; |
2918 | costSz = 4; |
2919 | } |
2920 | break; |
2921 | |
2922 | case GT_LCL_VAR: |
2923 | level = 1; |
2924 | if (gtIsLikelyRegVar(tree)) |
2925 | { |
2926 | costEx = 1; |
2927 | costSz = 1; |
2928 | /* Sign-extend and zero-extend are more expensive to load */ |
2929 | if (lvaTable[tree->gtLclVar.gtLclNum].lvNormalizeOnLoad()) |
2930 | { |
2931 | costEx += 1; |
2932 | costSz += 1; |
2933 | } |
2934 | } |
2935 | else |
2936 | { |
2937 | costEx = IND_COST_EX; |
2938 | costSz = 2; |
2939 | /* Sign-extend and zero-extend are more expensive to load */ |
2940 | if (varTypeIsSmall(tree->TypeGet())) |
2941 | { |
2942 | costEx += 1; |
2943 | costSz += 1; |
2944 | } |
2945 | } |
2946 | #if defined(_TARGET_AMD64_) |
2947 | // increase costSz for floating point locals |
2948 | if (isflt) |
2949 | { |
2950 | costSz += 1; |
2951 | if (!gtIsLikelyRegVar(tree)) |
2952 | { |
2953 | costSz += 1; |
2954 | } |
2955 | } |
2956 | #endif |
2957 | break; |
2958 | |
2959 | case GT_CLS_VAR: |
2960 | #ifdef _TARGET_ARM_ |
2961 | // We generate movw/movt/ldr |
2962 | level = 1; |
2963 | costEx = 3 + IND_COST_EX; // 6 |
2964 | costSz = 4 + 4 + 2; // 10 |
2965 | break; |
2966 | #endif |
2967 | case GT_LCL_FLD: |
2968 | level = 1; |
2969 | costEx = IND_COST_EX; |
2970 | costSz = 4; |
2971 | if (varTypeIsSmall(tree->TypeGet())) |
2972 | { |
2973 | costEx += 1; |
2974 | costSz += 1; |
2975 | } |
2976 | break; |
2977 | |
2978 | case GT_PHI_ARG: |
2979 | case GT_ARGPLACE: |
2980 | level = 0; |
2981 | costEx = 0; |
2982 | costSz = 0; |
2983 | break; |
2984 | |
2985 | default: |
2986 | level = 1; |
2987 | costEx = 1; |
2988 | costSz = 1; |
2989 | break; |
2990 | } |
2991 | goto DONE; |
2992 | } |
2993 | |
2994 | /* Is it a 'simple' unary/binary operator? */ |
2995 | |
2996 | if (kind & GTK_SMPOP) |
2997 | { |
2998 | int lvlb; // preference for op2 |
2999 | unsigned lvl2; // scratch variable |
3000 | |
3001 | GenTree* op1 = tree->gtOp.gtOp1; |
3002 | GenTree* op2 = tree->gtGetOp2IfPresent(); |
3003 | |
3004 | costEx = 0; |
3005 | costSz = 0; |
3006 | |
3007 | if (tree->OperIsAddrMode()) |
3008 | { |
3009 | if (op1 == nullptr) |
3010 | { |
3011 | op1 = op2; |
3012 | op2 = nullptr; |
3013 | } |
3014 | } |
3015 | |
3016 | /* Check for a nilary operator */ |
3017 | |
3018 | if (op1 == nullptr) |
3019 | { |
3020 | assert(op2 == nullptr); |
3021 | |
3022 | level = 0; |
3023 | |
3024 | goto DONE; |
3025 | } |
3026 | |
3027 | /* Is this a unary operator? */ |
3028 | |
3029 | if (op2 == nullptr) |
3030 | { |
3031 | /* Process the operand of the operator */ |
3032 | |
3033 | /* Most Unary ops have costEx of 1 */ |
3034 | costEx = 1; |
3035 | costSz = 1; |
3036 | |
3037 | level = gtSetEvalOrder(op1); |
3038 | |
3039 | /* Special handling for some operators */ |
3040 | |
3041 | switch (oper) |
3042 | { |
3043 | case GT_JTRUE: |
3044 | costEx = 2; |
3045 | costSz = 2; |
3046 | break; |
3047 | |
3048 | case GT_SWITCH: |
3049 | costEx = 10; |
3050 | costSz = 5; |
3051 | break; |
3052 | |
3053 | case GT_CAST: |
3054 | #if defined(_TARGET_ARM_) |
3055 | costEx = 1; |
3056 | costSz = 1; |
3057 | if (isflt || varTypeIsFloating(op1->TypeGet())) |
3058 | { |
3059 | costEx = 3; |
3060 | costSz = 4; |
3061 | } |
3062 | #elif defined(_TARGET_ARM64_) |
3063 | costEx = 1; |
3064 | costSz = 2; |
3065 | if (isflt || varTypeIsFloating(op1->TypeGet())) |
3066 | { |
3067 | costEx = 2; |
3068 | costSz = 4; |
3069 | } |
3070 | #elif defined(_TARGET_XARCH_) |
3071 | costEx = 1; |
3072 | costSz = 2; |
3073 | |
3074 | if (isflt || varTypeIsFloating(op1->TypeGet())) |
3075 | { |
3076 | /* cast involving floats always go through memory */ |
3077 | costEx = IND_COST_EX * 2; |
3078 | costSz = 6; |
3079 | } |
3080 | #else |
3081 | #error "Unknown _TARGET_" |
3082 | #endif |
3083 | |
3084 | /* Overflow casts are a lot more expensive */ |
3085 | if (tree->gtOverflow()) |
3086 | { |
3087 | costEx += 6; |
3088 | costSz += 6; |
3089 | } |
3090 | |
3091 | break; |
3092 | |
3093 | case GT_LIST: |
3094 | case GT_FIELD_LIST: |
3095 | case GT_NOP: |
3096 | costEx = 0; |
3097 | costSz = 0; |
3098 | break; |
3099 | |
3100 | case GT_INTRINSIC: |
3101 | // GT_INTRINSIC intrinsics Sin, Cos, Sqrt, Abs ... have higher costs. |
3102 | // TODO: tune these costs target specific as some of these are |
3103 | // target intrinsics and would cost less to generate code. |
3104 | switch (tree->gtIntrinsic.gtIntrinsicId) |
3105 | { |
3106 | default: |
3107 | assert(!"missing case for gtIntrinsicId" ); |
3108 | costEx = 12; |
3109 | costSz = 12; |
3110 | break; |
3111 | |
3112 | case CORINFO_INTRINSIC_Sin: |
3113 | case CORINFO_INTRINSIC_Cos: |
3114 | case CORINFO_INTRINSIC_Sqrt: |
3115 | case CORINFO_INTRINSIC_Cbrt: |
3116 | case CORINFO_INTRINSIC_Cosh: |
3117 | case CORINFO_INTRINSIC_Sinh: |
3118 | case CORINFO_INTRINSIC_Tan: |
3119 | case CORINFO_INTRINSIC_Tanh: |
3120 | case CORINFO_INTRINSIC_Asin: |
3121 | case CORINFO_INTRINSIC_Asinh: |
3122 | case CORINFO_INTRINSIC_Acos: |
3123 | case CORINFO_INTRINSIC_Acosh: |
3124 | case CORINFO_INTRINSIC_Atan: |
3125 | case CORINFO_INTRINSIC_Atanh: |
3126 | case CORINFO_INTRINSIC_Atan2: |
3127 | case CORINFO_INTRINSIC_Log10: |
3128 | case CORINFO_INTRINSIC_Pow: |
3129 | case CORINFO_INTRINSIC_Exp: |
3130 | case CORINFO_INTRINSIC_Ceiling: |
3131 | case CORINFO_INTRINSIC_Floor: |
3132 | case CORINFO_INTRINSIC_Object_GetType: |
3133 | // Giving intrinsics a large fixed execution cost is because we'd like to CSE |
3134 | // them, even if they are implemented by calls. This is different from modeling |
3135 | // user calls since we never CSE user calls. |
3136 | costEx = 36; |
3137 | costSz = 4; |
3138 | break; |
3139 | |
3140 | case CORINFO_INTRINSIC_Abs: |
3141 | costEx = 5; |
3142 | costSz = 15; |
3143 | break; |
3144 | |
3145 | case CORINFO_INTRINSIC_Round: |
3146 | costEx = 3; |
3147 | costSz = 4; |
3148 | break; |
3149 | } |
3150 | level++; |
3151 | break; |
3152 | |
3153 | case GT_NOT: |
3154 | case GT_NEG: |
3155 | // We need to ensure that -x is evaluated before x or else |
3156 | // we get burned while adjusting genFPstkLevel in x*-x where |
3157 | // the rhs x is the last use of the enregistered x. |
3158 | // |
3159 | // Even in the integer case we want to prefer to |
3160 | // evaluate the side without the GT_NEG node, all other things |
3161 | // being equal. Also a GT_NOT requires a scratch register |
3162 | |
3163 | level++; |
3164 | break; |
3165 | |
3166 | case GT_ADDR: |
3167 | |
3168 | costEx = 0; |
3169 | costSz = 1; |
3170 | |
3171 | // If we have a GT_ADDR of an GT_IND we can just copy the costs from indOp1 |
3172 | if (op1->OperGet() == GT_IND) |
3173 | { |
3174 | GenTree* indOp1 = op1->gtOp.gtOp1; |
3175 | costEx = indOp1->gtCostEx; |
3176 | costSz = indOp1->gtCostSz; |
3177 | } |
3178 | break; |
3179 | |
3180 | case GT_ARR_LENGTH: |
3181 | level++; |
3182 | |
3183 | /* Array Len should be the same as an indirections, which have a costEx of IND_COST_EX */ |
3184 | costEx = IND_COST_EX - 1; |
3185 | costSz = 2; |
3186 | break; |
3187 | |
3188 | case GT_MKREFANY: |
3189 | case GT_OBJ: |
3190 | // We estimate the cost of a GT_OBJ or GT_MKREFANY to be two loads (GT_INDs) |
3191 | costEx = 2 * IND_COST_EX; |
3192 | costSz = 2 * 2; |
3193 | break; |
3194 | |
3195 | case GT_BOX: |
3196 | // We estimate the cost of a GT_BOX to be two stores (GT_INDs) |
3197 | costEx = 2 * IND_COST_EX; |
3198 | costSz = 2 * 2; |
3199 | break; |
3200 | |
3201 | case GT_BLK: |
3202 | case GT_IND: |
3203 | |
3204 | /* An indirection should always have a non-zero level. |
3205 | * Only constant leaf nodes have level 0. |
3206 | */ |
3207 | |
3208 | if (level == 0) |
3209 | { |
3210 | level = 1; |
3211 | } |
3212 | |
3213 | /* Indirections have a costEx of IND_COST_EX */ |
3214 | costEx = IND_COST_EX; |
3215 | costSz = 2; |
3216 | |
3217 | /* If we have to sign-extend or zero-extend, bump the cost */ |
3218 | if (varTypeIsSmall(tree->TypeGet())) |
3219 | { |
3220 | costEx += 1; |
3221 | costSz += 1; |
3222 | } |
3223 | |
3224 | if (isflt) |
3225 | { |
3226 | if (tree->TypeGet() == TYP_DOUBLE) |
3227 | { |
3228 | costEx += 1; |
3229 | } |
3230 | #ifdef _TARGET_ARM_ |
3231 | costSz += 2; |
3232 | #endif // _TARGET_ARM_ |
3233 | } |
3234 | |
3235 | // Can we form an addressing mode with this indirection? |
3236 | // TODO-CQ: Consider changing this to op1->gtEffectiveVal() to take into account |
3237 | // addressing modes hidden under a comma node. |
3238 | |
3239 | if (op1->gtOper == GT_ADD) |
3240 | { |
3241 | bool rev; |
3242 | #if SCALED_ADDR_MODES |
3243 | unsigned mul; |
3244 | #endif // SCALED_ADDR_MODES |
3245 | ssize_t cns; |
3246 | GenTree* base; |
3247 | GenTree* idx; |
3248 | |
3249 | // See if we can form a complex addressing mode. |
3250 | |
3251 | GenTree* addr = op1->gtEffectiveVal(); |
3252 | |
3253 | bool doAddrMode = true; |
3254 | // See if we can form a complex addressing mode. |
3255 | // Always use an addrMode for an array index indirection. |
3256 | // TODO-1stClassStructs: Always do this, but first make sure it's |
3257 | // done in Lowering as well. |
3258 | if ((tree->gtFlags & GTF_IND_ARR_INDEX) == 0) |
3259 | { |
3260 | if (tree->TypeGet() == TYP_STRUCT) |
3261 | { |
3262 | doAddrMode = false; |
3263 | } |
3264 | else if (varTypeIsStruct(tree)) |
3265 | { |
3266 | // This is a heuristic attempting to match prior behavior when indirections |
3267 | // under a struct assignment would not be considered for addressing modes. |
3268 | if (compCurStmt != nullptr) |
3269 | { |
3270 | GenTree* expr = compCurStmt->gtStmt.gtStmtExpr; |
3271 | if ((expr->OperGet() == GT_ASG) && |
3272 | ((expr->gtGetOp1() == tree) || (expr->gtGetOp2() == tree))) |
3273 | { |
3274 | doAddrMode = false; |
3275 | } |
3276 | } |
3277 | } |
3278 | } |
3279 | if ((doAddrMode) && |
3280 | codeGen->genCreateAddrMode(addr, // address |
3281 | false, // fold |
3282 | &rev, // reverse ops |
3283 | &base, // base addr |
3284 | &idx, // index val |
3285 | #if SCALED_ADDR_MODES |
3286 | &mul, // scaling |
3287 | #endif // SCALED_ADDR_MODES |
3288 | &cns)) // displacement |
3289 | { |
3290 | // We can form a complex addressing mode, so mark each of the interior |
3291 | // nodes with GTF_ADDRMODE_NO_CSE and calculate a more accurate cost. |
3292 | |
3293 | addr->gtFlags |= GTF_ADDRMODE_NO_CSE; |
3294 | #ifdef _TARGET_XARCH_ |
3295 | // addrmodeCount is the count of items that we used to form |
3296 | // an addressing mode. The maximum value is 4 when we have |
3297 | // all of these: { base, idx, cns, mul } |
3298 | // |
3299 | unsigned addrmodeCount = 0; |
3300 | if (base) |
3301 | { |
3302 | costEx += base->gtCostEx; |
3303 | costSz += base->gtCostSz; |
3304 | addrmodeCount++; |
3305 | } |
3306 | |
3307 | if (idx) |
3308 | { |
3309 | costEx += idx->gtCostEx; |
3310 | costSz += idx->gtCostSz; |
3311 | addrmodeCount++; |
3312 | } |
3313 | |
3314 | if (cns) |
3315 | { |
3316 | if (((signed char)cns) == ((int)cns)) |
3317 | { |
3318 | costSz += 1; |
3319 | } |
3320 | else |
3321 | { |
3322 | costSz += 4; |
3323 | } |
3324 | addrmodeCount++; |
3325 | } |
3326 | if (mul) |
3327 | { |
3328 | addrmodeCount++; |
3329 | } |
3330 | // When we form a complex addressing mode we can reduced the costs |
3331 | // associated with the interior GT_ADD and GT_LSH nodes: |
3332 | // |
3333 | // GT_ADD -- reduce this interior GT_ADD by (-3,-3) |
3334 | // / \ -- |
3335 | // GT_ADD 'cns' -- reduce this interior GT_ADD by (-2,-2) |
3336 | // / \ -- |
3337 | // 'base' GT_LSL -- reduce this interior GT_LSL by (-1,-1) |
3338 | // / \ -- |
3339 | // 'idx' 'mul' |
3340 | // |
3341 | if (addrmodeCount > 1) |
3342 | { |
3343 | // The number of interior GT_ADD and GT_LSL will always be one less than addrmodeCount |
3344 | // |
3345 | addrmodeCount--; |
3346 | |
3347 | GenTree* tmp = addr; |
3348 | while (addrmodeCount > 0) |
3349 | { |
3350 | // decrement the gtCosts for the interior GT_ADD or GT_LSH node by the remaining |
3351 | // addrmodeCount |
3352 | tmp->SetCosts(tmp->gtCostEx - addrmodeCount, tmp->gtCostSz - addrmodeCount); |
3353 | |
3354 | addrmodeCount--; |
3355 | if (addrmodeCount > 0) |
3356 | { |
3357 | GenTree* tmpOp1 = tmp->gtOp.gtOp1; |
3358 | GenTree* tmpOp2 = tmp->gtGetOp2(); |
3359 | assert(tmpOp2 != nullptr); |
3360 | |
3361 | if ((tmpOp1 != base) && (tmpOp1->OperGet() == GT_ADD)) |
3362 | { |
3363 | tmp = tmpOp1; |
3364 | } |
3365 | else if (tmpOp2->OperGet() == GT_LSH) |
3366 | { |
3367 | tmp = tmpOp2; |
3368 | } |
3369 | else if (tmpOp1->OperGet() == GT_LSH) |
3370 | { |
3371 | tmp = tmpOp1; |
3372 | } |
3373 | else if (tmpOp2->OperGet() == GT_ADD) |
3374 | { |
3375 | tmp = tmpOp2; |
3376 | } |
3377 | else |
3378 | { |
3379 | // We can very rarely encounter a tree that has a GT_COMMA node |
3380 | // that is difficult to walk, so we just early out without decrementing. |
3381 | addrmodeCount = 0; |
3382 | } |
3383 | } |
3384 | } |
3385 | } |
3386 | #elif defined _TARGET_ARM_ |
3387 | if (base) |
3388 | { |
3389 | costEx += base->gtCostEx; |
3390 | costSz += base->gtCostSz; |
3391 | if ((base->gtOper == GT_LCL_VAR) && ((idx == NULL) || (cns == 0))) |
3392 | { |
3393 | costSz -= 1; |
3394 | } |
3395 | } |
3396 | |
3397 | if (idx) |
3398 | { |
3399 | costEx += idx->gtCostEx; |
3400 | costSz += idx->gtCostSz; |
3401 | if (mul > 0) |
3402 | { |
3403 | costSz += 2; |
3404 | } |
3405 | } |
3406 | |
3407 | if (cns) |
3408 | { |
3409 | if (cns >= 128) // small offsets fits into a 16-bit instruction |
3410 | { |
3411 | if (cns < 4096) // medium offsets require a 32-bit instruction |
3412 | { |
3413 | if (!isflt) |
3414 | costSz += 2; |
3415 | } |
3416 | else |
3417 | { |
3418 | costEx += 2; // Very large offsets require movw/movt instructions |
3419 | costSz += 8; |
3420 | } |
3421 | } |
3422 | } |
3423 | #elif defined _TARGET_ARM64_ |
3424 | if (base) |
3425 | { |
3426 | costEx += base->gtCostEx; |
3427 | costSz += base->gtCostSz; |
3428 | } |
3429 | |
3430 | if (idx) |
3431 | { |
3432 | costEx += idx->gtCostEx; |
3433 | costSz += idx->gtCostSz; |
3434 | } |
3435 | |
3436 | if (cns != 0) |
3437 | { |
3438 | if (cns >= (4096 * genTypeSize(tree->TypeGet()))) |
3439 | { |
3440 | costEx += 1; |
3441 | costSz += 4; |
3442 | } |
3443 | } |
3444 | #else |
3445 | #error "Unknown _TARGET_" |
3446 | #endif |
3447 | |
3448 | assert(addr->gtOper == GT_ADD); |
3449 | assert(!addr->gtOverflow()); |
3450 | assert(op2 == nullptr); |
3451 | assert(mul != 1); |
3452 | |
3453 | // If we have an addressing mode, we have one of: |
3454 | // [base + cns] |
3455 | // [ idx * mul ] // mul >= 2, else we would use base instead of idx |
3456 | // [ idx * mul + cns] // mul >= 2, else we would use base instead of idx |
3457 | // [base + idx * mul ] // mul can be 0, 2, 4, or 8 |
3458 | // [base + idx * mul + cns] // mul can be 0, 2, 4, or 8 |
3459 | // Note that mul == 0 is semantically equivalent to mul == 1. |
3460 | // Note that cns can be zero. |
3461 | CLANG_FORMAT_COMMENT_ANCHOR; |
3462 | |
3463 | #if SCALED_ADDR_MODES |
3464 | assert((base != nullptr) || (idx != nullptr && mul >= 2)); |
3465 | #else |
3466 | assert(base != NULL); |
3467 | #endif |
3468 | |
3469 | INDEBUG(GenTree* op1Save = addr); |
3470 | |
3471 | // Walk 'addr' identifying non-overflow ADDs that will be part of the address mode. |
3472 | // Note that we will be modifying 'op1' and 'op2' so that eventually they should |
3473 | // map to the base and index. |
3474 | op1 = addr; |
3475 | gtWalkOp(&op1, &op2, base, false); |
3476 | |
3477 | // op1 and op2 are now descendents of the root GT_ADD of the addressing mode. |
3478 | assert(op1 != op1Save); |
3479 | assert(op2 != nullptr); |
3480 | |
3481 | // Walk the operands again (the third operand is unused in this case). |
3482 | // This time we will only consider adds with constant op2's, since |
3483 | // we have already found either a non-ADD op1 or a non-constant op2. |
3484 | gtWalkOp(&op1, &op2, nullptr, true); |
3485 | |
3486 | #if defined(_TARGET_XARCH_) |
3487 | // For XARCH we will fold GT_ADDs in the op2 position into the addressing mode, so we call |
3488 | // gtWalkOp on both operands of the original GT_ADD. |
3489 | // This is not done for ARMARCH. Though the stated reason is that we don't try to create a |
3490 | // scaled index, in fact we actually do create them (even base + index*scale + offset). |
3491 | |
3492 | // At this point, 'op2' may itself be an ADD of a constant that should be folded |
3493 | // into the addressing mode. |
3494 | // Walk op2 looking for non-overflow GT_ADDs of constants. |
3495 | gtWalkOp(&op2, &op1, nullptr, true); |
3496 | #endif // defined(_TARGET_XARCH_) |
3497 | |
3498 | // OK we are done walking the tree |
3499 | // Now assert that op1 and op2 correspond with base and idx |
3500 | // in one of the several acceptable ways. |
3501 | |
3502 | // Note that sometimes op1/op2 is equal to idx/base |
3503 | // and other times op1/op2 is a GT_COMMA node with |
3504 | // an effective value that is idx/base |
3505 | |
3506 | if (mul > 1) |
3507 | { |
3508 | if ((op1 != base) && (op1->gtOper == GT_LSH)) |
3509 | { |
3510 | op1->gtFlags |= GTF_ADDRMODE_NO_CSE; |
3511 | if (op1->gtOp.gtOp1->gtOper == GT_MUL) |
3512 | { |
3513 | op1->gtOp.gtOp1->gtFlags |= GTF_ADDRMODE_NO_CSE; |
3514 | } |
3515 | assert((base == nullptr) || (op2 == base) || |
3516 | (op2->gtEffectiveVal() == base->gtEffectiveVal()) || |
3517 | (gtWalkOpEffectiveVal(op2) == gtWalkOpEffectiveVal(base))); |
3518 | } |
3519 | else |
3520 | { |
3521 | assert(op2); |
3522 | assert(op2->gtOper == GT_LSH || op2->gtOper == GT_MUL); |
3523 | op2->gtFlags |= GTF_ADDRMODE_NO_CSE; |
3524 | // We may have eliminated multiple shifts and multiplies in the addressing mode, |
3525 | // so navigate down through them to get to "idx". |
3526 | GenTree* op2op1 = op2->gtOp.gtOp1; |
3527 | while ((op2op1->gtOper == GT_LSH || op2op1->gtOper == GT_MUL) && op2op1 != idx) |
3528 | { |
3529 | op2op1->gtFlags |= GTF_ADDRMODE_NO_CSE; |
3530 | op2op1 = op2op1->gtOp.gtOp1; |
3531 | } |
3532 | assert(op1->gtEffectiveVal() == base); |
3533 | assert(op2op1 == idx); |
3534 | } |
3535 | } |
3536 | else |
3537 | { |
3538 | assert(mul == 0); |
3539 | |
3540 | if ((op1 == idx) || (op1->gtEffectiveVal() == idx)) |
3541 | { |
3542 | if (idx != nullptr) |
3543 | { |
3544 | if ((op1->gtOper == GT_MUL) || (op1->gtOper == GT_LSH)) |
3545 | { |
3546 | if ((op1->gtOp.gtOp1->gtOper == GT_NOP) || |
3547 | (op1->gtOp.gtOp1->gtOper == GT_MUL && |
3548 | op1->gtOp.gtOp1->gtOp.gtOp1->gtOper == GT_NOP)) |
3549 | { |
3550 | op1->gtFlags |= GTF_ADDRMODE_NO_CSE; |
3551 | if (op1->gtOp.gtOp1->gtOper == GT_MUL) |
3552 | { |
3553 | op1->gtOp.gtOp1->gtFlags |= GTF_ADDRMODE_NO_CSE; |
3554 | } |
3555 | } |
3556 | } |
3557 | } |
3558 | assert((op2 == base) || (op2->gtEffectiveVal() == base)); |
3559 | } |
3560 | else if ((op1 == base) || (op1->gtEffectiveVal() == base)) |
3561 | { |
3562 | if (idx != nullptr) |
3563 | { |
3564 | assert(op2); |
3565 | if ((op2->gtOper == GT_MUL) || (op2->gtOper == GT_LSH)) |
3566 | { |
3567 | if ((op2->gtOp.gtOp1->gtOper == GT_NOP) || |
3568 | (op2->gtOp.gtOp1->gtOper == GT_MUL && |
3569 | op2->gtOp.gtOp1->gtOp.gtOp1->gtOper == GT_NOP)) |
3570 | { |
3571 | op2->gtFlags |= GTF_ADDRMODE_NO_CSE; |
3572 | if (op2->gtOp.gtOp1->gtOper == GT_MUL) |
3573 | { |
3574 | op2->gtOp.gtOp1->gtFlags |= GTF_ADDRMODE_NO_CSE; |
3575 | } |
3576 | } |
3577 | } |
3578 | assert((op2 == idx) || (op2->gtEffectiveVal() == idx)); |
3579 | } |
3580 | } |
3581 | else |
3582 | { |
3583 | // op1 isn't base or idx. Is this possible? Or should there be an assert? |
3584 | } |
3585 | } |
3586 | goto DONE; |
3587 | |
3588 | } // end if (genCreateAddrMode(...)) |
3589 | |
3590 | } // end if (op1->gtOper == GT_ADD) |
3591 | else if (gtIsLikelyRegVar(op1)) |
3592 | { |
3593 | /* Indirection of an enregister LCL_VAR, don't increase costEx/costSz */ |
3594 | goto DONE; |
3595 | } |
3596 | #ifdef _TARGET_XARCH_ |
3597 | else if (op1->IsCnsIntOrI()) |
3598 | { |
3599 | // Indirection of a CNS_INT, subtract 1 from costEx |
3600 | // makes costEx 3 for x86 and 4 for amd64 |
3601 | // |
3602 | costEx += (op1->gtCostEx - 1); |
3603 | costSz += op1->gtCostSz; |
3604 | goto DONE; |
3605 | } |
3606 | #endif |
3607 | break; |
3608 | |
3609 | default: |
3610 | break; |
3611 | } |
3612 | costEx += op1->gtCostEx; |
3613 | costSz += op1->gtCostSz; |
3614 | goto DONE; |
3615 | } |
3616 | |
3617 | /* Binary operator - check for certain special cases */ |
3618 | |
3619 | lvlb = 0; |
3620 | |
3621 | /* Default Binary ops have a cost of 1,1 */ |
3622 | costEx = 1; |
3623 | costSz = 1; |
3624 | |
3625 | #ifdef _TARGET_ARM_ |
3626 | if (isflt) |
3627 | { |
3628 | costSz += 2; |
3629 | } |
3630 | #endif |
3631 | #ifndef _TARGET_64BIT_ |
3632 | if (varTypeIsLong(op1->TypeGet())) |
3633 | { |
3634 | /* Operations on longs are more expensive */ |
3635 | costEx += 3; |
3636 | costSz += 3; |
3637 | } |
3638 | #endif |
3639 | switch (oper) |
3640 | { |
3641 | case GT_MOD: |
3642 | case GT_UMOD: |
3643 | |
3644 | /* Modulo by a power of 2 is easy */ |
3645 | |
3646 | if (op2->IsCnsIntOrI()) |
3647 | { |
3648 | size_t ival = op2->gtIntConCommon.IconValue(); |
3649 | |
3650 | if (ival > 0 && ival == genFindLowestBit(ival)) |
3651 | { |
3652 | break; |
3653 | } |
3654 | } |
3655 | |
3656 | __fallthrough; |
3657 | |
3658 | case GT_DIV: |
3659 | case GT_UDIV: |
3660 | |
3661 | if (isflt) |
3662 | { |
3663 | /* fp division is very expensive to execute */ |
3664 | costEx = 36; // TYP_DOUBLE |
3665 | costSz += 3; |
3666 | } |
3667 | else |
3668 | { |
3669 | /* integer division is also very expensive */ |
3670 | costEx = 20; |
3671 | costSz += 2; |
3672 | |
3673 | // Encourage the first operand to be evaluated (into EAX/EDX) first */ |
3674 | lvlb -= 3; |
3675 | } |
3676 | break; |
3677 | |
3678 | case GT_MUL: |
3679 | |
3680 | if (isflt) |
3681 | { |
3682 | /* FP multiplication instructions are more expensive */ |
3683 | costEx += 4; |
3684 | costSz += 3; |
3685 | } |
3686 | else |
3687 | { |
3688 | /* Integer multiplication instructions are more expensive */ |
3689 | costEx += 3; |
3690 | costSz += 2; |
3691 | |
3692 | if (tree->gtOverflow()) |
3693 | { |
3694 | /* Overflow check are more expensive */ |
3695 | costEx += 3; |
3696 | costSz += 3; |
3697 | } |
3698 | |
3699 | #ifdef _TARGET_X86_ |
3700 | if ((tree->gtType == TYP_LONG) || tree->gtOverflow()) |
3701 | { |
3702 | /* We use imulEAX for TYP_LONG and overflow multiplications */ |
3703 | // Encourage the first operand to be evaluated (into EAX/EDX) first */ |
3704 | lvlb -= 4; |
3705 | |
3706 | /* The 64-bit imul instruction costs more */ |
3707 | costEx += 4; |
3708 | } |
3709 | #endif // _TARGET_X86_ |
3710 | } |
3711 | break; |
3712 | |
3713 | case GT_ADD: |
3714 | case GT_SUB: |
3715 | if (isflt) |
3716 | { |
3717 | /* FP instructions are a bit more expensive */ |
3718 | costEx += 4; |
3719 | costSz += 3; |
3720 | break; |
3721 | } |
3722 | |
3723 | /* Overflow check are more expensive */ |
3724 | if (tree->gtOverflow()) |
3725 | { |
3726 | costEx += 3; |
3727 | costSz += 3; |
3728 | } |
3729 | break; |
3730 | |
3731 | case GT_COMMA: |
3732 | |
3733 | /* Comma tosses the result of the left operand */ |
3734 | gtSetEvalOrder(op1); |
3735 | level = gtSetEvalOrder(op2); |
3736 | |
3737 | /* GT_COMMA cost is the sum of op1 and op2 costs */ |
3738 | costEx = (op1->gtCostEx + op2->gtCostEx); |
3739 | costSz = (op1->gtCostSz + op2->gtCostSz); |
3740 | |
3741 | goto DONE; |
3742 | |
3743 | case GT_COLON: |
3744 | |
3745 | level = gtSetEvalOrder(op1); |
3746 | lvl2 = gtSetEvalOrder(op2); |
3747 | |
3748 | if (level < lvl2) |
3749 | { |
3750 | level = lvl2; |
3751 | } |
3752 | else if (level == lvl2) |
3753 | { |
3754 | level += 1; |
3755 | } |
3756 | |
3757 | costEx = op1->gtCostEx + op2->gtCostEx; |
3758 | costSz = op1->gtCostSz + op2->gtCostSz; |
3759 | |
3760 | goto DONE; |
3761 | |
3762 | case GT_LIST: |
3763 | case GT_FIELD_LIST: |
3764 | { |
3765 | const bool isListCallArgs = false; |
3766 | const bool callArgsInRegs = false; |
3767 | return gtSetListOrder(tree, isListCallArgs, callArgsInRegs); |
3768 | } |
3769 | |
3770 | case GT_ASG: |
3771 | /* Assignments need a bit of special handling */ |
3772 | /* Process the target */ |
3773 | level = gtSetEvalOrder(op1); |
3774 | |
3775 | if (gtIsLikelyRegVar(op1)) |
3776 | { |
3777 | assert(lvlb == 0); |
3778 | lvl2 = gtSetEvalOrder(op2); |
3779 | |
3780 | /* Assignment to an enregistered LCL_VAR */ |
3781 | costEx = op2->gtCostEx; |
3782 | costSz = max(3, op2->gtCostSz); // 3 is an estimate for a reg-reg assignment |
3783 | goto DONE_OP1_AFTER_COST; |
3784 | } |
3785 | goto DONE_OP1; |
3786 | |
3787 | default: |
3788 | break; |
3789 | } |
3790 | |
3791 | /* Process the sub-operands */ |
3792 | |
3793 | level = gtSetEvalOrder(op1); |
3794 | if (lvlb < 0) |
3795 | { |
3796 | level -= lvlb; // lvlb is negative, so this increases level |
3797 | lvlb = 0; |
3798 | } |
3799 | |
3800 | DONE_OP1: |
3801 | assert(lvlb >= 0); |
3802 | lvl2 = gtSetEvalOrder(op2) + lvlb; |
3803 | |
3804 | costEx += (op1->gtCostEx + op2->gtCostEx); |
3805 | costSz += (op1->gtCostSz + op2->gtCostSz); |
3806 | |
3807 | DONE_OP1_AFTER_COST: |
3808 | |
3809 | bool bReverseInAssignment = false; |
3810 | if (oper == GT_ASG) |
3811 | { |
3812 | GenTree* op1Val = op1; |
3813 | |
3814 | // Skip over the GT_IND/GT_ADDR tree (if one exists) |
3815 | // |
3816 | if ((op1->gtOper == GT_IND) && (op1->gtOp.gtOp1->gtOper == GT_ADDR)) |
3817 | { |
3818 | op1Val = op1->gtOp.gtOp1->gtOp.gtOp1; |
3819 | } |
3820 | |
3821 | switch (op1Val->gtOper) |
3822 | { |
3823 | case GT_IND: |
3824 | case GT_BLK: |
3825 | case GT_OBJ: |
3826 | case GT_DYN_BLK: |
3827 | |
3828 | // In an indirection, the destination address is evaluated prior to the source. |
3829 | // If we have any side effects on the target indirection, |
3830 | // we have to evaluate op1 first. |
3831 | // However, if the LHS is a lclVar address, SSA relies on using evaluation order for its |
3832 | // renaming, and therefore the RHS must be evaluated first. |
3833 | // If we have an assignment involving a lclVar address, the LHS may be marked as having |
3834 | // side-effects. |
3835 | // However the side-effects won't require that we evaluate the LHS address first: |
3836 | // - The GTF_GLOB_REF might have been conservatively set on a FIELD of a local. |
3837 | // - The local might be address-exposed, but that side-effect happens at the actual assignment (not |
3838 | // when its address is "evaluated") so it doesn't change the side effect to "evaluate" the address |
3839 | // after the RHS (note that in this case it won't be renamed by SSA anyway, but the reordering is |
3840 | // safe). |
3841 | // |
3842 | if (op1Val->AsIndir()->Addr()->IsLocalAddrExpr()) |
3843 | { |
3844 | bReverseInAssignment = true; |
3845 | tree->gtFlags |= GTF_REVERSE_OPS; |
3846 | break; |
3847 | } |
3848 | if (op1Val->AsIndir()->Addr()->gtFlags & GTF_ALL_EFFECT) |
3849 | { |
3850 | break; |
3851 | } |
3852 | |
3853 | // In case op2 assigns to a local var that is used in op1Val, we have to evaluate op1Val first. |
3854 | if (op2->gtFlags & GTF_ASG) |
3855 | { |
3856 | break; |
3857 | } |
3858 | |
3859 | // If op2 is simple then evaluate op1 first |
3860 | |
3861 | if (op2->OperKind() & GTK_LEAF) |
3862 | { |
3863 | break; |
3864 | } |
3865 | |
3866 | // fall through and set GTF_REVERSE_OPS |
3867 | |
3868 | case GT_LCL_VAR: |
3869 | case GT_LCL_FLD: |
3870 | |
3871 | // We evaluate op2 before op1 |
3872 | bReverseInAssignment = true; |
3873 | tree->gtFlags |= GTF_REVERSE_OPS; |
3874 | break; |
3875 | |
3876 | default: |
3877 | break; |
3878 | } |
3879 | } |
3880 | else if (kind & GTK_RELOP) |
3881 | { |
3882 | /* Float compares remove both operands from the FP stack */ |
3883 | /* Also FP comparison uses EAX for flags */ |
3884 | |
3885 | if (varTypeIsFloating(op1->TypeGet())) |
3886 | { |
3887 | level++; |
3888 | lvl2++; |
3889 | } |
3890 | if ((tree->gtFlags & GTF_RELOP_JMP_USED) == 0) |
3891 | { |
3892 | /* Using a setcc instruction is more expensive */ |
3893 | costEx += 3; |
3894 | } |
3895 | } |
3896 | |
3897 | /* Check for other interesting cases */ |
3898 | |
3899 | switch (oper) |
3900 | { |
3901 | case GT_LSH: |
3902 | case GT_RSH: |
3903 | case GT_RSZ: |
3904 | case GT_ROL: |
3905 | case GT_ROR: |
3906 | /* Variable sized shifts are more expensive and use REG_SHIFT */ |
3907 | |
3908 | if (!op2->IsCnsIntOrI()) |
3909 | { |
3910 | costEx += 3; |
3911 | #ifndef _TARGET_64BIT_ |
3912 | // Variable sized LONG shifts require the use of a helper call |
3913 | // |
3914 | if (tree->gtType == TYP_LONG) |
3915 | { |
3916 | level += 5; |
3917 | lvl2 += 5; |
3918 | costEx += 3 * IND_COST_EX; |
3919 | costSz += 4; |
3920 | } |
3921 | #endif // !_TARGET_64BIT_ |
3922 | } |
3923 | break; |
3924 | |
3925 | case GT_INTRINSIC: |
3926 | |
3927 | switch (tree->gtIntrinsic.gtIntrinsicId) |
3928 | { |
3929 | case CORINFO_INTRINSIC_Atan2: |
3930 | case CORINFO_INTRINSIC_Pow: |
3931 | // These math intrinsics are actually implemented by user calls. |
3932 | // Increase the Sethi 'complexity' by two to reflect the argument |
3933 | // register requirement. |
3934 | level += 2; |
3935 | break; |
3936 | default: |
3937 | assert(!"Unknown binary GT_INTRINSIC operator" ); |
3938 | break; |
3939 | } |
3940 | |
3941 | break; |
3942 | |
3943 | default: |
3944 | break; |
3945 | } |
3946 | |
3947 | /* We need to evalutate constants later as many places in codegen |
3948 | can't handle op1 being a constant. This is normally naturally |
3949 | enforced as constants have the least level of 0. However, |
3950 | sometimes we end up with a tree like "cns1 < nop(cns2)". In |
3951 | such cases, both sides have a level of 0. So encourage constants |
3952 | to be evaluated last in such cases */ |
3953 | |
3954 | if ((level == 0) && (level == lvl2) && (op1->OperKind() & GTK_CONST) && |
3955 | (tree->OperIsCommutative() || tree->OperIsCompare())) |
3956 | { |
3957 | lvl2++; |
3958 | } |
3959 | |
3960 | /* We try to swap operands if the second one is more expensive */ |
3961 | bool tryToSwap; |
3962 | GenTree* opA; |
3963 | GenTree* opB; |
3964 | |
3965 | if (tree->gtFlags & GTF_REVERSE_OPS) |
3966 | { |
3967 | opA = op2; |
3968 | opB = op1; |
3969 | } |
3970 | else |
3971 | { |
3972 | opA = op1; |
3973 | opB = op2; |
3974 | } |
3975 | |
3976 | if (fgOrder == FGOrderLinear) |
3977 | { |
3978 | // Don't swap anything if we're in linear order; we're really just interested in the costs. |
3979 | tryToSwap = false; |
3980 | } |
3981 | else if (bReverseInAssignment) |
3982 | { |
3983 | // Assignments are special, we want the reverseops flags |
3984 | // so if possible it was set above. |
3985 | tryToSwap = false; |
3986 | } |
3987 | else if ((oper == GT_INTRINSIC) && IsIntrinsicImplementedByUserCall(tree->AsIntrinsic()->gtIntrinsicId)) |
3988 | { |
3989 | // We do not swap operand execution order for intrinsics that are implemented by user calls |
3990 | // because of trickiness around ensuring the execution order does not change during rationalization. |
3991 | tryToSwap = false; |
3992 | } |
3993 | else |
3994 | { |
3995 | if (tree->gtFlags & GTF_REVERSE_OPS) |
3996 | { |
3997 | tryToSwap = (level > lvl2); |
3998 | } |
3999 | else |
4000 | { |
4001 | tryToSwap = (level < lvl2); |
4002 | } |
4003 | |
4004 | // Try to force extra swapping when in the stress mode: |
4005 | if (compStressCompile(STRESS_REVERSE_FLAG, 60) && ((tree->gtFlags & GTF_REVERSE_OPS) == 0) && |
4006 | ((op2->OperKind() & GTK_CONST) == 0)) |
4007 | { |
4008 | tryToSwap = true; |
4009 | } |
4010 | } |
4011 | |
4012 | if (tryToSwap) |
4013 | { |
4014 | bool canSwap = gtCanSwapOrder(opA, opB); |
4015 | |
4016 | if (canSwap) |
4017 | { |
4018 | /* Can we swap the order by commuting the operands? */ |
4019 | |
4020 | switch (oper) |
4021 | { |
4022 | case GT_EQ: |
4023 | case GT_NE: |
4024 | case GT_LT: |
4025 | case GT_LE: |
4026 | case GT_GE: |
4027 | case GT_GT: |
4028 | if (GenTree::SwapRelop(oper) != oper) |
4029 | { |
4030 | tree->SetOper(GenTree::SwapRelop(oper), GenTree::PRESERVE_VN); |
4031 | } |
4032 | |
4033 | __fallthrough; |
4034 | |
4035 | case GT_ADD: |
4036 | case GT_MUL: |
4037 | |
4038 | case GT_OR: |
4039 | case GT_XOR: |
4040 | case GT_AND: |
4041 | |
4042 | /* Swap the operands */ |
4043 | |
4044 | tree->gtOp.gtOp1 = op2; |
4045 | tree->gtOp.gtOp2 = op1; |
4046 | break; |
4047 | |
4048 | case GT_QMARK: |
4049 | case GT_COLON: |
4050 | case GT_MKREFANY: |
4051 | break; |
4052 | |
4053 | case GT_LIST: |
4054 | case GT_FIELD_LIST: |
4055 | break; |
4056 | |
4057 | default: |
4058 | |
4059 | /* Mark the operand's evaluation order to be swapped */ |
4060 | if (tree->gtFlags & GTF_REVERSE_OPS) |
4061 | { |
4062 | tree->gtFlags &= ~GTF_REVERSE_OPS; |
4063 | } |
4064 | else |
4065 | { |
4066 | tree->gtFlags |= GTF_REVERSE_OPS; |
4067 | } |
4068 | |
4069 | break; |
4070 | } |
4071 | } |
4072 | } |
4073 | |
4074 | /* Swap the level counts */ |
4075 | if (tree->gtFlags & GTF_REVERSE_OPS) |
4076 | { |
4077 | unsigned tmpl; |
4078 | |
4079 | tmpl = level; |
4080 | level = lvl2; |
4081 | lvl2 = tmpl; |
4082 | } |
4083 | |
4084 | /* Compute the sethi number for this binary operator */ |
4085 | |
4086 | if (level < 1) |
4087 | { |
4088 | level = lvl2; |
4089 | } |
4090 | else if (level == lvl2) |
4091 | { |
4092 | level += 1; |
4093 | } |
4094 | |
4095 | goto DONE; |
4096 | } |
4097 | |
4098 | /* See what kind of a special operator we have here */ |
4099 | |
4100 | switch (oper) |
4101 | { |
4102 | unsigned lvl2; // Scratch variable |
4103 | |
4104 | case GT_CALL: |
4105 | |
4106 | assert(tree->gtFlags & GTF_CALL); |
4107 | |
4108 | level = 0; |
4109 | costEx = 5; |
4110 | costSz = 2; |
4111 | |
4112 | /* Evaluate the 'this' argument, if present */ |
4113 | |
4114 | if (tree->gtCall.gtCallObjp) |
4115 | { |
4116 | GenTree* thisVal = tree->gtCall.gtCallObjp; |
4117 | |
4118 | lvl2 = gtSetEvalOrder(thisVal); |
4119 | if (level < lvl2) |
4120 | { |
4121 | level = lvl2; |
4122 | } |
4123 | costEx += thisVal->gtCostEx; |
4124 | costSz += thisVal->gtCostSz + 1; |
4125 | } |
4126 | |
4127 | /* Evaluate the arguments, right to left */ |
4128 | |
4129 | if (tree->gtCall.gtCallArgs) |
4130 | { |
4131 | const bool isListCallArgs = true; |
4132 | const bool callArgsInRegs = false; |
4133 | lvl2 = gtSetListOrder(tree->gtCall.gtCallArgs, isListCallArgs, callArgsInRegs); |
4134 | if (level < lvl2) |
4135 | { |
4136 | level = lvl2; |
4137 | } |
4138 | costEx += tree->gtCall.gtCallArgs->gtCostEx; |
4139 | costSz += tree->gtCall.gtCallArgs->gtCostSz; |
4140 | } |
4141 | |
4142 | /* Evaluate the temp register arguments list |
4143 | * This is a "hidden" list and its only purpose is to |
4144 | * extend the life of temps until we make the call */ |
4145 | |
4146 | if (tree->gtCall.gtCallLateArgs) |
4147 | { |
4148 | const bool isListCallArgs = true; |
4149 | const bool callArgsInRegs = true; |
4150 | lvl2 = gtSetListOrder(tree->gtCall.gtCallLateArgs, isListCallArgs, callArgsInRegs); |
4151 | if (level < lvl2) |
4152 | { |
4153 | level = lvl2; |
4154 | } |
4155 | costEx += tree->gtCall.gtCallLateArgs->gtCostEx; |
4156 | costSz += tree->gtCall.gtCallLateArgs->gtCostSz; |
4157 | } |
4158 | |
4159 | if (tree->gtCall.gtCallType == CT_INDIRECT) |
4160 | { |
4161 | // pinvoke-calli cookie is a constant, or constant indirection |
4162 | assert(tree->gtCall.gtCallCookie == nullptr || tree->gtCall.gtCallCookie->gtOper == GT_CNS_INT || |
4163 | tree->gtCall.gtCallCookie->gtOper == GT_IND); |
4164 | |
4165 | GenTree* indirect = tree->gtCall.gtCallAddr; |
4166 | |
4167 | lvl2 = gtSetEvalOrder(indirect); |
4168 | if (level < lvl2) |
4169 | { |
4170 | level = lvl2; |
4171 | } |
4172 | costEx += indirect->gtCostEx + IND_COST_EX; |
4173 | costSz += indirect->gtCostSz; |
4174 | } |
4175 | else |
4176 | { |
4177 | #ifdef _TARGET_ARM_ |
4178 | if (tree->gtCall.IsVirtualStub()) |
4179 | { |
4180 | // We generate movw/movt/ldr |
4181 | costEx += (1 + IND_COST_EX); |
4182 | costSz += 8; |
4183 | if (tree->gtCall.gtCallMoreFlags & GTF_CALL_M_VIRTSTUB_REL_INDIRECT) |
4184 | { |
4185 | // Must use R12 for the ldr target -- REG_JUMP_THUNK_PARAM |
4186 | costSz += 2; |
4187 | } |
4188 | } |
4189 | else if (!opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT)) |
4190 | { |
4191 | costEx += 2; |
4192 | costSz += 6; |
4193 | } |
4194 | costSz += 2; |
4195 | #endif |
4196 | #ifdef _TARGET_XARCH_ |
4197 | costSz += 3; |
4198 | #endif |
4199 | } |
4200 | |
4201 | level += 1; |
4202 | |
4203 | /* Virtual calls are a bit more expensive */ |
4204 | if (tree->gtCall.IsVirtual()) |
4205 | { |
4206 | costEx += 2 * IND_COST_EX; |
4207 | costSz += 2; |
4208 | } |
4209 | |
4210 | level += 5; |
4211 | costEx += 3 * IND_COST_EX; |
4212 | break; |
4213 | |
4214 | case GT_ARR_ELEM: |
4215 | |
4216 | level = gtSetEvalOrder(tree->gtArrElem.gtArrObj); |
4217 | costEx = tree->gtArrElem.gtArrObj->gtCostEx; |
4218 | costSz = tree->gtArrElem.gtArrObj->gtCostSz; |
4219 | |
4220 | unsigned dim; |
4221 | for (dim = 0; dim < tree->gtArrElem.gtArrRank; dim++) |
4222 | { |
4223 | lvl2 = gtSetEvalOrder(tree->gtArrElem.gtArrInds[dim]); |
4224 | if (level < lvl2) |
4225 | { |
4226 | level = lvl2; |
4227 | } |
4228 | costEx += tree->gtArrElem.gtArrInds[dim]->gtCostEx; |
4229 | costSz += tree->gtArrElem.gtArrInds[dim]->gtCostSz; |
4230 | } |
4231 | |
4232 | level += tree->gtArrElem.gtArrRank; |
4233 | costEx += 2 + (tree->gtArrElem.gtArrRank * (IND_COST_EX + 1)); |
4234 | costSz += 2 + (tree->gtArrElem.gtArrRank * 2); |
4235 | break; |
4236 | |
4237 | case GT_ARR_OFFSET: |
4238 | level = gtSetEvalOrder(tree->gtArrOffs.gtOffset); |
4239 | costEx = tree->gtArrOffs.gtOffset->gtCostEx; |
4240 | costSz = tree->gtArrOffs.gtOffset->gtCostSz; |
4241 | lvl2 = gtSetEvalOrder(tree->gtArrOffs.gtIndex); |
4242 | level = max(level, lvl2); |
4243 | costEx += tree->gtArrOffs.gtIndex->gtCostEx; |
4244 | costSz += tree->gtArrOffs.gtIndex->gtCostSz; |
4245 | lvl2 = gtSetEvalOrder(tree->gtArrOffs.gtArrObj); |
4246 | level = max(level, lvl2); |
4247 | costEx += tree->gtArrOffs.gtArrObj->gtCostEx; |
4248 | costSz += tree->gtArrOffs.gtArrObj->gtCostSz; |
4249 | break; |
4250 | |
4251 | case GT_CMPXCHG: |
4252 | |
4253 | level = gtSetEvalOrder(tree->gtCmpXchg.gtOpLocation); |
4254 | costSz = tree->gtCmpXchg.gtOpLocation->gtCostSz; |
4255 | |
4256 | lvl2 = gtSetEvalOrder(tree->gtCmpXchg.gtOpValue); |
4257 | if (level < lvl2) |
4258 | { |
4259 | level = lvl2; |
4260 | } |
4261 | costSz += tree->gtCmpXchg.gtOpValue->gtCostSz; |
4262 | |
4263 | lvl2 = gtSetEvalOrder(tree->gtCmpXchg.gtOpComparand); |
4264 | if (level < lvl2) |
4265 | { |
4266 | level = lvl2; |
4267 | } |
4268 | costSz += tree->gtCmpXchg.gtOpComparand->gtCostSz; |
4269 | |
4270 | costEx = MAX_COST; // Seriously, what could be more expensive than lock cmpxchg? |
4271 | costSz += 5; // size of lock cmpxchg [reg+C], reg |
4272 | break; |
4273 | |
4274 | case GT_ARR_BOUNDS_CHECK: |
4275 | #ifdef FEATURE_SIMD |
4276 | case GT_SIMD_CHK: |
4277 | #endif // FEATURE_SIMD |
4278 | #ifdef FEATURE_HW_INTRINSICS |
4279 | case GT_HW_INTRINSIC_CHK: |
4280 | #endif // FEATURE_HW_INTRINSICS |
4281 | |
4282 | costEx = 4; // cmp reg,reg and jae throw (not taken) |
4283 | costSz = 7; // jump to cold section |
4284 | |
4285 | level = gtSetEvalOrder(tree->gtBoundsChk.gtIndex); |
4286 | costEx += tree->gtBoundsChk.gtIndex->gtCostEx; |
4287 | costSz += tree->gtBoundsChk.gtIndex->gtCostSz; |
4288 | |
4289 | lvl2 = gtSetEvalOrder(tree->gtBoundsChk.gtArrLen); |
4290 | if (level < lvl2) |
4291 | { |
4292 | level = lvl2; |
4293 | } |
4294 | costEx += tree->gtBoundsChk.gtArrLen->gtCostEx; |
4295 | costSz += tree->gtBoundsChk.gtArrLen->gtCostSz; |
4296 | |
4297 | break; |
4298 | |
4299 | case GT_STORE_DYN_BLK: |
4300 | case GT_DYN_BLK: |
4301 | { |
4302 | costEx = 0; |
4303 | costSz = 0; |
4304 | level = 0; |
4305 | if (oper == GT_STORE_DYN_BLK) |
4306 | { |
4307 | lvl2 = gtSetEvalOrder(tree->gtDynBlk.Data()); |
4308 | level = max(level, lvl2); |
4309 | costEx += tree->gtDynBlk.Data()->gtCostEx; |
4310 | costSz += tree->gtDynBlk.Data()->gtCostSz; |
4311 | } |
4312 | lvl2 = gtSetEvalOrder(tree->gtDynBlk.Addr()); |
4313 | level = max(level, lvl2); |
4314 | costEx = tree->gtDynBlk.Addr()->gtCostEx; |
4315 | costSz = tree->gtDynBlk.Addr()->gtCostSz; |
4316 | unsigned sizeLevel = gtSetEvalOrder(tree->gtDynBlk.gtDynamicSize); |
4317 | |
4318 | // Determine whether the size node should be evaluated first. |
4319 | // We would like to do this if the sizeLevel is larger than the current level, |
4320 | // but we have to ensure that we obey ordering constraints. |
4321 | if (tree->AsDynBlk()->gtEvalSizeFirst != (level < sizeLevel)) |
4322 | { |
4323 | bool canChange = true; |
4324 | |
4325 | GenTree* sizeNode = tree->AsDynBlk()->gtDynamicSize; |
4326 | GenTree* dst = tree->AsDynBlk()->Addr(); |
4327 | GenTree* src = tree->AsDynBlk()->Data(); |
4328 | |
4329 | if (tree->AsDynBlk()->gtEvalSizeFirst) |
4330 | { |
4331 | canChange = gtCanSwapOrder(sizeNode, dst); |
4332 | if (canChange && (src != nullptr)) |
4333 | { |
4334 | canChange = gtCanSwapOrder(sizeNode, src); |
4335 | } |
4336 | } |
4337 | else |
4338 | { |
4339 | canChange = gtCanSwapOrder(dst, sizeNode); |
4340 | if (canChange && (src != nullptr)) |
4341 | { |
4342 | gtCanSwapOrder(src, sizeNode); |
4343 | } |
4344 | } |
4345 | if (canChange) |
4346 | { |
4347 | tree->AsDynBlk()->gtEvalSizeFirst = (level < sizeLevel); |
4348 | } |
4349 | } |
4350 | level = max(level, sizeLevel); |
4351 | costEx += tree->gtDynBlk.gtDynamicSize->gtCostEx; |
4352 | costSz += tree->gtDynBlk.gtDynamicSize->gtCostSz; |
4353 | } |
4354 | break; |
4355 | |
4356 | case GT_INDEX_ADDR: |
4357 | costEx = 6; // cmp reg,reg; jae throw; mov reg, [addrmode] (not taken) |
4358 | costSz = 9; // jump to cold section |
4359 | |
4360 | level = gtSetEvalOrder(tree->AsIndexAddr()->Index()); |
4361 | costEx += tree->AsIndexAddr()->Index()->gtCostEx; |
4362 | costSz += tree->AsIndexAddr()->Index()->gtCostSz; |
4363 | |
4364 | lvl2 = gtSetEvalOrder(tree->AsIndexAddr()->Arr()); |
4365 | if (level < lvl2) |
4366 | { |
4367 | level = lvl2; |
4368 | } |
4369 | costEx += tree->AsIndexAddr()->Arr()->gtCostEx; |
4370 | costSz += tree->AsIndexAddr()->Arr()->gtCostSz; |
4371 | break; |
4372 | |
4373 | default: |
4374 | #ifdef DEBUG |
4375 | if (verbose) |
4376 | { |
4377 | printf("unexpected operator in this tree:\n" ); |
4378 | gtDispTree(tree); |
4379 | } |
4380 | #endif |
4381 | NO_WAY("unexpected operator" ); |
4382 | } |
4383 | |
4384 | DONE: |
4385 | |
4386 | #ifdef FEATURE_HW_INTRINSICS |
4387 | if ((oper == GT_HWIntrinsic) && (tree->gtGetOp1() == nullptr)) |
4388 | { |
4389 | // We can have nullary HWIntrinsic nodes, and we must have non-zero cost. |
4390 | costEx = 1; |
4391 | costSz = 1; |
4392 | } |
4393 | #endif // FEATURE_HW_INTRINSICS |
4394 | |
4395 | // Some path through this function must have set the costs. |
4396 | assert(costEx != -1); |
4397 | assert(costSz != -1); |
4398 | |
4399 | tree->SetCosts(costEx, costSz); |
4400 | |
4401 | return level; |
4402 | } |
4403 | #ifdef _PREFAST_ |
4404 | #pragma warning(pop) |
4405 | #endif |
4406 | |
4407 | /***************************************************************************** |
4408 | * |
4409 | * If the given tree is an integer constant that can be used |
4410 | * in a scaled index address mode as a multiplier (e.g. "[4*index]"), then return |
4411 | * the scale factor: 2, 4, or 8. Otherwise, return 0. Note that we never return 1, |
4412 | * to match the behavior of GetScaleIndexShf(). |
4413 | */ |
4414 | |
4415 | unsigned GenTree::GetScaleIndexMul() |
4416 | { |
4417 | if (IsCnsIntOrI() && jitIsScaleIndexMul(gtIntConCommon.IconValue()) && gtIntConCommon.IconValue() != 1) |
4418 | { |
4419 | return (unsigned)gtIntConCommon.IconValue(); |
4420 | } |
4421 | |
4422 | return 0; |
4423 | } |
4424 | |
4425 | /***************************************************************************** |
4426 | * |
4427 | * If the given tree is the right-hand side of a left shift (that is, |
4428 | * 'y' in the tree 'x' << 'y'), and it is an integer constant that can be used |
4429 | * in a scaled index address mode as a multiplier (e.g. "[4*index]"), then return |
4430 | * the scale factor: 2, 4, or 8. Otherwise, return 0. |
4431 | */ |
4432 | |
4433 | unsigned GenTree::GetScaleIndexShf() |
4434 | { |
4435 | if (IsCnsIntOrI() && jitIsScaleIndexShift(gtIntConCommon.IconValue())) |
4436 | { |
4437 | return (unsigned)(1 << gtIntConCommon.IconValue()); |
4438 | } |
4439 | |
4440 | return 0; |
4441 | } |
4442 | |
4443 | /***************************************************************************** |
4444 | * |
4445 | * If the given tree is a scaled index (i.e. "op * 4" or "op << 2"), returns |
4446 | * the multiplier: 2, 4, or 8; otherwise returns 0. Note that "1" is never |
4447 | * returned. |
4448 | */ |
4449 | |
4450 | unsigned GenTree::GetScaledIndex() |
4451 | { |
4452 | // with (!opts.OptEnabled(CLFLG_CONSTANTFOLD) we can have |
4453 | // CNS_INT * CNS_INT |
4454 | // |
4455 | if (gtOp.gtOp1->IsCnsIntOrI()) |
4456 | { |
4457 | return 0; |
4458 | } |
4459 | |
4460 | switch (gtOper) |
4461 | { |
4462 | case GT_MUL: |
4463 | return gtOp.gtOp2->GetScaleIndexMul(); |
4464 | |
4465 | case GT_LSH: |
4466 | return gtOp.gtOp2->GetScaleIndexShf(); |
4467 | |
4468 | default: |
4469 | assert(!"GenTree::GetScaledIndex() called with illegal gtOper" ); |
4470 | break; |
4471 | } |
4472 | |
4473 | return 0; |
4474 | } |
4475 | |
4476 | /***************************************************************************** |
4477 | * |
4478 | * Returns true if "addr" is a GT_ADD node, at least one of whose arguments is an integer (<= 32 bit) |
4479 | * constant. If it returns true, it sets "*offset" to (one of the) constant value(s), and |
4480 | * "*addr" to the other argument. |
4481 | */ |
4482 | |
4483 | bool GenTree::IsAddWithI32Const(GenTree** addr, int* offset) |
4484 | { |
4485 | if (OperGet() == GT_ADD) |
4486 | { |
4487 | if (gtOp.gtOp1->IsIntCnsFitsInI32()) |
4488 | { |
4489 | *offset = (int)gtOp.gtOp1->gtIntCon.gtIconVal; |
4490 | *addr = gtOp.gtOp2; |
4491 | return true; |
4492 | } |
4493 | else if (gtOp.gtOp2->IsIntCnsFitsInI32()) |
4494 | { |
4495 | *offset = (int)gtOp.gtOp2->gtIntCon.gtIconVal; |
4496 | *addr = gtOp.gtOp1; |
4497 | return true; |
4498 | } |
4499 | } |
4500 | // Otherwise... |
4501 | return false; |
4502 | } |
4503 | |
4504 | //------------------------------------------------------------------------ |
4505 | // gtGetChildPointer: If 'parent' is the parent of this node, return the pointer |
4506 | // to the child node so that it can be modified; otherwise, return nullptr. |
4507 | // |
4508 | // Arguments: |
4509 | // parent - The possible parent of this node |
4510 | // |
4511 | // Return Value: |
4512 | // If "child" is a child of "parent", returns a pointer to the child node in the parent |
4513 | // (i.e. a pointer to a GenTree pointer). |
4514 | // Otherwise, returns nullptr. |
4515 | // |
4516 | // Assumptions: |
4517 | // 'parent' must be non-null |
4518 | // |
4519 | // Notes: |
4520 | // When FEATURE_MULTIREG_ARGS is defined we can get here with GT_OBJ tree. |
4521 | // This happens when we have a struct that is passed in multiple registers. |
4522 | // |
4523 | // Also note that when UNIX_AMD64_ABI is defined the GT_LDOBJ |
4524 | // later gets converted to a GT_FIELD_LIST with two GT_LCL_FLDs in Lower/LowerXArch. |
4525 | // |
4526 | |
4527 | GenTree** GenTree::gtGetChildPointer(GenTree* parent) const |
4528 | |
4529 | { |
4530 | switch (parent->OperGet()) |
4531 | { |
4532 | default: |
4533 | if (!parent->OperIsSimple()) |
4534 | { |
4535 | return nullptr; |
4536 | } |
4537 | if (this == parent->gtOp.gtOp1) |
4538 | { |
4539 | return &(parent->gtOp.gtOp1); |
4540 | } |
4541 | if (this == parent->gtOp.gtOp2) |
4542 | { |
4543 | return &(parent->gtOp.gtOp2); |
4544 | } |
4545 | break; |
4546 | |
4547 | case GT_CMPXCHG: |
4548 | if (this == parent->gtCmpXchg.gtOpLocation) |
4549 | { |
4550 | return &(parent->gtCmpXchg.gtOpLocation); |
4551 | } |
4552 | if (this == parent->gtCmpXchg.gtOpValue) |
4553 | { |
4554 | return &(parent->gtCmpXchg.gtOpValue); |
4555 | } |
4556 | if (this == parent->gtCmpXchg.gtOpComparand) |
4557 | { |
4558 | return &(parent->gtCmpXchg.gtOpComparand); |
4559 | } |
4560 | break; |
4561 | |
4562 | case GT_ARR_BOUNDS_CHECK: |
4563 | #ifdef FEATURE_SIMD |
4564 | case GT_SIMD_CHK: |
4565 | #endif // FEATURE_SIMD |
4566 | #ifdef FEATURE_HW_INTRINSICS |
4567 | case GT_HW_INTRINSIC_CHK: |
4568 | #endif // FEATURE_HW_INTRINSICS |
4569 | if (this == parent->gtBoundsChk.gtIndex) |
4570 | { |
4571 | return &(parent->gtBoundsChk.gtIndex); |
4572 | } |
4573 | if (this == parent->gtBoundsChk.gtArrLen) |
4574 | { |
4575 | return &(parent->gtBoundsChk.gtArrLen); |
4576 | } |
4577 | if (this == parent->gtBoundsChk.gtIndRngFailBB) |
4578 | { |
4579 | return &(parent->gtBoundsChk.gtIndRngFailBB); |
4580 | } |
4581 | break; |
4582 | |
4583 | case GT_ARR_ELEM: |
4584 | if (this == parent->gtArrElem.gtArrObj) |
4585 | { |
4586 | return &(parent->gtArrElem.gtArrObj); |
4587 | } |
4588 | for (int i = 0; i < GT_ARR_MAX_RANK; i++) |
4589 | { |
4590 | if (this == parent->gtArrElem.gtArrInds[i]) |
4591 | { |
4592 | return &(parent->gtArrElem.gtArrInds[i]); |
4593 | } |
4594 | } |
4595 | break; |
4596 | |
4597 | case GT_ARR_OFFSET: |
4598 | if (this == parent->gtArrOffs.gtOffset) |
4599 | { |
4600 | return &(parent->gtArrOffs.gtOffset); |
4601 | } |
4602 | if (this == parent->gtArrOffs.gtIndex) |
4603 | { |
4604 | return &(parent->gtArrOffs.gtIndex); |
4605 | } |
4606 | if (this == parent->gtArrOffs.gtArrObj) |
4607 | { |
4608 | return &(parent->gtArrOffs.gtArrObj); |
4609 | } |
4610 | break; |
4611 | |
4612 | case GT_STORE_DYN_BLK: |
4613 | case GT_DYN_BLK: |
4614 | if (this == parent->gtDynBlk.gtOp1) |
4615 | { |
4616 | return &(parent->gtDynBlk.gtOp1); |
4617 | } |
4618 | if (this == parent->gtDynBlk.gtOp2) |
4619 | { |
4620 | return &(parent->gtDynBlk.gtOp2); |
4621 | } |
4622 | if (this == parent->gtDynBlk.gtDynamicSize) |
4623 | { |
4624 | return &(parent->gtDynBlk.gtDynamicSize); |
4625 | } |
4626 | break; |
4627 | |
4628 | case GT_FIELD: |
4629 | if (this == parent->AsField()->gtFldObj) |
4630 | { |
4631 | return &(parent->AsField()->gtFldObj); |
4632 | } |
4633 | break; |
4634 | |
4635 | case GT_RET_EXPR: |
4636 | if (this == parent->gtRetExpr.gtInlineCandidate) |
4637 | { |
4638 | return &(parent->gtRetExpr.gtInlineCandidate); |
4639 | } |
4640 | break; |
4641 | |
4642 | case GT_CALL: |
4643 | { |
4644 | GenTreeCall* call = parent->AsCall(); |
4645 | |
4646 | if (this == call->gtCallObjp) |
4647 | { |
4648 | return &(call->gtCallObjp); |
4649 | } |
4650 | if (this == call->gtCallArgs) |
4651 | { |
4652 | return reinterpret_cast<GenTree**>(&(call->gtCallArgs)); |
4653 | } |
4654 | if (this == call->gtCallLateArgs) |
4655 | { |
4656 | return reinterpret_cast<GenTree**>(&(call->gtCallLateArgs)); |
4657 | } |
4658 | if (this == call->gtControlExpr) |
4659 | { |
4660 | return &(call->gtControlExpr); |
4661 | } |
4662 | if (call->gtCallType == CT_INDIRECT) |
4663 | { |
4664 | if (this == call->gtCallCookie) |
4665 | { |
4666 | return &(call->gtCallCookie); |
4667 | } |
4668 | if (this == call->gtCallAddr) |
4669 | { |
4670 | return &(call->gtCallAddr); |
4671 | } |
4672 | } |
4673 | } |
4674 | break; |
4675 | |
4676 | case GT_STMT: |
4677 | noway_assert(!"Illegal node for gtGetChildPointer()" ); |
4678 | unreached(); |
4679 | } |
4680 | |
4681 | return nullptr; |
4682 | } |
4683 | |
4684 | bool GenTree::TryGetUse(GenTree* def, GenTree*** use) |
4685 | { |
4686 | assert(def != nullptr); |
4687 | assert(use != nullptr); |
4688 | |
4689 | switch (OperGet()) |
4690 | { |
4691 | // Leaf nodes |
4692 | case GT_LCL_VAR: |
4693 | case GT_LCL_FLD: |
4694 | case GT_LCL_VAR_ADDR: |
4695 | case GT_LCL_FLD_ADDR: |
4696 | case GT_CATCH_ARG: |
4697 | case GT_LABEL: |
4698 | case GT_FTN_ADDR: |
4699 | case GT_RET_EXPR: |
4700 | case GT_CNS_INT: |
4701 | case GT_CNS_LNG: |
4702 | case GT_CNS_DBL: |
4703 | case GT_CNS_STR: |
4704 | case GT_MEMORYBARRIER: |
4705 | case GT_JMP: |
4706 | case GT_JCC: |
4707 | case GT_SETCC: |
4708 | case GT_NO_OP: |
4709 | case GT_START_NONGC: |
4710 | case GT_PROF_HOOK: |
4711 | #if !FEATURE_EH_FUNCLETS |
4712 | case GT_END_LFIN: |
4713 | #endif // !FEATURE_EH_FUNCLETS |
4714 | case GT_PHI_ARG: |
4715 | case GT_JMPTABLE: |
4716 | case GT_CLS_VAR: |
4717 | case GT_CLS_VAR_ADDR: |
4718 | case GT_ARGPLACE: |
4719 | case GT_PHYSREG: |
4720 | case GT_EMITNOP: |
4721 | case GT_PINVOKE_PROLOG: |
4722 | case GT_PINVOKE_EPILOG: |
4723 | case GT_IL_OFFSET: |
4724 | return false; |
4725 | |
4726 | // Standard unary operators |
4727 | case GT_STORE_LCL_VAR: |
4728 | case GT_STORE_LCL_FLD: |
4729 | case GT_NOT: |
4730 | case GT_NEG: |
4731 | case GT_COPY: |
4732 | case GT_RELOAD: |
4733 | case GT_ARR_LENGTH: |
4734 | case GT_CAST: |
4735 | case GT_BITCAST: |
4736 | case GT_CKFINITE: |
4737 | case GT_LCLHEAP: |
4738 | case GT_ADDR: |
4739 | case GT_IND: |
4740 | case GT_OBJ: |
4741 | case GT_BLK: |
4742 | case GT_BOX: |
4743 | case GT_ALLOCOBJ: |
4744 | case GT_RUNTIMELOOKUP: |
4745 | case GT_INIT_VAL: |
4746 | case GT_JTRUE: |
4747 | case GT_SWITCH: |
4748 | case GT_NULLCHECK: |
4749 | case GT_PUTARG_REG: |
4750 | case GT_PUTARG_STK: |
4751 | case GT_RETURNTRAP: |
4752 | case GT_NOP: |
4753 | case GT_RETURN: |
4754 | case GT_RETFILT: |
4755 | case GT_BSWAP: |
4756 | case GT_BSWAP16: |
4757 | if (def == this->AsUnOp()->gtOp1) |
4758 | { |
4759 | *use = &this->AsUnOp()->gtOp1; |
4760 | return true; |
4761 | } |
4762 | return false; |
4763 | |
4764 | // Variadic nodes |
4765 | case GT_PHI: |
4766 | assert(this->AsUnOp()->gtOp1 != nullptr); |
4767 | return this->AsUnOp()->gtOp1->TryGetUseList(def, use); |
4768 | |
4769 | case GT_FIELD_LIST: |
4770 | return TryGetUseList(def, use); |
4771 | |
4772 | #if FEATURE_ARG_SPLIT |
4773 | case GT_PUTARG_SPLIT: |
4774 | if (this->AsUnOp()->gtOp1->gtOper == GT_FIELD_LIST) |
4775 | { |
4776 | return this->AsUnOp()->gtOp1->TryGetUseList(def, use); |
4777 | } |
4778 | if (def == this->AsUnOp()->gtOp1) |
4779 | { |
4780 | *use = &this->AsUnOp()->gtOp1; |
4781 | return true; |
4782 | } |
4783 | return false; |
4784 | #endif // FEATURE_ARG_SPLIT |
4785 | |
4786 | #ifdef FEATURE_SIMD |
4787 | case GT_SIMD: |
4788 | if (this->AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInitN) |
4789 | { |
4790 | assert(this->AsSIMD()->gtOp1 != nullptr); |
4791 | return this->AsSIMD()->gtOp1->TryGetUseList(def, use); |
4792 | } |
4793 | |
4794 | return TryGetUseBinOp(def, use); |
4795 | #endif // FEATURE_SIMD |
4796 | |
4797 | #ifdef FEATURE_HW_INTRINSICS |
4798 | case GT_HWIntrinsic: |
4799 | if ((this->AsHWIntrinsic()->gtOp1 != nullptr) && this->AsHWIntrinsic()->gtOp1->OperIsList()) |
4800 | { |
4801 | return this->AsHWIntrinsic()->gtOp1->TryGetUseList(def, use); |
4802 | } |
4803 | |
4804 | return TryGetUseBinOp(def, use); |
4805 | #endif // FEATURE_HW_INTRINSICS |
4806 | |
4807 | // Special nodes |
4808 | case GT_CMPXCHG: |
4809 | { |
4810 | GenTreeCmpXchg* const cmpXchg = this->AsCmpXchg(); |
4811 | if (def == cmpXchg->gtOpLocation) |
4812 | { |
4813 | *use = &cmpXchg->gtOpLocation; |
4814 | return true; |
4815 | } |
4816 | if (def == cmpXchg->gtOpValue) |
4817 | { |
4818 | *use = &cmpXchg->gtOpValue; |
4819 | return true; |
4820 | } |
4821 | if (def == cmpXchg->gtOpComparand) |
4822 | { |
4823 | *use = &cmpXchg->gtOpComparand; |
4824 | return true; |
4825 | } |
4826 | return false; |
4827 | } |
4828 | |
4829 | case GT_ARR_BOUNDS_CHECK: |
4830 | #ifdef FEATURE_SIMD |
4831 | case GT_SIMD_CHK: |
4832 | #endif // FEATURE_SIMD |
4833 | #ifdef FEATURE_HW_INTRINSICS |
4834 | case GT_HW_INTRINSIC_CHK: |
4835 | #endif // FEATURE_HW_INTRINSICS |
4836 | { |
4837 | GenTreeBoundsChk* const boundsChk = this->AsBoundsChk(); |
4838 | if (def == boundsChk->gtIndex) |
4839 | { |
4840 | *use = &boundsChk->gtIndex; |
4841 | return true; |
4842 | } |
4843 | if (def == boundsChk->gtArrLen) |
4844 | { |
4845 | *use = &boundsChk->gtArrLen; |
4846 | return true; |
4847 | } |
4848 | return false; |
4849 | } |
4850 | |
4851 | case GT_FIELD: |
4852 | if (def == this->AsField()->gtFldObj) |
4853 | { |
4854 | *use = &this->AsField()->gtFldObj; |
4855 | return true; |
4856 | } |
4857 | return false; |
4858 | |
4859 | case GT_STMT: |
4860 | if (def == this->AsStmt()->gtStmtExpr) |
4861 | { |
4862 | *use = &this->AsStmt()->gtStmtExpr; |
4863 | return true; |
4864 | } |
4865 | return false; |
4866 | |
4867 | case GT_ARR_ELEM: |
4868 | { |
4869 | GenTreeArrElem* const arrElem = this->AsArrElem(); |
4870 | if (def == arrElem->gtArrObj) |
4871 | { |
4872 | *use = &arrElem->gtArrObj; |
4873 | return true; |
4874 | } |
4875 | for (unsigned i = 0; i < arrElem->gtArrRank; i++) |
4876 | { |
4877 | if (def == arrElem->gtArrInds[i]) |
4878 | { |
4879 | *use = &arrElem->gtArrInds[i]; |
4880 | return true; |
4881 | } |
4882 | } |
4883 | return false; |
4884 | } |
4885 | |
4886 | case GT_ARR_OFFSET: |
4887 | { |
4888 | GenTreeArrOffs* const arrOffs = this->AsArrOffs(); |
4889 | if (def == arrOffs->gtOffset) |
4890 | { |
4891 | *use = &arrOffs->gtOffset; |
4892 | return true; |
4893 | } |
4894 | if (def == arrOffs->gtIndex) |
4895 | { |
4896 | *use = &arrOffs->gtIndex; |
4897 | return true; |
4898 | } |
4899 | if (def == arrOffs->gtArrObj) |
4900 | { |
4901 | *use = &arrOffs->gtArrObj; |
4902 | return true; |
4903 | } |
4904 | return false; |
4905 | } |
4906 | |
4907 | case GT_DYN_BLK: |
4908 | { |
4909 | GenTreeDynBlk* const dynBlock = this->AsDynBlk(); |
4910 | if (def == dynBlock->gtOp1) |
4911 | { |
4912 | *use = &dynBlock->gtOp1; |
4913 | return true; |
4914 | } |
4915 | if (def == dynBlock->gtDynamicSize) |
4916 | { |
4917 | *use = &dynBlock->gtDynamicSize; |
4918 | return true; |
4919 | } |
4920 | return false; |
4921 | } |
4922 | |
4923 | case GT_STORE_DYN_BLK: |
4924 | { |
4925 | GenTreeDynBlk* const dynBlock = this->AsDynBlk(); |
4926 | if (def == dynBlock->gtOp1) |
4927 | { |
4928 | *use = &dynBlock->gtOp1; |
4929 | return true; |
4930 | } |
4931 | if (def == dynBlock->gtOp2) |
4932 | { |
4933 | *use = &dynBlock->gtOp2; |
4934 | return true; |
4935 | } |
4936 | if (def == dynBlock->gtDynamicSize) |
4937 | { |
4938 | *use = &dynBlock->gtDynamicSize; |
4939 | return true; |
4940 | } |
4941 | return false; |
4942 | } |
4943 | |
4944 | case GT_CALL: |
4945 | { |
4946 | GenTreeCall* const call = this->AsCall(); |
4947 | if (def == call->gtCallObjp) |
4948 | { |
4949 | *use = &call->gtCallObjp; |
4950 | return true; |
4951 | } |
4952 | if (def == call->gtControlExpr) |
4953 | { |
4954 | *use = &call->gtControlExpr; |
4955 | return true; |
4956 | } |
4957 | if (call->gtCallType == CT_INDIRECT) |
4958 | { |
4959 | if (def == call->gtCallCookie) |
4960 | { |
4961 | *use = &call->gtCallCookie; |
4962 | return true; |
4963 | } |
4964 | if (def == call->gtCallAddr) |
4965 | { |
4966 | *use = &call->gtCallAddr; |
4967 | return true; |
4968 | } |
4969 | } |
4970 | if ((call->gtCallArgs != nullptr) && call->gtCallArgs->TryGetUseList(def, use)) |
4971 | { |
4972 | return true; |
4973 | } |
4974 | |
4975 | return (call->gtCallLateArgs != nullptr) && call->gtCallLateArgs->TryGetUseList(def, use); |
4976 | } |
4977 | |
4978 | // Binary nodes |
4979 | default: |
4980 | assert(this->OperIsBinary()); |
4981 | return TryGetUseBinOp(def, use); |
4982 | } |
4983 | } |
4984 | |
4985 | bool GenTree::TryGetUseList(GenTree* def, GenTree*** use) |
4986 | { |
4987 | assert(def != nullptr); |
4988 | assert(use != nullptr); |
4989 | |
4990 | for (GenTreeArgList* node = this->AsArgList(); node != nullptr; node = node->Rest()) |
4991 | { |
4992 | if (def == node->gtOp1) |
4993 | { |
4994 | *use = &node->gtOp1; |
4995 | return true; |
4996 | } |
4997 | } |
4998 | return false; |
4999 | } |
5000 | |
5001 | bool GenTree::TryGetUseBinOp(GenTree* def, GenTree*** use) |
5002 | { |
5003 | assert(def != nullptr); |
5004 | assert(use != nullptr); |
5005 | assert(this->OperIsBinary()); |
5006 | |
5007 | GenTreeOp* const binOp = this->AsOp(); |
5008 | if (def == binOp->gtOp1) |
5009 | { |
5010 | *use = &binOp->gtOp1; |
5011 | return true; |
5012 | } |
5013 | if (def == binOp->gtOp2) |
5014 | { |
5015 | *use = &binOp->gtOp2; |
5016 | return true; |
5017 | } |
5018 | return false; |
5019 | } |
5020 | |
5021 | //------------------------------------------------------------------------ |
5022 | // GenTree::ReplaceOperand: |
5023 | // Replace a given operand to this node with a new operand. If the |
5024 | // current node is a call node, this will also udpate the call |
5025 | // argument table if necessary. |
5026 | // |
5027 | // Arguments: |
5028 | // useEdge - the use edge that points to the operand to be replaced. |
5029 | // replacement - the replacement node. |
5030 | // |
5031 | void GenTree::ReplaceOperand(GenTree** useEdge, GenTree* replacement) |
5032 | { |
5033 | assert(useEdge != nullptr); |
5034 | assert(replacement != nullptr); |
5035 | assert(TryGetUse(*useEdge, &useEdge)); |
5036 | |
5037 | if (OperGet() == GT_CALL) |
5038 | { |
5039 | AsCall()->ReplaceCallOperand(useEdge, replacement); |
5040 | } |
5041 | else |
5042 | { |
5043 | *useEdge = replacement; |
5044 | } |
5045 | } |
5046 | |
5047 | //------------------------------------------------------------------------ |
5048 | // gtGetParent: Get the parent of this node, and optionally capture the |
5049 | // pointer to the child so that it can be modified. |
5050 | // |
5051 | // Arguments: |
5052 | |
5053 | // parentChildPointer - A pointer to a GenTree** (yes, that's three |
5054 | // levels, i.e. GenTree ***), which if non-null, |
5055 | // will be set to point to the field in the parent |
5056 | // that points to this node. |
5057 | // |
5058 | // Return value - The parent of this node. |
5059 | // |
5060 | // Notes: |
5061 | // |
5062 | // This requires that the execution order must be defined (i.e. gtSetEvalOrder() has been called). |
5063 | // To enable the child to be replaced, it accepts an argument, parentChildPointer that, if non-null, |
5064 | // will be set to point to the child pointer in the parent that points to this node. |
5065 | |
5066 | GenTree* GenTree::gtGetParent(GenTree*** parentChildPtrPtr) const |
5067 | { |
5068 | // Find the parent node; it must be after this node in the execution order. |
5069 | GenTree** parentChildPtr = nullptr; |
5070 | GenTree* parent; |
5071 | for (parent = gtNext; parent != nullptr; parent = parent->gtNext) |
5072 | { |
5073 | parentChildPtr = gtGetChildPointer(parent); |
5074 | if (parentChildPtr != nullptr) |
5075 | { |
5076 | break; |
5077 | } |
5078 | } |
5079 | if (parentChildPtrPtr != nullptr) |
5080 | { |
5081 | *parentChildPtrPtr = parentChildPtr; |
5082 | } |
5083 | return parent; |
5084 | } |
5085 | |
5086 | //------------------------------------------------------------------------------ |
5087 | // OperRequiresAsgFlag : Check whether the operation requires GTF_ASG flag regardless |
5088 | // of the children's flags. |
5089 | // |
5090 | |
5091 | bool GenTree::OperRequiresAsgFlag() |
5092 | { |
5093 | if (OperIs(GT_ASG) || OperIs(GT_XADD, GT_XCHG, GT_LOCKADD, GT_CMPXCHG, GT_MEMORYBARRIER)) |
5094 | { |
5095 | return true; |
5096 | } |
5097 | #ifdef FEATURE_HW_INTRINSICS |
5098 | if (gtOper == GT_HWIntrinsic) |
5099 | { |
5100 | GenTreeHWIntrinsic* hwIntrinsicNode = this->AsHWIntrinsic(); |
5101 | if (hwIntrinsicNode->OperIsMemoryStore()) |
5102 | { |
5103 | // A MemoryStore operation is an assignment |
5104 | return true; |
5105 | } |
5106 | } |
5107 | #endif // FEATURE_HW_INTRINSICS |
5108 | return false; |
5109 | } |
5110 | |
5111 | //------------------------------------------------------------------------------ |
5112 | // OperRequiresCallFlag : Check whether the operation requires GTF_CALL flag regardless |
5113 | // of the children's flags. |
5114 | // |
5115 | |
5116 | bool GenTree::OperRequiresCallFlag(Compiler* comp) |
5117 | { |
5118 | switch (gtOper) |
5119 | { |
5120 | case GT_CALL: |
5121 | return true; |
5122 | |
5123 | case GT_INTRINSIC: |
5124 | return comp->IsIntrinsicImplementedByUserCall(this->AsIntrinsic()->gtIntrinsicId); |
5125 | |
5126 | #if FEATURE_FIXED_OUT_ARGS && !defined(_TARGET_64BIT_) |
5127 | case GT_LSH: |
5128 | case GT_RSH: |
5129 | case GT_RSZ: |
5130 | |
5131 | // Variable shifts of a long end up being helper calls, so mark the tree as such in morph. |
5132 | // This is potentially too conservative, since they'll get treated as having side effects. |
5133 | // It is important to mark them as calls so if they are part of an argument list, |
5134 | // they will get sorted and processed properly (for example, it is important to handle |
5135 | // all nested calls before putting struct arguments in the argument registers). We |
5136 | // could mark the trees just before argument processing, but it would require a full |
5137 | // tree walk of the argument tree, so we just do it when morphing, instead, even though we'll |
5138 | // mark non-argument trees (that will still get converted to calls, anyway). |
5139 | return (this->TypeGet() == TYP_LONG) && (gtGetOp2()->OperGet() != GT_CNS_INT); |
5140 | #endif // FEATURE_FIXED_OUT_ARGS && !_TARGET_64BIT_ |
5141 | |
5142 | default: |
5143 | return false; |
5144 | } |
5145 | } |
5146 | |
5147 | //------------------------------------------------------------------------------ |
5148 | // OperIsImplicitIndir : Check whether the operation contains an implicit |
5149 | // indirection. |
5150 | // Arguments: |
5151 | // this - a GenTree node |
5152 | // |
5153 | // Return Value: |
5154 | // True if the given node contains an implicit indirection |
5155 | // |
5156 | // Note that for the GT_HWIntrinsic node we have to examine the |
5157 | // details of the node to determine its result. |
5158 | // |
5159 | |
5160 | bool GenTree::OperIsImplicitIndir() const |
5161 | { |
5162 | switch (gtOper) |
5163 | { |
5164 | case GT_LOCKADD: |
5165 | case GT_XADD: |
5166 | case GT_XCHG: |
5167 | case GT_CMPXCHG: |
5168 | case GT_BLK: |
5169 | case GT_OBJ: |
5170 | case GT_DYN_BLK: |
5171 | case GT_STORE_BLK: |
5172 | case GT_STORE_OBJ: |
5173 | case GT_STORE_DYN_BLK: |
5174 | case GT_BOX: |
5175 | case GT_ARR_INDEX: |
5176 | case GT_ARR_ELEM: |
5177 | case GT_ARR_OFFSET: |
5178 | return true; |
5179 | #ifdef FEATURE_HW_INTRINSICS |
5180 | case GT_HWIntrinsic: |
5181 | { |
5182 | GenTreeHWIntrinsic* hwIntrinsicNode = (const_cast<GenTree*>(this))->AsHWIntrinsic(); |
5183 | return hwIntrinsicNode->OperIsMemoryLoadOrStore(); |
5184 | } |
5185 | #endif // FEATURE_HW_INTRINSICS |
5186 | default: |
5187 | return false; |
5188 | } |
5189 | } |
5190 | |
5191 | //------------------------------------------------------------------------------ |
5192 | // OperMayThrow : Check whether the operation may throw. |
5193 | // |
5194 | // |
5195 | // Arguments: |
5196 | // comp - Compiler instance |
5197 | // |
5198 | // Return Value: |
5199 | // True if the given operator may cause an exception |
5200 | |
5201 | bool GenTree::OperMayThrow(Compiler* comp) |
5202 | { |
5203 | GenTree* op; |
5204 | |
5205 | switch (gtOper) |
5206 | { |
5207 | case GT_MOD: |
5208 | case GT_DIV: |
5209 | case GT_UMOD: |
5210 | case GT_UDIV: |
5211 | |
5212 | /* Division with a non-zero, non-minus-one constant does not throw an exception */ |
5213 | |
5214 | op = gtOp.gtOp2; |
5215 | |
5216 | if (varTypeIsFloating(op->TypeGet())) |
5217 | { |
5218 | return false; // Floating point division does not throw. |
5219 | } |
5220 | |
5221 | // For integers only division by 0 or by -1 can throw |
5222 | if (op->IsIntegralConst() && !op->IsIntegralConst(0) && !op->IsIntegralConst(-1)) |
5223 | { |
5224 | return false; |
5225 | } |
5226 | return true; |
5227 | |
5228 | case GT_INTRINSIC: |
5229 | // If this is an intrinsic that represents the object.GetType(), it can throw an NullReferenceException. |
5230 | // Report it as may throw. |
5231 | // Note: Some of the rest of the existing intrinsics could potentially throw an exception (for example |
5232 | // the array and string element access ones). They are handled differently than the GetType intrinsic |
5233 | // and are not marked with GTF_EXCEPT. If these are revisited at some point to be marked as |
5234 | // GTF_EXCEPT, |
5235 | // the code below might need to be specialized to handle them properly. |
5236 | if ((this->gtFlags & GTF_EXCEPT) != 0) |
5237 | { |
5238 | return true; |
5239 | } |
5240 | |
5241 | break; |
5242 | |
5243 | case GT_CALL: |
5244 | |
5245 | CorInfoHelpFunc helper; |
5246 | helper = comp->eeGetHelperNum(this->AsCall()->gtCallMethHnd); |
5247 | return ((helper == CORINFO_HELP_UNDEF) || !comp->s_helperCallProperties.NoThrow(helper)); |
5248 | |
5249 | case GT_IND: |
5250 | case GT_BLK: |
5251 | case GT_OBJ: |
5252 | case GT_DYN_BLK: |
5253 | case GT_STORE_BLK: |
5254 | case GT_NULLCHECK: |
5255 | return (((this->gtFlags & GTF_IND_NONFAULTING) == 0) && comp->fgAddrCouldBeNull(this->AsIndir()->Addr())); |
5256 | |
5257 | case GT_ARR_LENGTH: |
5258 | return (((this->gtFlags & GTF_IND_NONFAULTING) == 0) && |
5259 | comp->fgAddrCouldBeNull(this->AsArrLen()->ArrRef())); |
5260 | |
5261 | case GT_ARR_ELEM: |
5262 | return comp->fgAddrCouldBeNull(this->gtArrElem.gtArrObj); |
5263 | |
5264 | case GT_ARR_BOUNDS_CHECK: |
5265 | case GT_ARR_INDEX: |
5266 | case GT_ARR_OFFSET: |
5267 | case GT_LCLHEAP: |
5268 | case GT_CKFINITE: |
5269 | #ifdef FEATURE_SIMD |
5270 | case GT_SIMD_CHK: |
5271 | #endif // FEATURE_SIMD |
5272 | #ifdef FEATURE_HW_INTRINSICS |
5273 | case GT_HW_INTRINSIC_CHK: |
5274 | #endif // FEATURE_HW_INTRINSICS |
5275 | case GT_INDEX_ADDR: |
5276 | return true; |
5277 | |
5278 | #ifdef FEATURE_HW_INTRINSICS |
5279 | case GT_HWIntrinsic: |
5280 | { |
5281 | GenTreeHWIntrinsic* hwIntrinsicNode = this->AsHWIntrinsic(); |
5282 | assert(hwIntrinsicNode != nullptr); |
5283 | if (hwIntrinsicNode->OperIsMemoryLoadOrStore()) |
5284 | { |
5285 | // This operation contains an implicit indirection |
5286 | // it could throw a null reference exception. |
5287 | // |
5288 | return true; |
5289 | } |
5290 | } |
5291 | #endif // FEATURE_HW_INTRINSICS |
5292 | |
5293 | default: |
5294 | break; |
5295 | } |
5296 | |
5297 | /* Overflow arithmetic operations also throw exceptions */ |
5298 | |
5299 | if (gtOverflowEx()) |
5300 | { |
5301 | return true; |
5302 | } |
5303 | |
5304 | return false; |
5305 | } |
5306 | |
5307 | #if DEBUGGABLE_GENTREE |
5308 | // static |
5309 | GenTree::VtablePtr GenTree::s_vtablesForOpers[] = {nullptr}; |
5310 | GenTree::VtablePtr GenTree::s_vtableForOp = nullptr; |
5311 | |
5312 | GenTree::VtablePtr GenTree::GetVtableForOper(genTreeOps oper) |
5313 | { |
5314 | noway_assert(oper < GT_COUNT); |
5315 | |
5316 | // First, check a cache. |
5317 | |
5318 | if (s_vtablesForOpers[oper] != nullptr) |
5319 | { |
5320 | return s_vtablesForOpers[oper]; |
5321 | } |
5322 | |
5323 | // Otherwise, look up the correct vtable entry. Note that we want the most derived GenTree subtype |
5324 | // for an oper. E.g., GT_LCL_VAR is defined in GTSTRUCT_3 as GenTreeLclVar and in GTSTRUCT_N as |
5325 | // GenTreeLclVarCommon. We want the GenTreeLclVar vtable, since nothing should actually be |
5326 | // instantiated as a GenTreeLclVarCommon. |
5327 | |
5328 | VtablePtr res = nullptr; |
5329 | switch (oper) |
5330 | { |
5331 | |
5332 | // clang-format off |
5333 | |
5334 | #define GTSTRUCT_0(nm, tag) /*handle explicitly*/ |
5335 | #define GTSTRUCT_1(nm, tag) \ |
5336 | case tag: \ |
5337 | { \ |
5338 | GenTree##nm gt; \ |
5339 | res = *reinterpret_cast<VtablePtr*>(>); \ |
5340 | } \ |
5341 | break; |
5342 | #define GTSTRUCT_2(nm, tag, tag2) \ |
5343 | case tag: \ |
5344 | case tag2: \ |
5345 | { \ |
5346 | GenTree##nm gt; \ |
5347 | res = *reinterpret_cast<VtablePtr*>(>); \ |
5348 | } \ |
5349 | break; |
5350 | #define GTSTRUCT_3(nm, tag, tag2, tag3) \ |
5351 | case tag: \ |
5352 | case tag2: \ |
5353 | case tag3: \ |
5354 | { \ |
5355 | GenTree##nm gt; \ |
5356 | res = *reinterpret_cast<VtablePtr*>(>); \ |
5357 | } \ |
5358 | break; |
5359 | #define GTSTRUCT_4(nm, tag, tag2, tag3, tag4) \ |
5360 | case tag: \ |
5361 | case tag2: \ |
5362 | case tag3: \ |
5363 | case tag4: \ |
5364 | { \ |
5365 | GenTree##nm gt; \ |
5366 | res = *reinterpret_cast<VtablePtr*>(>); \ |
5367 | } \ |
5368 | break; |
5369 | #define GTSTRUCT_N(nm, ...) /*handle explicitly*/ |
5370 | #define GTSTRUCT_2_SPECIAL(nm, tag, tag2) /*handle explicitly*/ |
5371 | #define GTSTRUCT_3_SPECIAL(nm, tag, tag2, tag3) /*handle explicitly*/ |
5372 | #include "gtstructs.h" |
5373 | |
5374 | // clang-format on |
5375 | |
5376 | // Handle the special cases. |
5377 | // The following opers are in GTSTRUCT_N but no other place (namely, no subtypes). |
5378 | |
5379 | case GT_STORE_BLK: |
5380 | case GT_BLK: |
5381 | { |
5382 | GenTreeBlk gt; |
5383 | res = *reinterpret_cast<VtablePtr*>(>); |
5384 | } |
5385 | break; |
5386 | |
5387 | case GT_IND: |
5388 | case GT_NULLCHECK: |
5389 | { |
5390 | GenTreeIndir gt; |
5391 | res = *reinterpret_cast<VtablePtr*>(>); |
5392 | } |
5393 | break; |
5394 | |
5395 | // Handle GT_LIST (but not GT_FIELD_LIST, which is also in a GTSTRUCT_1). |
5396 | |
5397 | case GT_LIST: |
5398 | { |
5399 | GenTreeArgList gt; |
5400 | res = *reinterpret_cast<VtablePtr*>(>); |
5401 | } |
5402 | break; |
5403 | |
5404 | // We don't need to handle GTSTRUCT_N for LclVarCommon, since all those allowed opers are specified |
5405 | // in their proper subtype. Similarly for GenTreeIndir. |
5406 | |
5407 | default: |
5408 | { |
5409 | // Should be unary or binary op. |
5410 | if (s_vtableForOp == nullptr) |
5411 | { |
5412 | unsigned opKind = OperKind(oper); |
5413 | assert(!IsExOp(opKind)); |
5414 | assert(OperIsSimple(oper) || OperIsLeaf(oper)); |
5415 | // Need to provide non-null operands. |
5416 | GenTreeIntCon dummyOp(TYP_INT, 0); |
5417 | GenTreeOp gt(oper, TYP_INT, &dummyOp, ((opKind & GTK_UNOP) ? nullptr : &dummyOp)); |
5418 | s_vtableForOp = *reinterpret_cast<VtablePtr*>(>); |
5419 | } |
5420 | res = s_vtableForOp; |
5421 | break; |
5422 | } |
5423 | } |
5424 | s_vtablesForOpers[oper] = res; |
5425 | return res; |
5426 | } |
5427 | |
5428 | void GenTree::SetVtableForOper(genTreeOps oper) |
5429 | { |
5430 | *reinterpret_cast<VtablePtr*>(this) = GetVtableForOper(oper); |
5431 | } |
5432 | #endif // DEBUGGABLE_GENTREE |
5433 | |
5434 | GenTree* Compiler::gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1, GenTree* op2) |
5435 | { |
5436 | assert(op1 != nullptr); |
5437 | assert(op2 != nullptr); |
5438 | |
5439 | // We should not be allocating nodes that extend GenTreeOp with this; |
5440 | // should call the appropriate constructor for the extended type. |
5441 | assert(!GenTree::IsExOp(GenTree::OperKind(oper))); |
5442 | |
5443 | GenTree* node = new (this, oper) GenTreeOp(oper, type, op1, op2); |
5444 | |
5445 | return node; |
5446 | } |
5447 | |
5448 | GenTree* Compiler::gtNewQmarkNode(var_types type, GenTree* cond, GenTree* colon) |
5449 | { |
5450 | compQmarkUsed = true; |
5451 | cond->gtFlags |= GTF_RELOP_QMARK; |
5452 | GenTree* result = new (this, GT_QMARK) GenTreeQmark(type, cond, colon, this); |
5453 | #ifdef DEBUG |
5454 | if (compQmarkRationalized) |
5455 | { |
5456 | fgCheckQmarkAllowedForm(result); |
5457 | } |
5458 | #endif |
5459 | return result; |
5460 | } |
5461 | |
5462 | GenTreeQmark::GenTreeQmark(var_types type, GenTree* cond, GenTree* colonOp, Compiler* comp) |
5463 | : GenTreeOp(GT_QMARK, type, cond, colonOp) |
5464 | { |
5465 | // These must follow a specific form. |
5466 | assert(cond != nullptr && cond->TypeGet() == TYP_INT); |
5467 | assert(colonOp != nullptr && colonOp->OperGet() == GT_COLON); |
5468 | } |
5469 | |
5470 | GenTreeIntCon* Compiler::gtNewIconNode(ssize_t value, var_types type) |
5471 | { |
5472 | return new (this, GT_CNS_INT) GenTreeIntCon(type, value); |
5473 | } |
5474 | |
5475 | // return a new node representing the value in a physical register |
5476 | GenTree* Compiler::gtNewPhysRegNode(regNumber reg, var_types type) |
5477 | { |
5478 | assert(genIsValidIntReg(reg) || (reg == REG_SPBASE)); |
5479 | GenTree* result = new (this, GT_PHYSREG) GenTreePhysReg(reg, type); |
5480 | return result; |
5481 | } |
5482 | |
5483 | GenTree* Compiler::gtNewJmpTableNode() |
5484 | { |
5485 | GenTree* node = new (this, GT_JMPTABLE) GenTreeJumpTable(TYP_INT); |
5486 | node->gtJumpTable.gtJumpTableAddr = 0; |
5487 | return node; |
5488 | } |
5489 | |
5490 | /***************************************************************************** |
5491 | * |
5492 | * Converts an annotated token into an icon flags (so that we will later be |
5493 | * able to tell the type of the handle that will be embedded in the icon |
5494 | * node) |
5495 | */ |
5496 | |
5497 | unsigned Compiler::gtTokenToIconFlags(unsigned token) |
5498 | { |
5499 | unsigned flags = 0; |
5500 | |
5501 | switch (TypeFromToken(token)) |
5502 | { |
5503 | case mdtTypeRef: |
5504 | case mdtTypeDef: |
5505 | case mdtTypeSpec: |
5506 | flags = GTF_ICON_CLASS_HDL; |
5507 | break; |
5508 | |
5509 | case mdtMethodDef: |
5510 | flags = GTF_ICON_METHOD_HDL; |
5511 | break; |
5512 | |
5513 | case mdtFieldDef: |
5514 | flags = GTF_ICON_FIELD_HDL; |
5515 | break; |
5516 | |
5517 | default: |
5518 | flags = GTF_ICON_TOKEN_HDL; |
5519 | break; |
5520 | } |
5521 | |
5522 | return flags; |
5523 | } |
5524 | |
5525 | //----------------------------------------------------------------------------------------- |
5526 | // gtNewIndOfIconHandleNode: Creates an indirection GenTree node of a constant handle |
5527 | // |
5528 | // Arguments: |
5529 | // indType - The type returned by the indirection node |
5530 | // addr - The constant address to read from |
5531 | // iconFlags - The GTF_ICON flag value that specifies the kind of handle that we have |
5532 | // isInvariant - The indNode should also be marked as invariant |
5533 | // |
5534 | // Return Value: |
5535 | // Returns a GT_IND node representing value at the address provided by 'value' |
5536 | // |
5537 | // Notes: |
5538 | // The GT_IND node is marked as non-faulting |
5539 | // If the indType is GT_REF we also mark the indNode as GTF_GLOB_REF |
5540 | // |
5541 | |
5542 | GenTree* Compiler::gtNewIndOfIconHandleNode(var_types indType, size_t addr, unsigned iconFlags, bool isInvariant) |
5543 | { |
5544 | GenTree* addrNode = gtNewIconHandleNode(addr, iconFlags); |
5545 | GenTree* indNode = gtNewOperNode(GT_IND, indType, addrNode); |
5546 | |
5547 | // This indirection won't cause an exception. |
5548 | // |
5549 | indNode->gtFlags |= GTF_IND_NONFAULTING; |
5550 | |
5551 | // String Literal handles are indirections that return a TYP_REF. |
5552 | // They are pointers into the GC heap and they are not invariant |
5553 | // as the address is a reportable GC-root and as such it can be |
5554 | // modified during a GC collection |
5555 | // |
5556 | if (indType == TYP_REF) |
5557 | { |
5558 | // This indirection points into the gloabal heap |
5559 | indNode->gtFlags |= GTF_GLOB_REF; |
5560 | } |
5561 | if (isInvariant) |
5562 | { |
5563 | // This indirection also is invariant. |
5564 | indNode->gtFlags |= GTF_IND_INVARIANT; |
5565 | } |
5566 | return indNode; |
5567 | } |
5568 | |
5569 | /***************************************************************************** |
5570 | * |
5571 | * Allocates a integer constant entry that represents a HANDLE to something. |
5572 | * It may not be allowed to embed HANDLEs directly into the JITed code (for eg, |
5573 | * as arguments to JIT helpers). Get a corresponding value that can be embedded. |
5574 | * If the handle needs to be accessed via an indirection, pValue points to it. |
5575 | */ |
5576 | |
5577 | GenTree* Compiler::gtNewIconEmbHndNode(void* value, void* pValue, unsigned iconFlags, void* compileTimeHandle) |
5578 | { |
5579 | GenTree* iconNode; |
5580 | GenTree* handleNode; |
5581 | |
5582 | if (value != nullptr) |
5583 | { |
5584 | // When 'value' is non-null, pValue is required to be null |
5585 | assert(pValue == nullptr); |
5586 | |
5587 | // use 'value' to construct an integer constant node |
5588 | iconNode = gtNewIconHandleNode((size_t)value, iconFlags); |
5589 | |
5590 | // 'value' is the handle |
5591 | handleNode = iconNode; |
5592 | } |
5593 | else |
5594 | { |
5595 | // When 'value' is null, pValue is required to be non-null |
5596 | assert(pValue != nullptr); |
5597 | |
5598 | // use 'pValue' to construct an integer constant node |
5599 | iconNode = gtNewIconHandleNode((size_t)pValue, iconFlags); |
5600 | |
5601 | // 'pValue' is an address of a location that contains the handle |
5602 | |
5603 | // construct the indirection of 'pValue' |
5604 | handleNode = gtNewOperNode(GT_IND, TYP_I_IMPL, iconNode); |
5605 | |
5606 | // This indirection won't cause an exception. |
5607 | handleNode->gtFlags |= GTF_IND_NONFAULTING; |
5608 | #if 0 |
5609 | // It should also be invariant, but marking it as such leads to bad diffs. |
5610 | |
5611 | // This indirection also is invariant. |
5612 | handleNode->gtFlags |= GTF_IND_INVARIANT; |
5613 | #endif |
5614 | } |
5615 | |
5616 | iconNode->gtIntCon.gtCompileTimeHandle = (size_t)compileTimeHandle; |
5617 | |
5618 | return handleNode; |
5619 | } |
5620 | |
5621 | /*****************************************************************************/ |
5622 | GenTree* Compiler::gtNewStringLiteralNode(InfoAccessType iat, void* pValue) |
5623 | { |
5624 | GenTree* tree = nullptr; |
5625 | |
5626 | switch (iat) |
5627 | { |
5628 | case IAT_VALUE: // constructStringLiteral in CoreRT case can return IAT_VALUE |
5629 | tree = gtNewIconEmbHndNode(pValue, nullptr, GTF_ICON_STR_HDL, nullptr); |
5630 | tree->gtType = TYP_REF; |
5631 | tree = gtNewOperNode(GT_NOP, TYP_REF, tree); // prevents constant folding |
5632 | break; |
5633 | |
5634 | case IAT_PVALUE: // The value needs to be accessed via an indirection |
5635 | // Create an indirection |
5636 | tree = gtNewIndOfIconHandleNode(TYP_REF, (size_t)pValue, GTF_ICON_STR_HDL, false); |
5637 | break; |
5638 | |
5639 | case IAT_PPVALUE: // The value needs to be accessed via a double indirection |
5640 | // Create the first indirection |
5641 | tree = gtNewIndOfIconHandleNode(TYP_I_IMPL, (size_t)pValue, GTF_ICON_PSTR_HDL, true); |
5642 | |
5643 | // Create the second indirection |
5644 | tree = gtNewOperNode(GT_IND, TYP_REF, tree); |
5645 | // This indirection won't cause an exception. |
5646 | tree->gtFlags |= GTF_IND_NONFAULTING; |
5647 | // This indirection points into the gloabal heap (it is String Object) |
5648 | tree->gtFlags |= GTF_GLOB_REF; |
5649 | break; |
5650 | |
5651 | default: |
5652 | noway_assert(!"Unexpected InfoAccessType" ); |
5653 | } |
5654 | |
5655 | return tree; |
5656 | } |
5657 | |
5658 | /*****************************************************************************/ |
5659 | |
5660 | GenTree* Compiler::gtNewLconNode(__int64 value) |
5661 | { |
5662 | #ifdef _TARGET_64BIT_ |
5663 | GenTree* node = new (this, GT_CNS_INT) GenTreeIntCon(TYP_LONG, value); |
5664 | #else |
5665 | GenTree* node = new (this, GT_CNS_LNG) GenTreeLngCon(value); |
5666 | #endif |
5667 | |
5668 | return node; |
5669 | } |
5670 | |
5671 | GenTree* Compiler::gtNewDconNode(double value) |
5672 | { |
5673 | GenTree* node = new (this, GT_CNS_DBL) GenTreeDblCon(value); |
5674 | |
5675 | return node; |
5676 | } |
5677 | |
5678 | GenTree* Compiler::gtNewSconNode(int CPX, CORINFO_MODULE_HANDLE scpHandle) |
5679 | { |
5680 | |
5681 | #if SMALL_TREE_NODES |
5682 | |
5683 | /* 'GT_CNS_STR' nodes later get transformed into 'GT_CALL' */ |
5684 | |
5685 | assert(GenTree::s_gtNodeSizes[GT_CALL] > GenTree::s_gtNodeSizes[GT_CNS_STR]); |
5686 | |
5687 | GenTree* node = new (this, GT_CALL) GenTreeStrCon(CPX, scpHandle DEBUGARG(/*largeNode*/ true)); |
5688 | #else |
5689 | GenTree* node = new (this, GT_CNS_STR) GenTreeStrCon(CPX, scpHandle DEBUGARG(/*largeNode*/ true)); |
5690 | #endif |
5691 | |
5692 | return node; |
5693 | } |
5694 | |
5695 | GenTree* Compiler::gtNewZeroConNode(var_types type) |
5696 | { |
5697 | GenTree* zero; |
5698 | switch (type) |
5699 | { |
5700 | case TYP_INT: |
5701 | zero = gtNewIconNode(0); |
5702 | break; |
5703 | |
5704 | case TYP_BYREF: |
5705 | __fallthrough; |
5706 | |
5707 | case TYP_REF: |
5708 | zero = gtNewIconNode(0); |
5709 | zero->gtType = type; |
5710 | break; |
5711 | |
5712 | case TYP_LONG: |
5713 | zero = gtNewLconNode(0); |
5714 | break; |
5715 | |
5716 | case TYP_FLOAT: |
5717 | zero = gtNewDconNode(0.0); |
5718 | zero->gtType = type; |
5719 | break; |
5720 | |
5721 | case TYP_DOUBLE: |
5722 | zero = gtNewDconNode(0.0); |
5723 | break; |
5724 | |
5725 | default: |
5726 | noway_assert(!"Bad type in gtNewZeroConNode" ); |
5727 | zero = nullptr; |
5728 | break; |
5729 | } |
5730 | return zero; |
5731 | } |
5732 | |
5733 | GenTree* Compiler::gtNewOneConNode(var_types type) |
5734 | { |
5735 | GenTree* one; |
5736 | switch (type) |
5737 | { |
5738 | case TYP_INT: |
5739 | case TYP_UINT: |
5740 | one = gtNewIconNode(1); |
5741 | break; |
5742 | |
5743 | case TYP_LONG: |
5744 | case TYP_ULONG: |
5745 | one = gtNewLconNode(1); |
5746 | break; |
5747 | |
5748 | case TYP_FLOAT: |
5749 | case TYP_DOUBLE: |
5750 | one = gtNewDconNode(1.0); |
5751 | one->gtType = type; |
5752 | break; |
5753 | |
5754 | default: |
5755 | noway_assert(!"Bad type in gtNewOneConNode" ); |
5756 | one = nullptr; |
5757 | break; |
5758 | } |
5759 | return one; |
5760 | } |
5761 | |
5762 | #ifdef FEATURE_SIMD |
5763 | //--------------------------------------------------------------------- |
5764 | // gtNewSIMDVectorZero: create a GT_SIMD node for Vector<T>.Zero |
5765 | // |
5766 | // Arguments: |
5767 | // simdType - simd vector type |
5768 | // baseType - element type of vector |
5769 | // size - size of vector in bytes |
5770 | GenTree* Compiler::gtNewSIMDVectorZero(var_types simdType, var_types baseType, unsigned size) |
5771 | { |
5772 | baseType = genActualType(baseType); |
5773 | GenTree* initVal = gtNewZeroConNode(baseType); |
5774 | initVal->gtType = baseType; |
5775 | return gtNewSIMDNode(simdType, initVal, nullptr, SIMDIntrinsicInit, baseType, size); |
5776 | } |
5777 | |
5778 | //--------------------------------------------------------------------- |
5779 | // gtNewSIMDVectorOne: create a GT_SIMD node for Vector<T>.One |
5780 | // |
5781 | // Arguments: |
5782 | // simdType - simd vector type |
5783 | // baseType - element type of vector |
5784 | // size - size of vector in bytes |
5785 | GenTree* Compiler::gtNewSIMDVectorOne(var_types simdType, var_types baseType, unsigned size) |
5786 | { |
5787 | GenTree* initVal; |
5788 | if (varTypeIsSmallInt(baseType)) |
5789 | { |
5790 | unsigned baseSize = genTypeSize(baseType); |
5791 | int val; |
5792 | if (baseSize == 1) |
5793 | { |
5794 | val = 0x01010101; |
5795 | } |
5796 | else |
5797 | { |
5798 | val = 0x00010001; |
5799 | } |
5800 | initVal = gtNewIconNode(val); |
5801 | } |
5802 | else |
5803 | { |
5804 | initVal = gtNewOneConNode(baseType); |
5805 | } |
5806 | |
5807 | baseType = genActualType(baseType); |
5808 | initVal->gtType = baseType; |
5809 | return gtNewSIMDNode(simdType, initVal, nullptr, SIMDIntrinsicInit, baseType, size); |
5810 | } |
5811 | #endif // FEATURE_SIMD |
5812 | |
5813 | GenTreeCall* Compiler::gtNewIndCallNode(GenTree* addr, var_types type, GenTreeArgList* args, IL_OFFSETX ilOffset) |
5814 | { |
5815 | return gtNewCallNode(CT_INDIRECT, (CORINFO_METHOD_HANDLE)addr, type, args, ilOffset); |
5816 | } |
5817 | |
5818 | GenTreeCall* Compiler::gtNewCallNode( |
5819 | gtCallTypes callType, CORINFO_METHOD_HANDLE callHnd, var_types type, GenTreeArgList* args, IL_OFFSETX ilOffset) |
5820 | { |
5821 | GenTreeCall* node = new (this, GT_CALL) GenTreeCall(genActualType(type)); |
5822 | |
5823 | node->gtFlags |= (GTF_CALL | GTF_GLOB_REF); |
5824 | if (args) |
5825 | { |
5826 | node->gtFlags |= (args->gtFlags & GTF_ALL_EFFECT); |
5827 | } |
5828 | node->gtCallType = callType; |
5829 | node->gtCallMethHnd = callHnd; |
5830 | node->gtCallArgs = args; |
5831 | node->gtCallObjp = nullptr; |
5832 | node->fgArgInfo = nullptr; |
5833 | node->callSig = nullptr; |
5834 | node->gtRetClsHnd = nullptr; |
5835 | node->gtControlExpr = nullptr; |
5836 | node->gtCallMoreFlags = 0; |
5837 | |
5838 | if (callType == CT_INDIRECT) |
5839 | { |
5840 | node->gtCallCookie = nullptr; |
5841 | } |
5842 | else |
5843 | { |
5844 | node->gtInlineCandidateInfo = nullptr; |
5845 | } |
5846 | node->gtCallLateArgs = nullptr; |
5847 | node->gtReturnType = type; |
5848 | |
5849 | #ifdef FEATURE_READYTORUN_COMPILER |
5850 | node->gtEntryPoint.addr = nullptr; |
5851 | node->gtEntryPoint.accessType = IAT_VALUE; |
5852 | #endif |
5853 | |
5854 | #if defined(DEBUG) || defined(INLINE_DATA) |
5855 | // These get updated after call node is built. |
5856 | node->gtInlineObservation = InlineObservation::CALLEE_UNUSED_INITIAL; |
5857 | node->gtRawILOffset = BAD_IL_OFFSET; |
5858 | #endif |
5859 | |
5860 | // Spec: Managed Retval sequence points needs to be generated while generating debug info for debuggable code. |
5861 | // |
5862 | // Implementation note: if not generating MRV info genCallSite2ILOffsetMap will be NULL and |
5863 | // codegen will pass BAD_IL_OFFSET as IL offset of a call node to emitter, which will cause emitter |
5864 | // not to emit IP mapping entry. |
5865 | if (opts.compDbgCode && opts.compDbgInfo) |
5866 | { |
5867 | // Managed Retval - IL offset of the call. This offset is used to emit a |
5868 | // CALL_INSTRUCTION type sequence point while emitting corresponding native call. |
5869 | // |
5870 | // TODO-Cleanup: |
5871 | // a) (Opt) We need not store this offset if the method doesn't return a |
5872 | // value. Rather it can be made BAD_IL_OFFSET to prevent a sequence |
5873 | // point being emitted. |
5874 | // |
5875 | // b) (Opt) Add new sequence points only if requested by debugger through |
5876 | // a new boundary type - ICorDebugInfo::BoundaryTypes |
5877 | if (genCallSite2ILOffsetMap == nullptr) |
5878 | { |
5879 | genCallSite2ILOffsetMap = new (getAllocator()) CallSiteILOffsetTable(getAllocator()); |
5880 | } |
5881 | |
5882 | // Make sure that there are no duplicate entries for a given call node |
5883 | assert(!genCallSite2ILOffsetMap->Lookup(node)); |
5884 | genCallSite2ILOffsetMap->Set(node, ilOffset); |
5885 | } |
5886 | |
5887 | // Initialize gtOtherRegs |
5888 | node->ClearOtherRegs(); |
5889 | |
5890 | // Initialize spill flags of gtOtherRegs |
5891 | node->ClearOtherRegFlags(); |
5892 | |
5893 | #if defined(_TARGET_X86_) || defined(_TARGET_ARM_) |
5894 | // Initialize the multi-reg long return info if necessary |
5895 | if (varTypeIsLong(node)) |
5896 | { |
5897 | // The return type will remain as the incoming long type |
5898 | node->gtReturnType = node->gtType; |
5899 | |
5900 | // Initialize Return type descriptor of call node |
5901 | ReturnTypeDesc* retTypeDesc = node->GetReturnTypeDesc(); |
5902 | retTypeDesc->InitializeLongReturnType(this); |
5903 | |
5904 | // must be a long returned in two registers |
5905 | assert(retTypeDesc->GetReturnRegCount() == 2); |
5906 | } |
5907 | #endif // defined(_TARGET_X86_) || defined(_TARGET_ARM_) |
5908 | |
5909 | return node; |
5910 | } |
5911 | |
5912 | GenTree* Compiler::gtNewLclvNode(unsigned lnum, var_types type, IL_OFFSETX ILoffs) |
5913 | { |
5914 | // We need to ensure that all struct values are normalized. |
5915 | // It might be nice to assert this in general, but we have assignments of int to long. |
5916 | if (varTypeIsStruct(type)) |
5917 | { |
5918 | // Make an exception for implicit by-ref parameters during global morph, since |
5919 | // their lvType has been updated to byref but their appearances have not yet all |
5920 | // been rewritten and so may have struct type still. |
5921 | assert(type == lvaTable[lnum].lvType || |
5922 | (lvaIsImplicitByRefLocal(lnum) && fgGlobalMorph && (lvaTable[lnum].lvType == TYP_BYREF))); |
5923 | } |
5924 | GenTree* node = new (this, GT_LCL_VAR) GenTreeLclVar(type, lnum, ILoffs); |
5925 | |
5926 | /* Cannot have this assert because the inliner uses this function |
5927 | * to add temporaries */ |
5928 | |
5929 | // assert(lnum < lvaCount); |
5930 | |
5931 | return node; |
5932 | } |
5933 | |
5934 | GenTree* Compiler::gtNewLclLNode(unsigned lnum, var_types type, IL_OFFSETX ILoffs) |
5935 | { |
5936 | // We need to ensure that all struct values are normalized. |
5937 | // It might be nice to assert this in general, but we have assignments of int to long. |
5938 | if (varTypeIsStruct(type)) |
5939 | { |
5940 | // Make an exception for implicit by-ref parameters during global morph, since |
5941 | // their lvType has been updated to byref but their appearances have not yet all |
5942 | // been rewritten and so may have struct type still. |
5943 | assert(type == lvaTable[lnum].lvType || |
5944 | (lvaIsImplicitByRefLocal(lnum) && fgGlobalMorph && (lvaTable[lnum].lvType == TYP_BYREF))); |
5945 | } |
5946 | #if SMALL_TREE_NODES |
5947 | /* This local variable node may later get transformed into a large node */ |
5948 | |
5949 | // assert(GenTree::s_gtNodeSizes[GT_CALL] > GenTree::s_gtNodeSizes[GT_LCL_VAR]); |
5950 | |
5951 | GenTree* node = new (this, GT_CALL) GenTreeLclVar(type, lnum, ILoffs DEBUGARG(/*largeNode*/ true)); |
5952 | #else |
5953 | GenTree* node = new (this, GT_LCL_VAR) GenTreeLclVar(type, lnum, ILoffs DEBUGARG(/*largeNode*/ true)); |
5954 | #endif |
5955 | |
5956 | return node; |
5957 | } |
5958 | |
5959 | GenTreeLclFld* Compiler::gtNewLclFldNode(unsigned lnum, var_types type, unsigned offset) |
5960 | { |
5961 | GenTreeLclFld* node = new (this, GT_LCL_FLD) GenTreeLclFld(type, lnum, offset); |
5962 | |
5963 | /* Cannot have this assert because the inliner uses this function |
5964 | * to add temporaries */ |
5965 | |
5966 | // assert(lnum < lvaCount); |
5967 | |
5968 | node->gtFieldSeq = FieldSeqStore::NotAField(); |
5969 | return node; |
5970 | } |
5971 | |
5972 | GenTree* Compiler::gtNewInlineCandidateReturnExpr(GenTree* inlineCandidate, var_types type) |
5973 | |
5974 | { |
5975 | assert(GenTree::s_gtNodeSizes[GT_RET_EXPR] == TREE_NODE_SZ_LARGE); |
5976 | |
5977 | GenTree* node = new (this, GT_RET_EXPR) GenTreeRetExpr(type); |
5978 | |
5979 | node->gtRetExpr.gtInlineCandidate = inlineCandidate; |
5980 | |
5981 | if (varTypeIsStruct(inlineCandidate) && !inlineCandidate->OperIsBlkOp()) |
5982 | { |
5983 | node->gtRetExpr.gtRetClsHnd = gtGetStructHandle(inlineCandidate); |
5984 | } |
5985 | |
5986 | // GT_RET_EXPR node eventually might be bashed back to GT_CALL (when inlining is aborted for example). |
5987 | // Therefore it should carry the GTF_CALL flag so that all the rules about spilling can apply to it as well. |
5988 | // For example, impImportLeave or CEE_POP need to spill GT_RET_EXPR before empty the evaluation stack. |
5989 | node->gtFlags |= GTF_CALL; |
5990 | |
5991 | return node; |
5992 | } |
5993 | |
5994 | GenTreeArgList* Compiler::gtNewListNode(GenTree* op1, GenTreeArgList* op2) |
5995 | { |
5996 | assert((op1 != nullptr) && (op1->OperGet() != GT_LIST)); |
5997 | |
5998 | return new (this, GT_LIST) GenTreeArgList(op1, op2); |
5999 | } |
6000 | |
6001 | /***************************************************************************** |
6002 | * |
6003 | * Create a list out of one value. |
6004 | */ |
6005 | |
6006 | GenTreeArgList* Compiler::gtNewArgList(GenTree* arg) |
6007 | { |
6008 | return new (this, GT_LIST) GenTreeArgList(arg); |
6009 | } |
6010 | |
6011 | /***************************************************************************** |
6012 | * |
6013 | * Create a list out of the two values. |
6014 | */ |
6015 | |
6016 | GenTreeArgList* Compiler::gtNewArgList(GenTree* arg1, GenTree* arg2) |
6017 | { |
6018 | return new (this, GT_LIST) GenTreeArgList(arg1, gtNewArgList(arg2)); |
6019 | } |
6020 | |
6021 | /***************************************************************************** |
6022 | * |
6023 | * Create a list out of the three values. |
6024 | */ |
6025 | |
6026 | GenTreeArgList* Compiler::gtNewArgList(GenTree* arg1, GenTree* arg2, GenTree* arg3) |
6027 | { |
6028 | return new (this, GT_LIST) GenTreeArgList(arg1, gtNewArgList(arg2, arg3)); |
6029 | } |
6030 | |
6031 | /***************************************************************************** |
6032 | * |
6033 | * Create a list out of the three values. |
6034 | */ |
6035 | |
6036 | GenTreeArgList* Compiler::gtNewArgList(GenTree* arg1, GenTree* arg2, GenTree* arg3, GenTree* arg4) |
6037 | { |
6038 | return new (this, GT_LIST) GenTreeArgList(arg1, gtNewArgList(arg2, arg3, arg4)); |
6039 | } |
6040 | |
6041 | /***************************************************************************** |
6042 | * |
6043 | * Given a GT_CALL node, access the fgArgInfo and find the entry |
6044 | * that has the matching argNum and return the fgArgTableEntryPtr |
6045 | */ |
6046 | |
6047 | fgArgTabEntry* Compiler::gtArgEntryByArgNum(GenTreeCall* call, unsigned argNum) |
6048 | { |
6049 | fgArgInfo* argInfo = call->fgArgInfo; |
6050 | noway_assert(argInfo != nullptr); |
6051 | return argInfo->GetArgEntry(argNum); |
6052 | } |
6053 | |
6054 | /***************************************************************************** |
6055 | * |
6056 | * Given a GT_CALL node, access the fgArgInfo and find the entry |
6057 | * that has the matching node and return the fgArgTableEntryPtr |
6058 | */ |
6059 | |
6060 | fgArgTabEntry* Compiler::gtArgEntryByNode(GenTreeCall* call, GenTree* node) |
6061 | { |
6062 | fgArgInfo* argInfo = call->fgArgInfo; |
6063 | noway_assert(argInfo != nullptr); |
6064 | |
6065 | unsigned argCount = argInfo->ArgCount(); |
6066 | fgArgTabEntry** argTable = argInfo->ArgTable(); |
6067 | fgArgTabEntry* curArgTabEntry = nullptr; |
6068 | |
6069 | for (unsigned i = 0; i < argCount; i++) |
6070 | { |
6071 | curArgTabEntry = argTable[i]; |
6072 | |
6073 | if (curArgTabEntry->node == node) |
6074 | { |
6075 | return curArgTabEntry; |
6076 | } |
6077 | else if (curArgTabEntry->parent != nullptr) |
6078 | { |
6079 | assert(curArgTabEntry->parent->OperIsList()); |
6080 | if (curArgTabEntry->parent->Current() == node) |
6081 | { |
6082 | return curArgTabEntry; |
6083 | } |
6084 | } |
6085 | else // (curArgTabEntry->parent == NULL) |
6086 | { |
6087 | if (call->gtCallObjp == node) |
6088 | { |
6089 | return curArgTabEntry; |
6090 | } |
6091 | } |
6092 | } |
6093 | noway_assert(!"gtArgEntryByNode: node not found" ); |
6094 | return nullptr; |
6095 | } |
6096 | |
6097 | /***************************************************************************** |
6098 | * |
6099 | * Find and return the entry with the given "lateArgInx". Requires that one is found |
6100 | * (asserts this). |
6101 | */ |
6102 | fgArgTabEntry* Compiler::gtArgEntryByLateArgIndex(GenTreeCall* call, unsigned lateArgInx) |
6103 | { |
6104 | fgArgInfo* argInfo = call->fgArgInfo; |
6105 | noway_assert(argInfo != nullptr); |
6106 | assert(lateArgInx != UINT_MAX); |
6107 | |
6108 | unsigned argCount = argInfo->ArgCount(); |
6109 | fgArgTabEntry** argTable = argInfo->ArgTable(); |
6110 | fgArgTabEntry* curArgTabEntry = nullptr; |
6111 | |
6112 | for (unsigned i = 0; i < argCount; i++) |
6113 | { |
6114 | curArgTabEntry = argTable[i]; |
6115 | if (curArgTabEntry->isLateArg() && curArgTabEntry->lateArgInx == lateArgInx) |
6116 | { |
6117 | return curArgTabEntry; |
6118 | } |
6119 | } |
6120 | noway_assert(!"gtArgEntryByNode: node not found" ); |
6121 | return nullptr; |
6122 | } |
6123 | |
6124 | //------------------------------------------------------------------------ |
6125 | // gtArgNodeByLateArgInx: Given a call instruction, find the argument with the given |
6126 | // late arg index (i.e. the given position in the gtCallLateArgs list). |
6127 | // Arguments: |
6128 | // call - the call node |
6129 | // lateArgInx - the index into the late args list |
6130 | // |
6131 | // Return value: |
6132 | // The late argument node. |
6133 | // |
6134 | GenTree* Compiler::gtArgNodeByLateArgInx(GenTreeCall* call, unsigned lateArgInx) |
6135 | { |
6136 | GenTree* argx = nullptr; |
6137 | unsigned regIndex = 0; |
6138 | |
6139 | for (GenTreeArgList *list = call->gtCall.gtCallLateArgs; list != nullptr; regIndex++, list = list->Rest()) |
6140 | { |
6141 | argx = list->Current(); |
6142 | assert(!argx->IsArgPlaceHolderNode()); // No placeholder nodes are in gtCallLateArgs; |
6143 | if (regIndex == lateArgInx) |
6144 | { |
6145 | break; |
6146 | } |
6147 | } |
6148 | noway_assert(argx != nullptr); |
6149 | return argx; |
6150 | } |
6151 | |
6152 | /***************************************************************************** |
6153 | * |
6154 | * Given an fgArgTabEntry*, return true if it is the 'this' pointer argument. |
6155 | */ |
6156 | bool Compiler::gtArgIsThisPtr(fgArgTabEntry* argEntry) |
6157 | { |
6158 | return (argEntry->parent == nullptr); |
6159 | } |
6160 | |
6161 | /***************************************************************************** |
6162 | * |
6163 | * Create a node that will assign 'src' to 'dst'. |
6164 | */ |
6165 | |
6166 | GenTree* Compiler::gtNewAssignNode(GenTree* dst, GenTree* src) |
6167 | { |
6168 | /* Mark the target as being assigned */ |
6169 | |
6170 | if ((dst->gtOper == GT_LCL_VAR) || (dst->OperGet() == GT_LCL_FLD)) |
6171 | { |
6172 | dst->gtFlags |= GTF_VAR_DEF; |
6173 | if (dst->IsPartialLclFld(this)) |
6174 | { |
6175 | // We treat these partial writes as combined uses and defs. |
6176 | dst->gtFlags |= GTF_VAR_USEASG; |
6177 | } |
6178 | } |
6179 | dst->gtFlags |= GTF_DONT_CSE; |
6180 | |
6181 | /* Create the assignment node */ |
6182 | |
6183 | GenTree* asg = gtNewOperNode(GT_ASG, dst->TypeGet(), dst, src); |
6184 | |
6185 | /* Mark the expression as containing an assignment */ |
6186 | |
6187 | asg->gtFlags |= GTF_ASG; |
6188 | |
6189 | return asg; |
6190 | } |
6191 | |
6192 | //------------------------------------------------------------------------ |
6193 | // gtNewObjNode: Creates a new Obj node. |
6194 | // |
6195 | // Arguments: |
6196 | // structHnd - The class handle of the struct type. |
6197 | // addr - The address of the struct. |
6198 | // |
6199 | // Return Value: |
6200 | // Returns a node representing the struct value at the given address. |
6201 | // |
6202 | // Assumptions: |
6203 | // Any entry and exit conditions, such as required preconditions of |
6204 | // data structures, memory to be freed by caller, etc. |
6205 | // |
6206 | // Notes: |
6207 | // It will currently return a GT_OBJ node for any struct type, but may |
6208 | // return a GT_IND or a non-indirection for a scalar type. |
6209 | // The node will not yet have its GC info initialized. This is because |
6210 | // we may not need this info if this is an r-value. |
6211 | |
6212 | GenTree* Compiler::gtNewObjNode(CORINFO_CLASS_HANDLE structHnd, GenTree* addr) |
6213 | { |
6214 | var_types nodeType = impNormStructType(structHnd); |
6215 | assert(varTypeIsStruct(nodeType)); |
6216 | unsigned size = info.compCompHnd->getClassSize(structHnd); |
6217 | |
6218 | // It would be convenient to set the GC info at this time, but we don't actually require |
6219 | // it unless this is going to be a destination. |
6220 | if (!varTypeIsStruct(nodeType)) |
6221 | { |
6222 | if ((addr->gtOper == GT_ADDR) && (addr->gtGetOp1()->TypeGet() == nodeType)) |
6223 | { |
6224 | return addr->gtGetOp1(); |
6225 | } |
6226 | else |
6227 | { |
6228 | return gtNewOperNode(GT_IND, nodeType, addr); |
6229 | } |
6230 | } |
6231 | GenTreeBlk* newBlkOrObjNode = new (this, GT_OBJ) GenTreeObj(nodeType, addr, structHnd, size); |
6232 | |
6233 | // An Obj is not a global reference, if it is known to be a local struct. |
6234 | if ((addr->gtFlags & GTF_GLOB_REF) == 0) |
6235 | { |
6236 | GenTreeLclVarCommon* lclNode = addr->IsLocalAddrExpr(); |
6237 | if (lclNode != nullptr) |
6238 | { |
6239 | newBlkOrObjNode->gtFlags |= GTF_IND_NONFAULTING; |
6240 | if (!lvaIsImplicitByRefLocal(lclNode->gtLclNum)) |
6241 | { |
6242 | newBlkOrObjNode->gtFlags &= ~GTF_GLOB_REF; |
6243 | } |
6244 | } |
6245 | } |
6246 | return newBlkOrObjNode; |
6247 | } |
6248 | |
6249 | //------------------------------------------------------------------------ |
6250 | // gtSetObjGcInfo: Set the GC info on an object node |
6251 | // |
6252 | // Arguments: |
6253 | // objNode - The object node of interest |
6254 | |
6255 | void Compiler::gtSetObjGcInfo(GenTreeObj* objNode) |
6256 | { |
6257 | CORINFO_CLASS_HANDLE structHnd = objNode->gtClass; |
6258 | var_types nodeType = objNode->TypeGet(); |
6259 | unsigned size = objNode->gtBlkSize; |
6260 | unsigned slots = 0; |
6261 | unsigned gcPtrCount = 0; |
6262 | BYTE* gcPtrs = nullptr; |
6263 | |
6264 | assert(varTypeIsStruct(nodeType)); |
6265 | assert(size == info.compCompHnd->getClassSize(structHnd)); |
6266 | assert(nodeType == impNormStructType(structHnd)); |
6267 | |
6268 | if (nodeType == TYP_STRUCT) |
6269 | { |
6270 | if (size >= TARGET_POINTER_SIZE) |
6271 | { |
6272 | // Get the GC fields info |
6273 | var_types simdBaseType; // Dummy argument |
6274 | slots = roundUp(size, TARGET_POINTER_SIZE) / TARGET_POINTER_SIZE; |
6275 | gcPtrs = new (this, CMK_ASTNode) BYTE[slots]; |
6276 | nodeType = impNormStructType(structHnd, gcPtrs, &gcPtrCount, &simdBaseType); |
6277 | } |
6278 | } |
6279 | objNode->SetGCInfo(gcPtrs, gcPtrCount, slots); |
6280 | assert(objNode->gtType == nodeType); |
6281 | } |
6282 | |
6283 | //------------------------------------------------------------------------ |
6284 | // gtNewStructVal: Return a node that represents a struct value |
6285 | // |
6286 | // Arguments: |
6287 | // structHnd - The class for the struct |
6288 | // addr - The address of the struct |
6289 | // |
6290 | // Return Value: |
6291 | // A block, object or local node that represents the struct value pointed to by 'addr'. |
6292 | |
6293 | GenTree* Compiler::gtNewStructVal(CORINFO_CLASS_HANDLE structHnd, GenTree* addr) |
6294 | { |
6295 | if (addr->gtOper == GT_ADDR) |
6296 | { |
6297 | GenTree* val = addr->gtGetOp1(); |
6298 | if (val->OperGet() == GT_LCL_VAR) |
6299 | { |
6300 | unsigned lclNum = addr->gtGetOp1()->AsLclVarCommon()->gtLclNum; |
6301 | LclVarDsc* varDsc = &(lvaTable[lclNum]); |
6302 | if (varTypeIsStruct(varDsc) && (varDsc->lvVerTypeInfo.GetClassHandle() == structHnd) && |
6303 | !lvaIsImplicitByRefLocal(lclNum)) |
6304 | { |
6305 | return addr->gtGetOp1(); |
6306 | } |
6307 | } |
6308 | } |
6309 | return gtNewObjNode(structHnd, addr); |
6310 | } |
6311 | |
6312 | //------------------------------------------------------------------------ |
6313 | // gtNewBlockVal: Return a node that represents a possibly untyped block value |
6314 | // |
6315 | // Arguments: |
6316 | // addr - The address of the block |
6317 | // size - The size of the block |
6318 | // |
6319 | // Return Value: |
6320 | // A block, object or local node that represents the block value pointed to by 'addr'. |
6321 | |
6322 | GenTree* Compiler::gtNewBlockVal(GenTree* addr, unsigned size) |
6323 | { |
6324 | // By default we treat this as an opaque struct type with known size. |
6325 | var_types blkType = TYP_STRUCT; |
6326 | if ((addr->gtOper == GT_ADDR) && (addr->gtGetOp1()->OperGet() == GT_LCL_VAR)) |
6327 | { |
6328 | GenTree* val = addr->gtGetOp1(); |
6329 | #if FEATURE_SIMD |
6330 | if (varTypeIsSIMD(val)) |
6331 | { |
6332 | if (genTypeSize(val->TypeGet()) == size) |
6333 | { |
6334 | blkType = val->TypeGet(); |
6335 | return addr->gtGetOp1(); |
6336 | } |
6337 | } |
6338 | else |
6339 | #endif // FEATURE_SIMD |
6340 | if (val->TypeGet() == TYP_STRUCT) |
6341 | { |
6342 | GenTreeLclVarCommon* lcl = addr->gtGetOp1()->AsLclVarCommon(); |
6343 | LclVarDsc* varDsc = &(lvaTable[lcl->gtLclNum]); |
6344 | if ((varDsc->TypeGet() == TYP_STRUCT) && (varDsc->lvExactSize == size)) |
6345 | { |
6346 | return addr->gtGetOp1(); |
6347 | } |
6348 | } |
6349 | } |
6350 | return new (this, GT_BLK) GenTreeBlk(GT_BLK, blkType, addr, size); |
6351 | } |
6352 | |
6353 | // Creates a new assignment node for a CpObj. |
6354 | // Parameters (exactly the same as MSIL CpObj): |
6355 | // |
6356 | // dstAddr - The target to copy the struct to |
6357 | // srcAddr - The source to copy the struct from |
6358 | // structHnd - A class token that represents the type of object being copied. May be null |
6359 | // if FEATURE_SIMD is enabled and the source has a SIMD type. |
6360 | // isVolatile - Is this marked as volatile memory? |
6361 | |
6362 | GenTree* Compiler::gtNewCpObjNode(GenTree* dstAddr, GenTree* srcAddr, CORINFO_CLASS_HANDLE structHnd, bool isVolatile) |
6363 | { |
6364 | GenTree* lhs = gtNewStructVal(structHnd, dstAddr); |
6365 | GenTree* src = nullptr; |
6366 | unsigned size; |
6367 | |
6368 | if (lhs->OperIsBlk()) |
6369 | { |
6370 | size = lhs->AsBlk()->gtBlkSize; |
6371 | if (lhs->OperGet() == GT_OBJ) |
6372 | { |
6373 | gtSetObjGcInfo(lhs->AsObj()); |
6374 | } |
6375 | } |
6376 | else |
6377 | { |
6378 | size = genTypeSize(lhs->gtType); |
6379 | } |
6380 | |
6381 | if (srcAddr->OperGet() == GT_ADDR) |
6382 | { |
6383 | src = srcAddr->gtOp.gtOp1; |
6384 | } |
6385 | else |
6386 | { |
6387 | src = gtNewOperNode(GT_IND, lhs->TypeGet(), srcAddr); |
6388 | } |
6389 | |
6390 | GenTree* result = gtNewBlkOpNode(lhs, src, size, isVolatile, true); |
6391 | return result; |
6392 | } |
6393 | |
6394 | //------------------------------------------------------------------------ |
6395 | // FixupInitBlkValue: Fixup the init value for an initBlk operation |
6396 | // |
6397 | // Arguments: |
6398 | // asgType - The type of assignment that the initBlk is being transformed into |
6399 | // |
6400 | // Return Value: |
6401 | // Modifies the constant value on this node to be the appropriate "fill" |
6402 | // value for the initblk. |
6403 | // |
6404 | // Notes: |
6405 | // The initBlk MSIL instruction takes a byte value, which must be |
6406 | // extended to the size of the assignment when an initBlk is transformed |
6407 | // to an assignment of a primitive type. |
6408 | // This performs the appropriate extension. |
6409 | |
6410 | void GenTreeIntCon::FixupInitBlkValue(var_types asgType) |
6411 | { |
6412 | assert(varTypeIsIntegralOrI(asgType)); |
6413 | unsigned size = genTypeSize(asgType); |
6414 | if (size > 1) |
6415 | { |
6416 | size_t cns = gtIconVal; |
6417 | cns = cns & 0xFF; |
6418 | cns |= cns << 8; |
6419 | if (size >= 4) |
6420 | { |
6421 | cns |= cns << 16; |
6422 | #ifdef _TARGET_64BIT_ |
6423 | if (size == 8) |
6424 | { |
6425 | cns |= cns << 32; |
6426 | } |
6427 | #endif // _TARGET_64BIT_ |
6428 | |
6429 | // Make the type match for evaluation types. |
6430 | gtType = asgType; |
6431 | |
6432 | // if we are initializing a GC type the value being assigned must be zero (null). |
6433 | assert(!varTypeIsGC(asgType) || (cns == 0)); |
6434 | } |
6435 | |
6436 | gtIconVal = cns; |
6437 | } |
6438 | } |
6439 | |
6440 | // |
6441 | //------------------------------------------------------------------------ |
6442 | // gtBlockOpInit: Initializes a BlkOp GenTree |
6443 | // |
6444 | // Arguments: |
6445 | // result - an assignment node that is to be initialized. |
6446 | // dst - the target (destination) we want to either initialize or copy to. |
6447 | // src - the init value for InitBlk or the source struct for CpBlk/CpObj. |
6448 | // isVolatile - specifies whether this node is a volatile memory operation. |
6449 | // |
6450 | // Assumptions: |
6451 | // 'result' is an assignment that is newly constructed. |
6452 | // If 'dst' is TYP_STRUCT, then it must be a block node or lclVar. |
6453 | // |
6454 | // Notes: |
6455 | // This procedure centralizes all the logic to both enforce proper structure and |
6456 | // to properly construct any InitBlk/CpBlk node. |
6457 | |
6458 | void Compiler::gtBlockOpInit(GenTree* result, GenTree* dst, GenTree* srcOrFillVal, bool isVolatile) |
6459 | { |
6460 | if (!result->OperIsBlkOp()) |
6461 | { |
6462 | assert(dst->TypeGet() != TYP_STRUCT); |
6463 | return; |
6464 | } |
6465 | #ifdef DEBUG |
6466 | // If the copy involves GC pointers, the caller must have already set |
6467 | // the node additional members (gtGcPtrs, gtGcPtrCount, gtSlots) on the dst. |
6468 | if ((dst->gtOper == GT_OBJ) && dst->AsBlk()->HasGCPtr()) |
6469 | { |
6470 | GenTreeObj* objNode = dst->AsObj(); |
6471 | assert(objNode->gtGcPtrs != nullptr); |
6472 | assert(!IsUninitialized(objNode->gtGcPtrs)); |
6473 | assert(!IsUninitialized(objNode->gtGcPtrCount)); |
6474 | assert(!IsUninitialized(objNode->gtSlots) && objNode->gtSlots > 0); |
6475 | |
6476 | for (unsigned i = 0; i < objNode->gtGcPtrCount; ++i) |
6477 | { |
6478 | CorInfoGCType t = (CorInfoGCType)objNode->gtGcPtrs[i]; |
6479 | switch (t) |
6480 | { |
6481 | case TYPE_GC_NONE: |
6482 | case TYPE_GC_REF: |
6483 | case TYPE_GC_BYREF: |
6484 | case TYPE_GC_OTHER: |
6485 | break; |
6486 | default: |
6487 | unreached(); |
6488 | } |
6489 | } |
6490 | } |
6491 | #endif // DEBUG |
6492 | |
6493 | /* In the case of CpBlk, we want to avoid generating |
6494 | * nodes where the source and destination are the same |
6495 | * because of two reasons, first, is useless, second |
6496 | * it introduces issues in liveness and also copying |
6497 | * memory from an overlapping memory location is |
6498 | * undefined both as per the ECMA standard and also |
6499 | * the memcpy semantics specify that. |
6500 | * |
6501 | * NOTE: In this case we'll only detect the case for addr of a local |
6502 | * and a local itself, any other complex expressions won't be |
6503 | * caught. |
6504 | * |
6505 | * TODO-Cleanup: though having this logic is goodness (i.e. avoids self-assignment |
6506 | * of struct vars very early), it was added because fgInterBlockLocalVarLiveness() |
6507 | * isn't handling self-assignment of struct variables correctly. This issue may not |
6508 | * surface if struct promotion is ON (which is the case on x86/arm). But still the |
6509 | * fundamental issue exists that needs to be addressed. |
6510 | */ |
6511 | if (result->OperIsCopyBlkOp()) |
6512 | { |
6513 | GenTree* currSrc = srcOrFillVal; |
6514 | GenTree* currDst = dst; |
6515 | |
6516 | if (currSrc->OperIsBlk() && (currSrc->AsBlk()->Addr()->OperGet() == GT_ADDR)) |
6517 | { |
6518 | currSrc = currSrc->AsBlk()->Addr()->gtGetOp1(); |
6519 | } |
6520 | if (currDst->OperIsBlk() && (currDst->AsBlk()->Addr()->OperGet() == GT_ADDR)) |
6521 | { |
6522 | currDst = currDst->AsBlk()->Addr()->gtGetOp1(); |
6523 | } |
6524 | |
6525 | if (currSrc->OperGet() == GT_LCL_VAR && currDst->OperGet() == GT_LCL_VAR && |
6526 | currSrc->gtLclVarCommon.gtLclNum == currDst->gtLclVarCommon.gtLclNum) |
6527 | { |
6528 | // Make this a NOP |
6529 | // TODO-Cleanup: probably doesn't matter, but could do this earlier and avoid creating a GT_ASG |
6530 | result->gtBashToNOP(); |
6531 | return; |
6532 | } |
6533 | } |
6534 | |
6535 | // Propagate all effect flags from children |
6536 | result->gtFlags |= dst->gtFlags & GTF_ALL_EFFECT; |
6537 | result->gtFlags |= result->gtOp.gtOp2->gtFlags & GTF_ALL_EFFECT; |
6538 | |
6539 | result->gtFlags |= (dst->gtFlags & GTF_EXCEPT) | (srcOrFillVal->gtFlags & GTF_EXCEPT); |
6540 | |
6541 | if (isVolatile) |
6542 | { |
6543 | result->gtFlags |= GTF_BLK_VOLATILE; |
6544 | } |
6545 | |
6546 | #ifdef FEATURE_SIMD |
6547 | if (result->OperIsCopyBlkOp() && varTypeIsSIMD(srcOrFillVal)) |
6548 | { |
6549 | // If the source is a GT_SIMD node of SIMD type, then the dst lclvar struct |
6550 | // should be labeled as simd intrinsic related struct. |
6551 | // This is done so that the morpher can transform any field accesses into |
6552 | // intrinsics, thus avoiding conflicting access methods (fields vs. whole-register). |
6553 | |
6554 | GenTree* src = srcOrFillVal; |
6555 | if (src->OperIsIndir() && (src->AsIndir()->Addr()->OperGet() == GT_ADDR)) |
6556 | { |
6557 | src = src->AsIndir()->Addr()->gtGetOp1(); |
6558 | } |
6559 | #ifdef FEATURE_HW_INTRINSICS |
6560 | if ((src->OperGet() == GT_SIMD) || (src->OperGet() == GT_HWIntrinsic)) |
6561 | #else |
6562 | if (src->OperGet() == GT_SIMD) |
6563 | #endif // FEATURE_HW_INTRINSICS |
6564 | { |
6565 | if (dst->OperIsBlk() && (dst->AsIndir()->Addr()->OperGet() == GT_ADDR)) |
6566 | { |
6567 | dst = dst->AsIndir()->Addr()->gtGetOp1(); |
6568 | } |
6569 | |
6570 | if (dst->OperIsLocal() && varTypeIsStruct(dst)) |
6571 | { |
6572 | setLclRelatedToSIMDIntrinsic(dst); |
6573 | } |
6574 | } |
6575 | } |
6576 | #endif // FEATURE_SIMD |
6577 | } |
6578 | |
6579 | //------------------------------------------------------------------------ |
6580 | // gtNewBlkOpNode: Creates a GenTree for a block (struct) assignment. |
6581 | // |
6582 | // Arguments: |
6583 | // dst - Destination or target to copy to / initialize the buffer. |
6584 | // srcOrFillVall - the size of the buffer to copy/initialize or zero, in the case of CpObj. |
6585 | // size - The size of the buffer or a class token (in the case of CpObj). |
6586 | // isVolatile - Whether this is a volatile memory operation or not. |
6587 | // isCopyBlock - True if this is a block copy (rather than a block init). |
6588 | // |
6589 | // Return Value: |
6590 | // Returns the newly constructed and initialized block operation. |
6591 | // |
6592 | // Notes: |
6593 | // If size is zero, the dst must be a GT_OBJ with the class handle. |
6594 | // 'dst' must be a block node or lclVar. |
6595 | // |
6596 | GenTree* Compiler::gtNewBlkOpNode(GenTree* dst, GenTree* srcOrFillVal, unsigned size, bool isVolatile, bool isCopyBlock) |
6597 | { |
6598 | assert(dst->OperIsBlk() || dst->OperIsLocal()); |
6599 | if (isCopyBlock) |
6600 | { |
6601 | srcOrFillVal->gtFlags |= GTF_DONT_CSE; |
6602 | if (srcOrFillVal->OperIsIndir() && (srcOrFillVal->gtGetOp1()->gtOper == GT_ADDR)) |
6603 | { |
6604 | srcOrFillVal = srcOrFillVal->gtGetOp1()->gtGetOp1(); |
6605 | } |
6606 | } |
6607 | else |
6608 | { |
6609 | // InitBlk |
6610 | assert(varTypeIsIntegral(srcOrFillVal)); |
6611 | if (varTypeIsStruct(dst)) |
6612 | { |
6613 | if (!srcOrFillVal->IsIntegralConst(0)) |
6614 | { |
6615 | srcOrFillVal = gtNewOperNode(GT_INIT_VAL, TYP_INT, srcOrFillVal); |
6616 | } |
6617 | } |
6618 | } |
6619 | |
6620 | GenTree* result = gtNewAssignNode(dst, srcOrFillVal); |
6621 | gtBlockOpInit(result, dst, srcOrFillVal, isVolatile); |
6622 | return result; |
6623 | } |
6624 | |
6625 | //------------------------------------------------------------------------ |
6626 | // gtNewPutArgReg: Creates a new PutArgReg node. |
6627 | // |
6628 | // Arguments: |
6629 | // type - The actual type of the argument |
6630 | // arg - The argument node |
6631 | // argReg - The register that the argument will be passed in |
6632 | // |
6633 | // Return Value: |
6634 | // Returns the newly created PutArgReg node. |
6635 | // |
6636 | // Notes: |
6637 | // The node is generated as GenTreeMultiRegOp on RyuJIT/armel, GenTreeOp on all the other archs. |
6638 | // |
6639 | GenTree* Compiler::gtNewPutArgReg(var_types type, GenTree* arg, regNumber argReg) |
6640 | { |
6641 | assert(arg != nullptr); |
6642 | |
6643 | GenTree* node = nullptr; |
6644 | #if defined(_TARGET_ARM_) |
6645 | // A PUTARG_REG could be a MultiRegOp on arm since we could move a double register to two int registers. |
6646 | node = new (this, GT_PUTARG_REG) GenTreeMultiRegOp(GT_PUTARG_REG, type, arg, nullptr); |
6647 | if (type == TYP_LONG) |
6648 | { |
6649 | node->AsMultiRegOp()->gtOtherReg = REG_NEXT(argReg); |
6650 | } |
6651 | #else |
6652 | node = gtNewOperNode(GT_PUTARG_REG, type, arg); |
6653 | #endif |
6654 | node->gtRegNum = argReg; |
6655 | |
6656 | return node; |
6657 | } |
6658 | |
6659 | //------------------------------------------------------------------------ |
6660 | // gtNewBitCastNode: Creates a new BitCast node. |
6661 | // |
6662 | // Arguments: |
6663 | // type - The actual type of the argument |
6664 | // arg - The argument node |
6665 | // argReg - The register that the argument will be passed in |
6666 | // |
6667 | // Return Value: |
6668 | // Returns the newly created BitCast node. |
6669 | // |
6670 | // Notes: |
6671 | // The node is generated as GenTreeMultiRegOp on RyuJIT/arm, as GenTreeOp on all the other archs. |
6672 | // |
6673 | GenTree* Compiler::gtNewBitCastNode(var_types type, GenTree* arg) |
6674 | { |
6675 | assert(arg != nullptr); |
6676 | |
6677 | GenTree* node = nullptr; |
6678 | #if defined(_TARGET_ARM_) |
6679 | // A BITCAST could be a MultiRegOp on arm since we could move a double register to two int registers. |
6680 | node = new (this, GT_BITCAST) GenTreeMultiRegOp(GT_BITCAST, type, arg, nullptr); |
6681 | #else |
6682 | node = gtNewOperNode(GT_BITCAST, type, arg); |
6683 | #endif |
6684 | |
6685 | return node; |
6686 | } |
6687 | |
6688 | //------------------------------------------------------------------------ |
6689 | // gtNewAllocObjNode: Helper to create an object allocation node. |
6690 | // |
6691 | // Arguments: |
6692 | // pResolvedToken - Resolved token for the object being allocated |
6693 | // useParent - true iff the token represents a child of the object's class |
6694 | // |
6695 | // Return Value: |
6696 | // Returns GT_ALLOCOBJ node that will be later morphed into an |
6697 | // allocation helper call or local variable allocation on the stack. |
6698 | |
6699 | GenTreeAllocObj* Compiler::gtNewAllocObjNode(CORINFO_RESOLVED_TOKEN* pResolvedToken, BOOL useParent) |
6700 | { |
6701 | const BOOL mustRestoreHandle = TRUE; |
6702 | BOOL* const pRuntimeLookup = nullptr; |
6703 | bool usingReadyToRunHelper = false; |
6704 | CorInfoHelpFunc helper = CORINFO_HELP_UNDEF; |
6705 | GenTree* opHandle = impTokenToHandle(pResolvedToken, pRuntimeLookup, mustRestoreHandle, useParent); |
6706 | |
6707 | #ifdef FEATURE_READYTORUN_COMPILER |
6708 | CORINFO_CONST_LOOKUP lookup = {}; |
6709 | |
6710 | if (opts.IsReadyToRun()) |
6711 | { |
6712 | helper = CORINFO_HELP_READYTORUN_NEW; |
6713 | CORINFO_LOOKUP_KIND* const pGenericLookupKind = nullptr; |
6714 | usingReadyToRunHelper = |
6715 | info.compCompHnd->getReadyToRunHelper(pResolvedToken, pGenericLookupKind, helper, &lookup); |
6716 | } |
6717 | #endif |
6718 | |
6719 | if (!usingReadyToRunHelper) |
6720 | { |
6721 | if (opHandle == nullptr) |
6722 | { |
6723 | // We must be backing out of an inline. |
6724 | assert(compDonotInline()); |
6725 | return nullptr; |
6726 | } |
6727 | } |
6728 | |
6729 | bool helperHasSideEffects; |
6730 | CorInfoHelpFunc helperTemp = |
6731 | info.compCompHnd->getNewHelper(pResolvedToken, info.compMethodHnd, &helperHasSideEffects); |
6732 | |
6733 | if (!usingReadyToRunHelper) |
6734 | { |
6735 | helper = helperTemp; |
6736 | } |
6737 | |
6738 | // TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call |
6739 | // and the newfast call with a single call to a dynamic R2R cell that will: |
6740 | // 1) Load the context |
6741 | // 2) Perform the generic dictionary lookup and caching, and generate the appropriate stub |
6742 | // 3) Allocate and return the new object for boxing |
6743 | // Reason: performance (today, we'll always use the slow helper for the R2R generics case) |
6744 | |
6745 | GenTreeAllocObj* allocObj = |
6746 | gtNewAllocObjNode(helper, helperHasSideEffects, pResolvedToken->hClass, TYP_REF, opHandle); |
6747 | |
6748 | #ifdef FEATURE_READYTORUN_COMPILER |
6749 | if (usingReadyToRunHelper) |
6750 | { |
6751 | allocObj->gtEntryPoint = lookup; |
6752 | } |
6753 | #endif |
6754 | |
6755 | return allocObj; |
6756 | } |
6757 | |
6758 | /***************************************************************************** |
6759 | * |
6760 | * Clones the given tree value and returns a copy of the given tree. |
6761 | * If 'complexOK' is false, the cloning is only done provided the tree |
6762 | * is not too complex (whatever that may mean); |
6763 | * If 'complexOK' is true, we try slightly harder to clone the tree. |
6764 | * In either case, NULL is returned if the tree cannot be cloned |
6765 | * |
6766 | * Note that there is the function gtCloneExpr() which does a more |
6767 | * complete job if you can't handle this function failing. |
6768 | */ |
6769 | |
6770 | GenTree* Compiler::gtClone(GenTree* tree, bool complexOK) |
6771 | { |
6772 | GenTree* copy; |
6773 | |
6774 | switch (tree->gtOper) |
6775 | { |
6776 | case GT_CNS_INT: |
6777 | |
6778 | #if defined(LATE_DISASM) |
6779 | if (tree->IsIconHandle()) |
6780 | { |
6781 | copy = gtNewIconHandleNode(tree->gtIntCon.gtIconVal, tree->gtFlags, tree->gtIntCon.gtFieldSeq); |
6782 | copy->gtIntCon.gtCompileTimeHandle = tree->gtIntCon.gtCompileTimeHandle; |
6783 | copy->gtType = tree->gtType; |
6784 | } |
6785 | else |
6786 | #endif |
6787 | { |
6788 | copy = new (this, GT_CNS_INT) |
6789 | GenTreeIntCon(tree->gtType, tree->gtIntCon.gtIconVal, tree->gtIntCon.gtFieldSeq); |
6790 | copy->gtIntCon.gtCompileTimeHandle = tree->gtIntCon.gtCompileTimeHandle; |
6791 | } |
6792 | break; |
6793 | |
6794 | case GT_CNS_LNG: |
6795 | copy = gtNewLconNode(tree->gtLngCon.gtLconVal); |
6796 | break; |
6797 | |
6798 | case GT_LCL_VAR: |
6799 | // Remember that the LclVar node has been cloned. The flag will be set |
6800 | // on 'copy' as well. |
6801 | tree->gtFlags |= GTF_VAR_CLONED; |
6802 | copy = gtNewLclvNode(tree->gtLclVarCommon.gtLclNum, tree->gtType, tree->gtLclVar.gtLclILoffs); |
6803 | break; |
6804 | |
6805 | case GT_LCL_FLD: |
6806 | case GT_LCL_FLD_ADDR: |
6807 | // Remember that the LclVar node has been cloned. The flag will be set |
6808 | // on 'copy' as well. |
6809 | tree->gtFlags |= GTF_VAR_CLONED; |
6810 | copy = new (this, tree->gtOper) |
6811 | GenTreeLclFld(tree->gtOper, tree->TypeGet(), tree->gtLclFld.gtLclNum, tree->gtLclFld.gtLclOffs); |
6812 | copy->gtLclFld.gtFieldSeq = tree->gtLclFld.gtFieldSeq; |
6813 | break; |
6814 | |
6815 | case GT_CLS_VAR: |
6816 | copy = new (this, GT_CLS_VAR) |
6817 | GenTreeClsVar(tree->gtType, tree->gtClsVar.gtClsVarHnd, tree->gtClsVar.gtFieldSeq); |
6818 | break; |
6819 | |
6820 | default: |
6821 | if (!complexOK) |
6822 | { |
6823 | return nullptr; |
6824 | } |
6825 | |
6826 | if (tree->gtOper == GT_FIELD) |
6827 | { |
6828 | GenTree* objp; |
6829 | |
6830 | // copied from line 9850 |
6831 | |
6832 | objp = nullptr; |
6833 | if (tree->gtField.gtFldObj) |
6834 | { |
6835 | objp = gtClone(tree->gtField.gtFldObj, false); |
6836 | if (!objp) |
6837 | { |
6838 | return objp; |
6839 | } |
6840 | } |
6841 | |
6842 | copy = gtNewFieldRef(tree->TypeGet(), tree->gtField.gtFldHnd, objp, tree->gtField.gtFldOffset); |
6843 | copy->gtField.gtFldMayOverlap = tree->gtField.gtFldMayOverlap; |
6844 | #ifdef FEATURE_READYTORUN_COMPILER |
6845 | copy->gtField.gtFieldLookup = tree->gtField.gtFieldLookup; |
6846 | #endif |
6847 | } |
6848 | else if (tree->OperIs(GT_ADD, GT_SUB)) |
6849 | { |
6850 | GenTree* op1 = tree->gtOp.gtOp1; |
6851 | GenTree* op2 = tree->gtOp.gtOp2; |
6852 | |
6853 | if (op1->OperIsLeaf() && op2->OperIsLeaf()) |
6854 | { |
6855 | op1 = gtClone(op1); |
6856 | if (op1 == nullptr) |
6857 | { |
6858 | return nullptr; |
6859 | } |
6860 | op2 = gtClone(op2); |
6861 | if (op2 == nullptr) |
6862 | { |
6863 | return nullptr; |
6864 | } |
6865 | |
6866 | copy = gtNewOperNode(tree->OperGet(), tree->TypeGet(), op1, op2); |
6867 | } |
6868 | else |
6869 | { |
6870 | return nullptr; |
6871 | } |
6872 | } |
6873 | else if (tree->gtOper == GT_ADDR) |
6874 | { |
6875 | GenTree* op1 = gtClone(tree->gtOp.gtOp1); |
6876 | if (op1 == nullptr) |
6877 | { |
6878 | return nullptr; |
6879 | } |
6880 | copy = gtNewOperNode(GT_ADDR, tree->TypeGet(), op1); |
6881 | } |
6882 | else |
6883 | { |
6884 | return nullptr; |
6885 | } |
6886 | |
6887 | break; |
6888 | } |
6889 | |
6890 | copy->gtFlags |= tree->gtFlags & ~GTF_NODE_MASK; |
6891 | #if defined(DEBUG) |
6892 | copy->gtDebugFlags |= tree->gtDebugFlags & ~GTF_DEBUG_NODE_MASK; |
6893 | #endif // defined(DEBUG) |
6894 | |
6895 | return copy; |
6896 | } |
6897 | |
6898 | //------------------------------------------------------------------------ |
6899 | // gtCloneExpr: Create a copy of `tree`, adding flags `addFlags`, mapping |
6900 | // local `varNum` to int constant `varVal` if it appears at |
6901 | // the root, and mapping uses of local `deepVarNum` to constant |
6902 | // `deepVarVal` if they occur beyond the root. |
6903 | // |
6904 | // Arguments: |
6905 | // tree - GenTree to create a copy of |
6906 | // addFlags - GTF_* flags to add to the copied tree nodes |
6907 | // varNum - lclNum to replace at the root, or ~0 for no root replacement |
6908 | // varVal - If replacing at root, replace local `varNum` with IntCns `varVal` |
6909 | // deepVarNum - lclNum to replace uses of beyond the root, or ~0 for no replacement |
6910 | // deepVarVal - If replacing beyond root, replace `deepVarNum` with IntCns `deepVarVal` |
6911 | // |
6912 | // Return Value: |
6913 | // A copy of the given tree with the replacements and added flags specified. |
6914 | // |
6915 | // Notes: |
6916 | // Top-level callers should generally call the overload that doesn't have |
6917 | // the explicit `deepVarNum` and `deepVarVal` parameters; those are used in |
6918 | // recursive invocations to avoid replacing defs. |
6919 | |
6920 | GenTree* Compiler::gtCloneExpr( |
6921 | GenTree* tree, unsigned addFlags, unsigned varNum, int varVal, unsigned deepVarNum, int deepVarVal) |
6922 | { |
6923 | if (tree == nullptr) |
6924 | { |
6925 | return nullptr; |
6926 | } |
6927 | |
6928 | /* Figure out what kind of a node we have */ |
6929 | |
6930 | genTreeOps oper = tree->OperGet(); |
6931 | unsigned kind = tree->OperKind(); |
6932 | GenTree* copy; |
6933 | |
6934 | /* Is this a constant or leaf node? */ |
6935 | |
6936 | if (kind & (GTK_CONST | GTK_LEAF)) |
6937 | { |
6938 | switch (oper) |
6939 | { |
6940 | case GT_CNS_INT: |
6941 | |
6942 | #if defined(LATE_DISASM) |
6943 | if (tree->IsIconHandle()) |
6944 | { |
6945 | copy = gtNewIconHandleNode(tree->gtIntCon.gtIconVal, tree->gtFlags, tree->gtIntCon.gtFieldSeq); |
6946 | copy->gtIntCon.gtCompileTimeHandle = tree->gtIntCon.gtCompileTimeHandle; |
6947 | copy->gtType = tree->gtType; |
6948 | } |
6949 | else |
6950 | #endif |
6951 | { |
6952 | copy = gtNewIconNode(tree->gtIntCon.gtIconVal, tree->gtType); |
6953 | copy->gtIntCon.gtCompileTimeHandle = tree->gtIntCon.gtCompileTimeHandle; |
6954 | copy->gtIntCon.gtFieldSeq = tree->gtIntCon.gtFieldSeq; |
6955 | } |
6956 | goto DONE; |
6957 | |
6958 | case GT_CNS_LNG: |
6959 | copy = gtNewLconNode(tree->gtLngCon.gtLconVal); |
6960 | goto DONE; |
6961 | |
6962 | case GT_CNS_DBL: |
6963 | copy = gtNewDconNode(tree->gtDblCon.gtDconVal); |
6964 | copy->gtType = tree->gtType; // keep the same type |
6965 | goto DONE; |
6966 | |
6967 | case GT_CNS_STR: |
6968 | copy = gtNewSconNode(tree->gtStrCon.gtSconCPX, tree->gtStrCon.gtScpHnd); |
6969 | goto DONE; |
6970 | |
6971 | case GT_LCL_VAR: |
6972 | |
6973 | if (tree->gtLclVarCommon.gtLclNum == varNum) |
6974 | { |
6975 | copy = gtNewIconNode(varVal, tree->gtType); |
6976 | if (tree->gtFlags & GTF_VAR_ARR_INDEX) |
6977 | { |
6978 | copy->LabelIndex(this); |
6979 | } |
6980 | } |
6981 | else |
6982 | { |
6983 | // Remember that the LclVar node has been cloned. The flag will |
6984 | // be set on 'copy' as well. |
6985 | tree->gtFlags |= GTF_VAR_CLONED; |
6986 | copy = gtNewLclvNode(tree->gtLclVar.gtLclNum, tree->gtType, tree->gtLclVar.gtLclILoffs); |
6987 | copy->AsLclVarCommon()->SetSsaNum(tree->AsLclVarCommon()->GetSsaNum()); |
6988 | } |
6989 | copy->gtFlags = tree->gtFlags; |
6990 | goto DONE; |
6991 | |
6992 | case GT_LCL_FLD: |
6993 | if (tree->gtLclFld.gtLclNum == varNum) |
6994 | { |
6995 | IMPL_LIMITATION("replacing GT_LCL_FLD with a constant" ); |
6996 | } |
6997 | else |
6998 | { |
6999 | // Remember that the LclVar node has been cloned. The flag will |
7000 | // be set on 'copy' as well. |
7001 | tree->gtFlags |= GTF_VAR_CLONED; |
7002 | copy = new (this, GT_LCL_FLD) |
7003 | GenTreeLclFld(tree->TypeGet(), tree->gtLclFld.gtLclNum, tree->gtLclFld.gtLclOffs); |
7004 | copy->gtLclFld.gtFieldSeq = tree->gtLclFld.gtFieldSeq; |
7005 | copy->gtFlags = tree->gtFlags; |
7006 | } |
7007 | goto DONE; |
7008 | |
7009 | case GT_CLS_VAR: |
7010 | copy = new (this, GT_CLS_VAR) |
7011 | GenTreeClsVar(tree->TypeGet(), tree->gtClsVar.gtClsVarHnd, tree->gtClsVar.gtFieldSeq); |
7012 | goto DONE; |
7013 | |
7014 | case GT_RET_EXPR: |
7015 | // GT_RET_EXPR is unique node, that contains a link to a gtInlineCandidate node, |
7016 | // that is part of another statement. We cannot clone both here and cannot |
7017 | // create another GT_RET_EXPR that points to the same gtInlineCandidate. |
7018 | NO_WAY("Cloning of GT_RET_EXPR node not supported" ); |
7019 | goto DONE; |
7020 | |
7021 | case GT_MEMORYBARRIER: |
7022 | copy = new (this, GT_MEMORYBARRIER) GenTree(GT_MEMORYBARRIER, TYP_VOID); |
7023 | goto DONE; |
7024 | |
7025 | case GT_ARGPLACE: |
7026 | copy = gtNewArgPlaceHolderNode(tree->gtType, tree->gtArgPlace.gtArgPlaceClsHnd); |
7027 | goto DONE; |
7028 | |
7029 | case GT_FTN_ADDR: |
7030 | copy = new (this, oper) GenTreeFptrVal(tree->gtType, tree->gtFptrVal.gtFptrMethod); |
7031 | |
7032 | #ifdef FEATURE_READYTORUN_COMPILER |
7033 | copy->gtFptrVal.gtEntryPoint = tree->gtFptrVal.gtEntryPoint; |
7034 | #endif |
7035 | goto DONE; |
7036 | |
7037 | case GT_CATCH_ARG: |
7038 | case GT_NO_OP: |
7039 | copy = new (this, oper) GenTree(oper, tree->gtType); |
7040 | goto DONE; |
7041 | |
7042 | #if !FEATURE_EH_FUNCLETS |
7043 | case GT_END_LFIN: |
7044 | #endif // !FEATURE_EH_FUNCLETS |
7045 | case GT_JMP: |
7046 | copy = new (this, oper) GenTreeVal(oper, tree->gtType, tree->gtVal.gtVal1); |
7047 | goto DONE; |
7048 | |
7049 | case GT_LABEL: |
7050 | copy = new (this, oper) GenTreeLabel(tree->gtLabel.gtLabBB); |
7051 | goto DONE; |
7052 | |
7053 | default: |
7054 | NO_WAY("Cloning of node not supported" ); |
7055 | goto DONE; |
7056 | } |
7057 | } |
7058 | |
7059 | /* Is it a 'simple' unary/binary operator? */ |
7060 | |
7061 | if (kind & GTK_SMPOP) |
7062 | { |
7063 | /* If necessary, make sure we allocate a "fat" tree node */ |
7064 | CLANG_FORMAT_COMMENT_ANCHOR; |
7065 | |
7066 | #if SMALL_TREE_NODES |
7067 | switch (oper) |
7068 | { |
7069 | /* These nodes sometimes get bashed to "fat" ones */ |
7070 | |
7071 | case GT_MUL: |
7072 | case GT_DIV: |
7073 | case GT_MOD: |
7074 | |
7075 | case GT_UDIV: |
7076 | case GT_UMOD: |
7077 | |
7078 | // In the implementation of gtNewLargeOperNode you have |
7079 | // to give an oper that will create a small node, |
7080 | // otherwise it asserts. |
7081 | // |
7082 | if (GenTree::s_gtNodeSizes[oper] == TREE_NODE_SZ_SMALL) |
7083 | { |
7084 | copy = gtNewLargeOperNode(oper, tree->TypeGet(), tree->gtOp.gtOp1, |
7085 | tree->OperIsBinary() ? tree->gtOp.gtOp2 : nullptr); |
7086 | } |
7087 | else // Always a large tree |
7088 | { |
7089 | if (tree->OperIsBinary()) |
7090 | { |
7091 | copy = gtNewOperNode(oper, tree->TypeGet(), tree->gtOp.gtOp1, tree->gtOp.gtOp2); |
7092 | } |
7093 | else |
7094 | { |
7095 | copy = gtNewOperNode(oper, tree->TypeGet(), tree->gtOp.gtOp1); |
7096 | } |
7097 | } |
7098 | break; |
7099 | |
7100 | case GT_CAST: |
7101 | copy = |
7102 | new (this, LargeOpOpcode()) GenTreeCast(tree->TypeGet(), tree->gtCast.CastOp(), tree->IsUnsigned(), |
7103 | tree->gtCast.gtCastType DEBUGARG(/*largeNode*/ TRUE)); |
7104 | break; |
7105 | |
7106 | // The nodes below this are not bashed, so they can be allocated at their individual sizes. |
7107 | |
7108 | case GT_LIST: |
7109 | assert((tree->gtOp.gtOp2 == nullptr) || tree->gtOp.gtOp2->OperIsList()); |
7110 | copy = new (this, GT_LIST) GenTreeArgList(tree->gtOp.gtOp1); |
7111 | copy->gtOp.gtOp2 = tree->gtOp.gtOp2; |
7112 | break; |
7113 | |
7114 | case GT_FIELD_LIST: |
7115 | copy = new (this, GT_FIELD_LIST) GenTreeFieldList(tree->gtOp.gtOp1, tree->AsFieldList()->gtFieldOffset, |
7116 | tree->AsFieldList()->gtFieldType, nullptr); |
7117 | copy->gtOp.gtOp2 = tree->gtOp.gtOp2; |
7118 | copy->gtFlags = (copy->gtFlags & ~GTF_FIELD_LIST_HEAD) | (tree->gtFlags & GTF_FIELD_LIST_HEAD); |
7119 | break; |
7120 | |
7121 | case GT_INDEX: |
7122 | { |
7123 | GenTreeIndex* asInd = tree->AsIndex(); |
7124 | copy = new (this, GT_INDEX) |
7125 | GenTreeIndex(asInd->TypeGet(), asInd->Arr(), asInd->Index(), asInd->gtIndElemSize); |
7126 | copy->AsIndex()->gtStructElemClass = asInd->gtStructElemClass; |
7127 | } |
7128 | break; |
7129 | |
7130 | case GT_INDEX_ADDR: |
7131 | { |
7132 | GenTreeIndexAddr* asIndAddr = tree->AsIndexAddr(); |
7133 | |
7134 | copy = new (this, GT_INDEX_ADDR) |
7135 | GenTreeIndexAddr(asIndAddr->Arr(), asIndAddr->Index(), asIndAddr->gtElemType, |
7136 | asIndAddr->gtStructElemClass, asIndAddr->gtElemSize, asIndAddr->gtLenOffset, |
7137 | asIndAddr->gtElemOffset); |
7138 | copy->AsIndexAddr()->gtIndRngFailBB = asIndAddr->gtIndRngFailBB; |
7139 | } |
7140 | break; |
7141 | |
7142 | case GT_ALLOCOBJ: |
7143 | { |
7144 | GenTreeAllocObj* asAllocObj = tree->AsAllocObj(); |
7145 | copy = new (this, GT_ALLOCOBJ) |
7146 | GenTreeAllocObj(tree->TypeGet(), asAllocObj->gtNewHelper, asAllocObj->gtHelperHasSideEffects, |
7147 | asAllocObj->gtAllocObjClsHnd, asAllocObj->gtOp1); |
7148 | } |
7149 | break; |
7150 | |
7151 | case GT_RUNTIMELOOKUP: |
7152 | { |
7153 | GenTreeRuntimeLookup* asRuntimeLookup = tree->AsRuntimeLookup(); |
7154 | |
7155 | copy = new (this, GT_RUNTIMELOOKUP) |
7156 | GenTreeRuntimeLookup(asRuntimeLookup->gtHnd, asRuntimeLookup->gtHndType, asRuntimeLookup->gtOp1); |
7157 | } |
7158 | break; |
7159 | |
7160 | case GT_ARR_LENGTH: |
7161 | copy = gtNewArrLen(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtArrLen.ArrLenOffset()); |
7162 | break; |
7163 | |
7164 | case GT_ARR_INDEX: |
7165 | copy = new (this, GT_ARR_INDEX) |
7166 | GenTreeArrIndex(tree->TypeGet(), |
7167 | gtCloneExpr(tree->gtArrIndex.ArrObj(), addFlags, deepVarNum, deepVarVal), |
7168 | gtCloneExpr(tree->gtArrIndex.IndexExpr(), addFlags, deepVarNum, deepVarVal), |
7169 | tree->gtArrIndex.gtCurrDim, tree->gtArrIndex.gtArrRank, |
7170 | tree->gtArrIndex.gtArrElemType); |
7171 | break; |
7172 | |
7173 | case GT_QMARK: |
7174 | copy = new (this, GT_QMARK) GenTreeQmark(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtOp.gtOp2, this); |
7175 | break; |
7176 | |
7177 | case GT_OBJ: |
7178 | copy = new (this, GT_OBJ) |
7179 | GenTreeObj(tree->TypeGet(), tree->gtOp.gtOp1, tree->AsObj()->gtClass, tree->gtBlk.gtBlkSize); |
7180 | copy->AsObj()->CopyGCInfo(tree->AsObj()); |
7181 | copy->gtBlk.gtBlkOpGcUnsafe = tree->gtBlk.gtBlkOpGcUnsafe; |
7182 | break; |
7183 | |
7184 | case GT_BLK: |
7185 | copy = new (this, GT_BLK) GenTreeBlk(GT_BLK, tree->TypeGet(), tree->gtOp.gtOp1, tree->gtBlk.gtBlkSize); |
7186 | copy->gtBlk.gtBlkOpGcUnsafe = tree->gtBlk.gtBlkOpGcUnsafe; |
7187 | break; |
7188 | |
7189 | case GT_DYN_BLK: |
7190 | copy = new (this, GT_DYN_BLK) GenTreeDynBlk(tree->gtOp.gtOp1, tree->gtDynBlk.gtDynamicSize); |
7191 | copy->gtBlk.gtBlkOpGcUnsafe = tree->gtBlk.gtBlkOpGcUnsafe; |
7192 | break; |
7193 | |
7194 | case GT_BOX: |
7195 | copy = new (this, GT_BOX) |
7196 | GenTreeBox(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtBox.gtAsgStmtWhenInlinedBoxValue, |
7197 | tree->gtBox.gtCopyStmtWhenInlinedBoxValue); |
7198 | break; |
7199 | |
7200 | case GT_INTRINSIC: |
7201 | copy = new (this, GT_INTRINSIC) |
7202 | GenTreeIntrinsic(tree->TypeGet(), tree->gtOp.gtOp1, tree->gtOp.gtOp2, |
7203 | tree->gtIntrinsic.gtIntrinsicId, tree->gtIntrinsic.gtMethodHandle); |
7204 | #ifdef FEATURE_READYTORUN_COMPILER |
7205 | copy->gtIntrinsic.gtEntryPoint = tree->gtIntrinsic.gtEntryPoint; |
7206 | #endif |
7207 | break; |
7208 | |
7209 | case GT_LEA: |
7210 | { |
7211 | GenTreeAddrMode* addrModeOp = tree->AsAddrMode(); |
7212 | copy = new (this, GT_LEA) |
7213 | GenTreeAddrMode(addrModeOp->TypeGet(), addrModeOp->Base(), addrModeOp->Index(), addrModeOp->gtScale, |
7214 | static_cast<unsigned>(addrModeOp->Offset())); |
7215 | } |
7216 | break; |
7217 | |
7218 | case GT_COPY: |
7219 | case GT_RELOAD: |
7220 | { |
7221 | copy = new (this, oper) GenTreeCopyOrReload(oper, tree->TypeGet(), tree->gtGetOp1()); |
7222 | } |
7223 | break; |
7224 | |
7225 | #ifdef FEATURE_SIMD |
7226 | case GT_SIMD: |
7227 | { |
7228 | GenTreeSIMD* simdOp = tree->AsSIMD(); |
7229 | copy = gtNewSIMDNode(simdOp->TypeGet(), simdOp->gtGetOp1(), simdOp->gtGetOp2IfPresent(), |
7230 | simdOp->gtSIMDIntrinsicID, simdOp->gtSIMDBaseType, simdOp->gtSIMDSize); |
7231 | } |
7232 | break; |
7233 | #endif |
7234 | |
7235 | #ifdef FEATURE_HW_INTRINSICS |
7236 | case GT_HWIntrinsic: |
7237 | { |
7238 | GenTreeHWIntrinsic* hwintrinsicOp = tree->AsHWIntrinsic(); |
7239 | copy = new (this, GT_HWIntrinsic) |
7240 | GenTreeHWIntrinsic(hwintrinsicOp->TypeGet(), hwintrinsicOp->gtGetOp1(), |
7241 | hwintrinsicOp->gtGetOp2IfPresent(), hwintrinsicOp->gtHWIntrinsicId, |
7242 | hwintrinsicOp->gtSIMDBaseType, hwintrinsicOp->gtSIMDSize); |
7243 | copy->AsHWIntrinsic()->gtIndexBaseType = hwintrinsicOp->gtIndexBaseType; |
7244 | } |
7245 | break; |
7246 | #endif |
7247 | |
7248 | default: |
7249 | assert(!GenTree::IsExOp(tree->OperKind()) && tree->OperIsSimple()); |
7250 | // We're in the SimpleOp case, so it's always unary or binary. |
7251 | if (GenTree::OperIsUnary(tree->OperGet())) |
7252 | { |
7253 | copy = gtNewOperNode(oper, tree->TypeGet(), tree->gtOp.gtOp1, /*doSimplifications*/ false); |
7254 | } |
7255 | else |
7256 | { |
7257 | assert(GenTree::OperIsBinary(tree->OperGet())); |
7258 | copy = gtNewOperNode(oper, tree->TypeGet(), tree->gtOp.gtOp1, tree->gtOp.gtOp2); |
7259 | } |
7260 | break; |
7261 | } |
7262 | #else |
7263 | // We're in the SimpleOp case, so it's always unary or binary. |
7264 | copy = gtNewOperNode(oper, tree->TypeGet(), tree->gtOp.gtOp1, tree->gtOp.gtOp2); |
7265 | #endif |
7266 | |
7267 | // Some flags are conceptually part of the gtOper, and should be copied immediately. |
7268 | if (tree->gtOverflowEx()) |
7269 | { |
7270 | copy->gtFlags |= GTF_OVERFLOW; |
7271 | } |
7272 | |
7273 | if (tree->gtOp.gtOp1) |
7274 | { |
7275 | if (tree->gtOper == GT_ASG) |
7276 | { |
7277 | // Don't replace varNum if it appears as the LHS of an assign. |
7278 | copy->gtOp.gtOp1 = gtCloneExpr(tree->gtOp.gtOp1, addFlags, -1, 0, deepVarNum, deepVarVal); |
7279 | } |
7280 | else |
7281 | { |
7282 | copy->gtOp.gtOp1 = gtCloneExpr(tree->gtOp.gtOp1, addFlags, deepVarNum, deepVarVal); |
7283 | } |
7284 | } |
7285 | |
7286 | if (tree->gtGetOp2IfPresent()) |
7287 | { |
7288 | copy->gtOp.gtOp2 = gtCloneExpr(tree->gtOp.gtOp2, addFlags, deepVarNum, deepVarVal); |
7289 | } |
7290 | |
7291 | /* Flags */ |
7292 | addFlags |= tree->gtFlags; |
7293 | |
7294 | // Copy any node annotations, if necessary. |
7295 | switch (tree->gtOper) |
7296 | { |
7297 | case GT_STOREIND: |
7298 | case GT_IND: |
7299 | case GT_OBJ: |
7300 | case GT_STORE_OBJ: |
7301 | { |
7302 | ArrayInfo arrInfo; |
7303 | if (!tree->AsIndir()->gtOp1->OperIs(GT_INDEX_ADDR) && TryGetArrayInfo(tree->AsIndir(), &arrInfo)) |
7304 | { |
7305 | GetArrayInfoMap()->Set(copy, arrInfo); |
7306 | } |
7307 | } |
7308 | break; |
7309 | |
7310 | default: |
7311 | break; |
7312 | } |
7313 | |
7314 | #ifdef DEBUG |
7315 | /* GTF_NODE_MASK should not be propagated from 'tree' to 'copy' */ |
7316 | addFlags &= ~GTF_NODE_MASK; |
7317 | #endif |
7318 | |
7319 | // Effects flags propagate upwards. |
7320 | if (copy->gtOp.gtOp1 != nullptr) |
7321 | { |
7322 | copy->gtFlags |= (copy->gtOp.gtOp1->gtFlags & GTF_ALL_EFFECT); |
7323 | } |
7324 | if (copy->gtGetOp2IfPresent() != nullptr) |
7325 | { |
7326 | copy->gtFlags |= (copy->gtGetOp2()->gtFlags & GTF_ALL_EFFECT); |
7327 | } |
7328 | |
7329 | goto DONE; |
7330 | } |
7331 | |
7332 | /* See what kind of a special operator we have here */ |
7333 | |
7334 | switch (oper) |
7335 | { |
7336 | case GT_STMT: |
7337 | copy = gtCloneExpr(tree->gtStmt.gtStmtExpr, addFlags, deepVarNum, deepVarVal); |
7338 | copy = gtNewStmt(copy, tree->gtStmt.gtStmtILoffsx); |
7339 | goto DONE; |
7340 | |
7341 | case GT_CALL: |
7342 | |
7343 | // We can't safely clone calls that have GT_RET_EXPRs via gtCloneExpr. |
7344 | // You must use gtCloneCandidateCall for these calls (and then do appropriate other fixup) |
7345 | if (tree->gtCall.IsInlineCandidate() || tree->gtCall.IsGuardedDevirtualizationCandidate()) |
7346 | { |
7347 | NO_WAY("Cloning of calls with associated GT_RET_EXPR nodes is not supported" ); |
7348 | } |
7349 | |
7350 | copy = gtCloneExprCallHelper(tree->AsCall(), addFlags, deepVarNum, deepVarVal); |
7351 | break; |
7352 | |
7353 | case GT_FIELD: |
7354 | |
7355 | copy = gtNewFieldRef(tree->TypeGet(), tree->gtField.gtFldHnd, nullptr, tree->gtField.gtFldOffset); |
7356 | |
7357 | copy->gtField.gtFldObj = tree->gtField.gtFldObj |
7358 | ? gtCloneExpr(tree->gtField.gtFldObj, addFlags, deepVarNum, deepVarVal) |
7359 | : nullptr; |
7360 | copy->gtField.gtFldMayOverlap = tree->gtField.gtFldMayOverlap; |
7361 | #ifdef FEATURE_READYTORUN_COMPILER |
7362 | copy->gtField.gtFieldLookup = tree->gtField.gtFieldLookup; |
7363 | #endif |
7364 | |
7365 | break; |
7366 | |
7367 | case GT_ARR_ELEM: |
7368 | { |
7369 | GenTree* inds[GT_ARR_MAX_RANK]; |
7370 | for (unsigned dim = 0; dim < tree->gtArrElem.gtArrRank; dim++) |
7371 | { |
7372 | inds[dim] = gtCloneExpr(tree->gtArrElem.gtArrInds[dim], addFlags, deepVarNum, deepVarVal); |
7373 | } |
7374 | copy = new (this, GT_ARR_ELEM) |
7375 | GenTreeArrElem(tree->TypeGet(), gtCloneExpr(tree->gtArrElem.gtArrObj, addFlags, deepVarNum, deepVarVal), |
7376 | tree->gtArrElem.gtArrRank, tree->gtArrElem.gtArrElemSize, tree->gtArrElem.gtArrElemType, |
7377 | &inds[0]); |
7378 | } |
7379 | break; |
7380 | |
7381 | case GT_ARR_OFFSET: |
7382 | { |
7383 | copy = new (this, GT_ARR_OFFSET) |
7384 | GenTreeArrOffs(tree->TypeGet(), gtCloneExpr(tree->gtArrOffs.gtOffset, addFlags, deepVarNum, deepVarVal), |
7385 | gtCloneExpr(tree->gtArrOffs.gtIndex, addFlags, deepVarNum, deepVarVal), |
7386 | gtCloneExpr(tree->gtArrOffs.gtArrObj, addFlags, deepVarNum, deepVarVal), |
7387 | tree->gtArrOffs.gtCurrDim, tree->gtArrOffs.gtArrRank, tree->gtArrOffs.gtArrElemType); |
7388 | } |
7389 | break; |
7390 | |
7391 | case GT_CMPXCHG: |
7392 | copy = new (this, GT_CMPXCHG) |
7393 | GenTreeCmpXchg(tree->TypeGet(), |
7394 | gtCloneExpr(tree->gtCmpXchg.gtOpLocation, addFlags, deepVarNum, deepVarVal), |
7395 | gtCloneExpr(tree->gtCmpXchg.gtOpValue, addFlags, deepVarNum, deepVarVal), |
7396 | gtCloneExpr(tree->gtCmpXchg.gtOpComparand, addFlags, deepVarNum, deepVarVal)); |
7397 | break; |
7398 | |
7399 | case GT_ARR_BOUNDS_CHECK: |
7400 | #ifdef FEATURE_SIMD |
7401 | case GT_SIMD_CHK: |
7402 | #endif // FEATURE_SIMD |
7403 | #ifdef FEATURE_HW_INTRINSICS |
7404 | case GT_HW_INTRINSIC_CHK: |
7405 | #endif // FEATURE_HW_INTRINSICS |
7406 | copy = new (this, oper) |
7407 | GenTreeBoundsChk(oper, tree->TypeGet(), |
7408 | gtCloneExpr(tree->gtBoundsChk.gtIndex, addFlags, deepVarNum, deepVarVal), |
7409 | gtCloneExpr(tree->gtBoundsChk.gtArrLen, addFlags, deepVarNum, deepVarVal), |
7410 | tree->gtBoundsChk.gtThrowKind); |
7411 | copy->gtBoundsChk.gtIndRngFailBB = tree->gtBoundsChk.gtIndRngFailBB; |
7412 | break; |
7413 | |
7414 | case GT_STORE_DYN_BLK: |
7415 | case GT_DYN_BLK: |
7416 | copy = new (this, oper) |
7417 | GenTreeDynBlk(gtCloneExpr(tree->gtDynBlk.Addr(), addFlags, deepVarNum, deepVarVal), |
7418 | gtCloneExpr(tree->gtDynBlk.gtDynamicSize, addFlags, deepVarNum, deepVarVal)); |
7419 | break; |
7420 | |
7421 | default: |
7422 | #ifdef DEBUG |
7423 | gtDispTree(tree); |
7424 | #endif |
7425 | NO_WAY("unexpected operator" ); |
7426 | } |
7427 | |
7428 | DONE: |
7429 | |
7430 | // If it has a zero-offset field seq, copy annotation. |
7431 | if (tree->TypeGet() == TYP_BYREF) |
7432 | { |
7433 | FieldSeqNode* fldSeq = nullptr; |
7434 | if (GetZeroOffsetFieldMap()->Lookup(tree, &fldSeq)) |
7435 | { |
7436 | GetZeroOffsetFieldMap()->Set(copy, fldSeq); |
7437 | } |
7438 | } |
7439 | |
7440 | copy->gtVNPair = tree->gtVNPair; // A cloned tree gets the orginal's Value number pair |
7441 | |
7442 | /* Compute the flags for the copied node. Note that we can do this only |
7443 | if we didnt gtFoldExpr(copy) */ |
7444 | |
7445 | if (copy->gtOper == oper) |
7446 | { |
7447 | addFlags |= tree->gtFlags; |
7448 | |
7449 | #ifdef DEBUG |
7450 | /* GTF_NODE_MASK should not be propagated from 'tree' to 'copy' */ |
7451 | addFlags &= ~GTF_NODE_MASK; |
7452 | #endif |
7453 | // Some other flags depend on the context of the expression, and should not be preserved. |
7454 | // For example, GTF_RELOP_QMARK: |
7455 | if (copy->OperKind() & GTK_RELOP) |
7456 | { |
7457 | addFlags &= ~GTF_RELOP_QMARK; |
7458 | } |
7459 | // On the other hand, if we're creating such a context, restore this flag. |
7460 | if (copy->OperGet() == GT_QMARK) |
7461 | { |
7462 | copy->gtOp.gtOp1->gtFlags |= GTF_RELOP_QMARK; |
7463 | } |
7464 | |
7465 | copy->gtFlags |= addFlags; |
7466 | |
7467 | // Update side effect flags since they may be different from the source side effect flags. |
7468 | // For example, we may have replaced some locals with constants and made indirections non-throwing. |
7469 | gtUpdateNodeSideEffects(copy); |
7470 | } |
7471 | |
7472 | /* GTF_COLON_COND should be propagated from 'tree' to 'copy' */ |
7473 | copy->gtFlags |= (tree->gtFlags & GTF_COLON_COND); |
7474 | |
7475 | #if defined(DEBUG) |
7476 | // Non-node debug flags should be propagated from 'tree' to 'copy' |
7477 | copy->gtDebugFlags |= (tree->gtDebugFlags & ~GTF_DEBUG_NODE_MASK); |
7478 | #endif |
7479 | |
7480 | /* Make sure to copy back fields that may have been initialized */ |
7481 | |
7482 | copy->CopyRawCosts(tree); |
7483 | copy->gtRsvdRegs = tree->gtRsvdRegs; |
7484 | copy->CopyReg(tree); |
7485 | return copy; |
7486 | } |
7487 | |
7488 | //------------------------------------------------------------------------ |
7489 | // gtCloneExprCallHelper: clone a call tree |
7490 | // |
7491 | // Notes: |
7492 | // Do not invoke this method directly, instead call either gtCloneExpr |
7493 | // or gtCloneCandidateCall, as appropriate. |
7494 | // |
7495 | // Arguments: |
7496 | // tree - the call to clone |
7497 | // addFlags - GTF_* flags to add to the copied tree nodes |
7498 | // deepVarNum - lclNum to replace uses of beyond the root, or BAD_VAR_NUM for no replacement |
7499 | // deepVarVal - If replacing beyond root, replace `deepVarNum` with IntCns `deepVarVal` |
7500 | // |
7501 | // Returns: |
7502 | // Cloned copy of call and all subtrees. |
7503 | |
7504 | GenTreeCall* Compiler::gtCloneExprCallHelper(GenTreeCall* tree, unsigned addFlags, unsigned deepVarNum, int deepVarVal) |
7505 | { |
7506 | GenTreeCall* copy = new (this, GT_CALL) GenTreeCall(tree->TypeGet()); |
7507 | |
7508 | copy->gtCallObjp = tree->gtCallObjp ? gtCloneExpr(tree->gtCallObjp, addFlags, deepVarNum, deepVarVal) : nullptr; |
7509 | copy->gtCallArgs = |
7510 | tree->gtCallArgs ? gtCloneExpr(tree->gtCallArgs, addFlags, deepVarNum, deepVarVal)->AsArgList() : nullptr; |
7511 | copy->gtCallMoreFlags = tree->gtCallMoreFlags; |
7512 | copy->gtCallLateArgs = tree->gtCallLateArgs |
7513 | ? gtCloneExpr(tree->gtCallLateArgs, addFlags, deepVarNum, deepVarVal)->AsArgList() |
7514 | : nullptr; |
7515 | |
7516 | #if !FEATURE_FIXED_OUT_ARGS |
7517 | copy->regArgList = tree->regArgList; |
7518 | copy->regArgListCount = tree->regArgListCount; |
7519 | #endif |
7520 | |
7521 | // The call sig comes from the EE and doesn't change throughout the compilation process, meaning |
7522 | // we only really need one physical copy of it. Therefore a shallow pointer copy will suffice. |
7523 | // (Note that this still holds even if the tree we are cloning was created by an inlinee compiler, |
7524 | // because the inlinee still uses the inliner's memory allocator anyway.) |
7525 | copy->callSig = tree->callSig; |
7526 | |
7527 | copy->gtCallType = tree->gtCallType; |
7528 | copy->gtReturnType = tree->gtReturnType; |
7529 | copy->gtControlExpr = tree->gtControlExpr; |
7530 | |
7531 | /* Copy the union */ |
7532 | if (tree->gtCallType == CT_INDIRECT) |
7533 | { |
7534 | copy->gtCallCookie = |
7535 | tree->gtCallCookie ? gtCloneExpr(tree->gtCallCookie, addFlags, deepVarNum, deepVarVal) : nullptr; |
7536 | copy->gtCallAddr = tree->gtCallAddr ? gtCloneExpr(tree->gtCallAddr, addFlags, deepVarNum, deepVarVal) : nullptr; |
7537 | } |
7538 | else if (tree->IsVirtualStub()) |
7539 | { |
7540 | copy->gtCallMethHnd = tree->gtCallMethHnd; |
7541 | copy->gtStubCallStubAddr = tree->gtStubCallStubAddr; |
7542 | } |
7543 | else |
7544 | { |
7545 | copy->gtCallMethHnd = tree->gtCallMethHnd; |
7546 | copy->gtInlineCandidateInfo = nullptr; |
7547 | } |
7548 | |
7549 | if (tree->fgArgInfo) |
7550 | { |
7551 | // Create and initialize the fgArgInfo for our copy of the call tree |
7552 | copy->fgArgInfo = new (this, CMK_Unknown) fgArgInfo(copy, tree); |
7553 | } |
7554 | else |
7555 | { |
7556 | copy->fgArgInfo = nullptr; |
7557 | } |
7558 | |
7559 | copy->gtRetClsHnd = tree->gtRetClsHnd; |
7560 | |
7561 | #if FEATURE_MULTIREG_RET |
7562 | copy->gtReturnTypeDesc = tree->gtReturnTypeDesc; |
7563 | #endif |
7564 | |
7565 | #ifdef FEATURE_READYTORUN_COMPILER |
7566 | copy->setEntryPoint(tree->gtEntryPoint); |
7567 | #endif |
7568 | |
7569 | #if defined(DEBUG) || defined(INLINE_DATA) |
7570 | copy->gtInlineObservation = tree->gtInlineObservation; |
7571 | copy->gtRawILOffset = tree->gtCall.gtRawILOffset; |
7572 | #endif |
7573 | |
7574 | copy->CopyOtherRegFlags(tree); |
7575 | |
7576 | return copy; |
7577 | } |
7578 | |
7579 | //------------------------------------------------------------------------ |
7580 | // gtCloneCandidateCall: clone a call that is an inline or guarded |
7581 | // devirtualization candidate (~ any call that can have a GT_RET_EXPR) |
7582 | // |
7583 | // Notes: |
7584 | // If the call really is a candidate, the caller must take additional steps |
7585 | // after cloning to re-establish candidate info and the relationship between |
7586 | // the candidate and any associated GT_RET_EXPR. |
7587 | // |
7588 | // Arguments: |
7589 | // call - the call to clone |
7590 | // |
7591 | // Returns: |
7592 | // Cloned copy of call and all subtrees. |
7593 | |
7594 | GenTreeCall* Compiler::gtCloneCandidateCall(GenTreeCall* call) |
7595 | { |
7596 | assert(call->IsInlineCandidate() || call->IsGuardedDevirtualizationCandidate()); |
7597 | |
7598 | GenTreeCall* result = gtCloneExprCallHelper(call); |
7599 | |
7600 | // There is some common post-processing in gtCloneExpr that we reproduce |
7601 | // here, for the fields that make sense for candidate calls. |
7602 | result->gtFlags |= call->gtFlags; |
7603 | |
7604 | #if defined(DEBUG) |
7605 | result->gtDebugFlags |= (call->gtDebugFlags & ~GTF_DEBUG_NODE_MASK); |
7606 | #endif |
7607 | |
7608 | result->CopyReg(call); |
7609 | |
7610 | return result; |
7611 | } |
7612 | |
7613 | //------------------------------------------------------------------------ |
7614 | // gtReplaceTree: Replace a tree with a new tree. |
7615 | // |
7616 | // Arguments: |
7617 | // stmt - The top-level root stmt of the tree being replaced. |
7618 | // Must not be null. |
7619 | // tree - The tree being replaced. Must not be null. |
7620 | // replacementTree - The replacement tree. Must not be null. |
7621 | // |
7622 | // Return Value: |
7623 | // The tree node that replaces the old tree. |
7624 | // |
7625 | // Assumptions: |
7626 | // The sequencing of the stmt has been done. |
7627 | // |
7628 | // Notes: |
7629 | // The caller must ensure that the original statement has been sequenced, |
7630 | // and the side effect flags are updated on the statement nodes, |
7631 | // but this method will sequence 'replacementTree', and insert it into the |
7632 | // proper place in the statement sequence. |
7633 | |
7634 | GenTree* Compiler::gtReplaceTree(GenTree* stmt, GenTree* tree, GenTree* replacementTree) |
7635 | { |
7636 | assert(fgStmtListThreaded); |
7637 | assert(tree != nullptr); |
7638 | assert(stmt != nullptr); |
7639 | assert(replacementTree != nullptr); |
7640 | |
7641 | GenTree** treePtr = nullptr; |
7642 | GenTree* treeParent = tree->gtGetParent(&treePtr); |
7643 | |
7644 | assert(treeParent != nullptr || tree == stmt->gtStmt.gtStmtExpr); |
7645 | |
7646 | if (treePtr == nullptr) |
7647 | { |
7648 | // Replace the stmt expr and rebuild the linear order for "stmt". |
7649 | assert(treeParent == nullptr); |
7650 | assert(fgOrder != FGOrderLinear); |
7651 | stmt->gtStmt.gtStmtExpr = tree; |
7652 | fgSetStmtSeq(stmt); |
7653 | } |
7654 | else |
7655 | { |
7656 | assert(treeParent != nullptr); |
7657 | |
7658 | // Check to see if the node to be replaced is a call argument and if so, |
7659 | // set `treeParent` to the call node. |
7660 | GenTree* cursor = treeParent; |
7661 | while ((cursor != nullptr) && (cursor->OperGet() == GT_LIST)) |
7662 | { |
7663 | cursor = cursor->gtNext; |
7664 | } |
7665 | |
7666 | if ((cursor != nullptr) && (cursor->OperGet() == GT_CALL)) |
7667 | { |
7668 | treeParent = cursor; |
7669 | } |
7670 | |
7671 | #ifdef DEBUG |
7672 | GenTree** useEdge; |
7673 | assert(treeParent->TryGetUse(tree, &useEdge)); |
7674 | assert(useEdge == treePtr); |
7675 | #endif // DEBUG |
7676 | |
7677 | GenTree* treeFirstNode = fgGetFirstNode(tree); |
7678 | GenTree* treeLastNode = tree; |
7679 | GenTree* treePrevNode = treeFirstNode->gtPrev; |
7680 | GenTree* treeNextNode = treeLastNode->gtNext; |
7681 | |
7682 | treeParent->ReplaceOperand(treePtr, replacementTree); |
7683 | |
7684 | // Build the linear order for "replacementTree". |
7685 | fgSetTreeSeq(replacementTree, treePrevNode); |
7686 | |
7687 | // Restore linear-order Prev and Next for "replacementTree". |
7688 | if (treePrevNode != nullptr) |
7689 | { |
7690 | treeFirstNode = fgGetFirstNode(replacementTree); |
7691 | treeFirstNode->gtPrev = treePrevNode; |
7692 | treePrevNode->gtNext = treeFirstNode; |
7693 | } |
7694 | else |
7695 | { |
7696 | // Update the linear oder start of "stmt" if treeFirstNode |
7697 | // appears to have replaced the original first node. |
7698 | assert(treeFirstNode == stmt->gtStmt.gtStmtList); |
7699 | stmt->gtStmt.gtStmtList = fgGetFirstNode(replacementTree); |
7700 | } |
7701 | |
7702 | if (treeNextNode != nullptr) |
7703 | { |
7704 | treeLastNode = replacementTree; |
7705 | treeLastNode->gtNext = treeNextNode; |
7706 | treeNextNode->gtPrev = treeLastNode; |
7707 | } |
7708 | } |
7709 | |
7710 | return replacementTree; |
7711 | } |
7712 | |
7713 | //------------------------------------------------------------------------ |
7714 | // gtUpdateSideEffects: Update the side effects of a tree and its ancestors |
7715 | // |
7716 | // Arguments: |
7717 | // stmt - The tree's statement |
7718 | // tree - Tree to update the side effects for |
7719 | // |
7720 | // Note: If tree's order hasn't been established, the method updates side effect |
7721 | // flags on all statement's nodes. |
7722 | |
7723 | void Compiler::gtUpdateSideEffects(GenTree* stmt, GenTree* tree) |
7724 | { |
7725 | if (fgStmtListThreaded) |
7726 | { |
7727 | gtUpdateTreeAncestorsSideEffects(tree); |
7728 | } |
7729 | else |
7730 | { |
7731 | gtUpdateStmtSideEffects(stmt); |
7732 | } |
7733 | } |
7734 | |
7735 | //------------------------------------------------------------------------ |
7736 | // gtUpdateTreeAncestorsSideEffects: Update the side effects of a tree and its ancestors |
7737 | // when statement order has been established. |
7738 | // |
7739 | // Arguments: |
7740 | // tree - Tree to update the side effects for |
7741 | |
7742 | void Compiler::(GenTree* tree) |
7743 | { |
7744 | assert(fgStmtListThreaded); |
7745 | while (tree != nullptr) |
7746 | { |
7747 | gtUpdateNodeSideEffects(tree); |
7748 | tree = tree->gtGetParent(nullptr); |
7749 | } |
7750 | } |
7751 | |
7752 | //------------------------------------------------------------------------ |
7753 | // gtUpdateStmtSideEffects: Update the side effects for statement tree nodes. |
7754 | // |
7755 | // Arguments: |
7756 | // stmt - The statement to update side effects on |
7757 | |
7758 | void Compiler::gtUpdateStmtSideEffects(GenTree* stmt) |
7759 | { |
7760 | fgWalkTree(&stmt->gtStmt.gtStmtExpr, fgUpdateSideEffectsPre, fgUpdateSideEffectsPost); |
7761 | } |
7762 | |
7763 | //------------------------------------------------------------------------ |
7764 | // gtUpdateNodeOperSideEffects: Update the side effects based on the node operation. |
7765 | // |
7766 | // Arguments: |
7767 | // tree - Tree to update the side effects on |
7768 | // |
7769 | // Notes: |
7770 | // This method currently only updates GTF_EXCEPT, GTF_ASG, and GTF_CALL flags. |
7771 | // The other side effect flags may remain unnecessarily (conservatively) set. |
7772 | // The caller of this method is expected to update the flags based on the children's flags. |
7773 | |
7774 | void Compiler::gtUpdateNodeOperSideEffects(GenTree* tree) |
7775 | { |
7776 | if (tree->OperMayThrow(this)) |
7777 | { |
7778 | tree->gtFlags |= GTF_EXCEPT; |
7779 | } |
7780 | else |
7781 | { |
7782 | tree->gtFlags &= ~GTF_EXCEPT; |
7783 | if (tree->OperIsIndirOrArrLength()) |
7784 | { |
7785 | tree->gtFlags |= GTF_IND_NONFAULTING; |
7786 | } |
7787 | } |
7788 | |
7789 | if (tree->OperRequiresAsgFlag()) |
7790 | { |
7791 | tree->gtFlags |= GTF_ASG; |
7792 | } |
7793 | else |
7794 | { |
7795 | tree->gtFlags &= ~GTF_ASG; |
7796 | } |
7797 | |
7798 | if (tree->OperRequiresCallFlag(this)) |
7799 | { |
7800 | tree->gtFlags |= GTF_CALL; |
7801 | } |
7802 | else |
7803 | { |
7804 | tree->gtFlags &= ~GTF_CALL; |
7805 | } |
7806 | } |
7807 | |
7808 | //------------------------------------------------------------------------ |
7809 | // gtUpdateNodeSideEffects: Update the side effects based on the node operation and |
7810 | // children's side efects. |
7811 | // |
7812 | // Arguments: |
7813 | // tree - Tree to update the side effects on |
7814 | // |
7815 | // Notes: |
7816 | // This method currently only updates GTF_EXCEPT and GTF_ASG flags. The other side effect |
7817 | // flags may remain unnecessarily (conservatively) set. |
7818 | |
7819 | void Compiler::gtUpdateNodeSideEffects(GenTree* tree) |
7820 | { |
7821 | gtUpdateNodeOperSideEffects(tree); |
7822 | unsigned nChildren = tree->NumChildren(); |
7823 | for (unsigned childNum = 0; childNum < nChildren; childNum++) |
7824 | { |
7825 | GenTree* child = tree->GetChild(childNum); |
7826 | if (child != nullptr) |
7827 | { |
7828 | tree->gtFlags |= (child->gtFlags & GTF_ALL_EFFECT); |
7829 | } |
7830 | } |
7831 | } |
7832 | |
7833 | //------------------------------------------------------------------------ |
7834 | // fgUpdateSideEffectsPre: Update the side effects based on the tree operation. |
7835 | // |
7836 | // Arguments: |
7837 | // pTree - Pointer to the tree to update the side effects |
7838 | // fgWalkPre - Walk data |
7839 | // |
7840 | // Notes: |
7841 | // This method currently only updates GTF_EXCEPT and GTF_ASG flags. The other side effect |
7842 | // flags may remain unnecessarily (conservatively) set. |
7843 | |
7844 | Compiler::fgWalkResult Compiler::fgUpdateSideEffectsPre(GenTree** pTree, fgWalkData* fgWalkPre) |
7845 | { |
7846 | fgWalkPre->compiler->gtUpdateNodeOperSideEffects(*pTree); |
7847 | |
7848 | return WALK_CONTINUE; |
7849 | } |
7850 | |
7851 | //------------------------------------------------------------------------ |
7852 | // fgUpdateSideEffectsPost: Update the side effects of the parent based on the tree's flags. |
7853 | // |
7854 | // Arguments: |
7855 | // pTree - Pointer to the tree |
7856 | // fgWalkPost - Walk data |
7857 | // |
7858 | // Notes: |
7859 | // The routine is used for updating the stale side effect flags for ancestor |
7860 | // nodes starting from treeParent up to the top-level stmt expr. |
7861 | |
7862 | Compiler::fgWalkResult Compiler::fgUpdateSideEffectsPost(GenTree** pTree, fgWalkData* fgWalkPost) |
7863 | { |
7864 | GenTree* tree = *pTree; |
7865 | GenTree* parent = fgWalkPost->parent; |
7866 | if (parent != nullptr) |
7867 | { |
7868 | parent->gtFlags |= (tree->gtFlags & GTF_ALL_EFFECT); |
7869 | } |
7870 | return WALK_CONTINUE; |
7871 | } |
7872 | |
7873 | /***************************************************************************** |
7874 | * |
7875 | * Compares two trees and returns true when both trees are the same. |
7876 | * Instead of fully comparing the two trees this method can just return false. |
7877 | * Thus callers should not assume that the trees are different when false is returned. |
7878 | * Only when true is returned can the caller perform code optimizations. |
7879 | * The current implementation only compares a limited set of LEAF/CONST node |
7880 | * and returns false for all othere trees. |
7881 | */ |
7882 | bool Compiler::gtCompareTree(GenTree* op1, GenTree* op2) |
7883 | { |
7884 | /* Make sure that both trees are of the same GT node kind */ |
7885 | if (op1->OperGet() != op2->OperGet()) |
7886 | { |
7887 | return false; |
7888 | } |
7889 | |
7890 | /* Make sure that both trees are returning the same type */ |
7891 | if (op1->gtType != op2->gtType) |
7892 | { |
7893 | return false; |
7894 | } |
7895 | |
7896 | /* Figure out what kind of a node we have */ |
7897 | |
7898 | genTreeOps oper = op1->OperGet(); |
7899 | unsigned kind = op1->OperKind(); |
7900 | |
7901 | /* Is this a constant or leaf node? */ |
7902 | |
7903 | if (kind & (GTK_CONST | GTK_LEAF)) |
7904 | { |
7905 | switch (oper) |
7906 | { |
7907 | case GT_CNS_INT: |
7908 | if ((op1->gtIntCon.gtIconVal == op2->gtIntCon.gtIconVal) && GenTree::SameIconHandleFlag(op1, op2)) |
7909 | { |
7910 | return true; |
7911 | } |
7912 | break; |
7913 | |
7914 | case GT_CNS_LNG: |
7915 | if (op1->gtLngCon.gtLconVal == op2->gtLngCon.gtLconVal) |
7916 | { |
7917 | return true; |
7918 | } |
7919 | break; |
7920 | |
7921 | case GT_CNS_STR: |
7922 | if (op1->gtStrCon.gtSconCPX == op2->gtStrCon.gtSconCPX) |
7923 | { |
7924 | return true; |
7925 | } |
7926 | break; |
7927 | |
7928 | case GT_LCL_VAR: |
7929 | if (op1->gtLclVarCommon.gtLclNum == op2->gtLclVarCommon.gtLclNum) |
7930 | { |
7931 | return true; |
7932 | } |
7933 | break; |
7934 | |
7935 | case GT_CLS_VAR: |
7936 | if (op1->gtClsVar.gtClsVarHnd == op2->gtClsVar.gtClsVarHnd) |
7937 | { |
7938 | return true; |
7939 | } |
7940 | break; |
7941 | |
7942 | default: |
7943 | // we return false for these unhandled 'oper' kinds |
7944 | break; |
7945 | } |
7946 | } |
7947 | return false; |
7948 | } |
7949 | |
7950 | GenTree* Compiler::gtGetThisArg(GenTreeCall* call) |
7951 | { |
7952 | if (call->gtCallObjp != nullptr) |
7953 | { |
7954 | if (call->gtCallObjp->gtOper != GT_NOP && call->gtCallObjp->gtOper != GT_ASG) |
7955 | { |
7956 | if (!(call->gtCallObjp->gtFlags & GTF_LATE_ARG)) |
7957 | { |
7958 | return call->gtCallObjp; |
7959 | } |
7960 | } |
7961 | |
7962 | if (call->gtCallLateArgs) |
7963 | { |
7964 | regNumber thisReg = REG_ARG_0; |
7965 | unsigned argNum = 0; |
7966 | fgArgTabEntry* thisArgTabEntry = gtArgEntryByArgNum(call, argNum); |
7967 | GenTree* result = thisArgTabEntry->node; |
7968 | |
7969 | #if !FEATURE_FIXED_OUT_ARGS |
7970 | GenTree* lateArgs = call->gtCallLateArgs; |
7971 | regList list = call->regArgList; |
7972 | int index = 0; |
7973 | while (lateArgs != NULL) |
7974 | { |
7975 | assert(lateArgs->gtOper == GT_LIST); |
7976 | assert(index < call->regArgListCount); |
7977 | regNumber curArgReg = list[index]; |
7978 | if (curArgReg == thisReg) |
7979 | { |
7980 | if (optAssertionPropagatedCurrentStmt) |
7981 | result = lateArgs->gtOp.gtOp1; |
7982 | |
7983 | assert(result == lateArgs->gtOp.gtOp1); |
7984 | } |
7985 | |
7986 | lateArgs = lateArgs->gtOp.gtOp2; |
7987 | index++; |
7988 | } |
7989 | #endif |
7990 | return result; |
7991 | } |
7992 | } |
7993 | return nullptr; |
7994 | } |
7995 | |
7996 | bool GenTree::gtSetFlags() const |
7997 | { |
7998 | // |
7999 | // When FEATURE_SET_FLAGS (_TARGET_ARM_) is active the method returns true |
8000 | // when the gtFlags has the flag GTF_SET_FLAGS set |
8001 | // otherwise the architecture will be have instructions that typically set |
8002 | // the flags and this method will return true. |
8003 | // |
8004 | // Exceptions: GT_IND (load/store) is not allowed to set the flags |
8005 | // and on XARCH the GT_MUL/GT_DIV and all overflow instructions |
8006 | // do not set the condition flags |
8007 | // |
8008 | // Precondition we have a GTK_SMPOP |
8009 | // |
8010 | if (!varTypeIsIntegralOrI(TypeGet()) && (TypeGet() != TYP_VOID)) |
8011 | { |
8012 | return false; |
8013 | } |
8014 | |
8015 | if (((gtFlags & GTF_SET_FLAGS) != 0) && (gtOper != GT_IND)) |
8016 | { |
8017 | // GTF_SET_FLAGS is not valid on GT_IND and is overlaid with GTF_NONFAULTING_IND |
8018 | return true; |
8019 | } |
8020 | else |
8021 | { |
8022 | return false; |
8023 | } |
8024 | } |
8025 | |
8026 | bool GenTree::gtRequestSetFlags() |
8027 | { |
8028 | bool result = false; |
8029 | |
8030 | #if FEATURE_SET_FLAGS |
8031 | // This method is a Nop unless FEATURE_SET_FLAGS is defined |
8032 | |
8033 | // In order to set GTF_SET_FLAGS |
8034 | // we must have a GTK_SMPOP |
8035 | // and we have a integer or machine size type (not floating point or TYP_LONG on 32-bit) |
8036 | // |
8037 | if (!OperIsSimple()) |
8038 | return false; |
8039 | |
8040 | if (!varTypeIsIntegralOrI(TypeGet())) |
8041 | return false; |
8042 | |
8043 | switch (gtOper) |
8044 | { |
8045 | case GT_IND: |
8046 | case GT_ARR_LENGTH: |
8047 | // These will turn into simple load from memory instructions |
8048 | // and we can't force the setting of the flags on load from memory |
8049 | break; |
8050 | |
8051 | case GT_MUL: |
8052 | case GT_DIV: |
8053 | // These instructions don't set the flags (on x86/x64) |
8054 | // |
8055 | break; |
8056 | |
8057 | default: |
8058 | // Otherwise we can set the flags for this gtOper |
8059 | // and codegen must set the condition flags. |
8060 | // |
8061 | gtFlags |= GTF_SET_FLAGS; |
8062 | result = true; |
8063 | break; |
8064 | } |
8065 | #endif // FEATURE_SET_FLAGS |
8066 | |
8067 | // Codegen for this tree must set the condition flags if |
8068 | // this method returns true. |
8069 | // |
8070 | return result; |
8071 | } |
8072 | |
8073 | unsigned GenTree::NumChildren() |
8074 | { |
8075 | if (OperIsConst() || OperIsLeaf()) |
8076 | { |
8077 | return 0; |
8078 | } |
8079 | else if (OperIsUnary()) |
8080 | { |
8081 | if (OperGet() == GT_NOP || OperGet() == GT_RETURN || OperGet() == GT_RETFILT) |
8082 | { |
8083 | if (gtOp.gtOp1 == nullptr) |
8084 | { |
8085 | return 0; |
8086 | } |
8087 | else |
8088 | { |
8089 | return 1; |
8090 | } |
8091 | } |
8092 | else |
8093 | { |
8094 | return 1; |
8095 | } |
8096 | } |
8097 | else if (OperIsBinary()) |
8098 | { |
8099 | // All binary operators except LEA have at least one arg; the second arg may sometimes be null, however. |
8100 | if (OperGet() == GT_LEA) |
8101 | { |
8102 | unsigned childCount = 0; |
8103 | if (gtOp.gtOp1 != nullptr) |
8104 | { |
8105 | childCount++; |
8106 | } |
8107 | if (gtOp.gtOp2 != nullptr) |
8108 | { |
8109 | childCount++; |
8110 | } |
8111 | return childCount; |
8112 | } |
8113 | #ifdef FEATURE_HW_INTRINSICS |
8114 | // GT_HWIntrinsic require special handling |
8115 | if (OperGet() == GT_HWIntrinsic) |
8116 | { |
8117 | if (gtOp.gtOp1 == nullptr) |
8118 | { |
8119 | return 0; |
8120 | } |
8121 | } |
8122 | #endif |
8123 | assert(gtOp.gtOp1 != nullptr); |
8124 | if (gtOp.gtOp2 == nullptr) |
8125 | { |
8126 | return 1; |
8127 | } |
8128 | else |
8129 | { |
8130 | return 2; |
8131 | } |
8132 | } |
8133 | else |
8134 | { |
8135 | // Special |
8136 | switch (OperGet()) |
8137 | { |
8138 | case GT_CMPXCHG: |
8139 | return 3; |
8140 | |
8141 | case GT_ARR_BOUNDS_CHECK: |
8142 | #ifdef FEATURE_SIMD |
8143 | case GT_SIMD_CHK: |
8144 | #endif // FEATURE_SIMD |
8145 | #ifdef FEATURE_HW_INTRINSICS |
8146 | case GT_HW_INTRINSIC_CHK: |
8147 | #endif // FEATURE_HW_INTRINSICS |
8148 | return 2; |
8149 | |
8150 | case GT_FIELD: |
8151 | case GT_STMT: |
8152 | return 1; |
8153 | |
8154 | case GT_ARR_ELEM: |
8155 | return 1 + AsArrElem()->gtArrRank; |
8156 | |
8157 | case GT_DYN_BLK: |
8158 | return 2; |
8159 | |
8160 | case GT_ARR_OFFSET: |
8161 | case GT_STORE_DYN_BLK: |
8162 | return 3; |
8163 | |
8164 | case GT_CALL: |
8165 | { |
8166 | GenTreeCall* call = AsCall(); |
8167 | unsigned res = 0; // arg list(s) (including late args). |
8168 | if (call->gtCallObjp != nullptr) |
8169 | { |
8170 | res++; // Add objp? |
8171 | } |
8172 | if (call->gtCallArgs != nullptr) |
8173 | { |
8174 | res++; // Add args? |
8175 | } |
8176 | if (call->gtCallLateArgs != nullptr) |
8177 | { |
8178 | res++; // Add late args? |
8179 | } |
8180 | if (call->gtControlExpr != nullptr) |
8181 | { |
8182 | res++; |
8183 | } |
8184 | |
8185 | if (call->gtCallType == CT_INDIRECT) |
8186 | { |
8187 | if (call->gtCallCookie != nullptr) |
8188 | { |
8189 | res++; |
8190 | } |
8191 | if (call->gtCallAddr != nullptr) |
8192 | { |
8193 | res++; |
8194 | } |
8195 | } |
8196 | return res; |
8197 | } |
8198 | case GT_NONE: |
8199 | return 0; |
8200 | default: |
8201 | unreached(); |
8202 | } |
8203 | } |
8204 | } |
8205 | |
8206 | GenTree* GenTree::GetChild(unsigned childNum) |
8207 | { |
8208 | assert(childNum < NumChildren()); // Precondition. |
8209 | assert(NumChildren() <= MAX_CHILDREN); |
8210 | assert(!(OperIsConst() || OperIsLeaf())); |
8211 | if (OperIsUnary()) |
8212 | { |
8213 | return AsUnOp()->gtOp1; |
8214 | } |
8215 | // Special case for assignment of dynamic block. |
8216 | // This code is here to duplicate the former case where the size may be evaluated prior to the |
8217 | // source and destination addresses. In order to do this, we treat the size as a child of the |
8218 | // assignment. |
8219 | // TODO-1stClassStructs: Revisit the need to duplicate former behavior, so that we can remove |
8220 | // these special cases. |
8221 | if ((OperGet() == GT_ASG) && (gtOp.gtOp1->OperGet() == GT_DYN_BLK) && (childNum == 2)) |
8222 | { |
8223 | return gtOp.gtOp1->AsDynBlk()->gtDynamicSize; |
8224 | } |
8225 | else if (OperIsBinary()) |
8226 | { |
8227 | if (OperIsAddrMode()) |
8228 | { |
8229 | // If this is the first (0th) child, only return op1 if it is non-null |
8230 | // Otherwise, we return gtOp2. |
8231 | if (childNum == 0 && AsOp()->gtOp1 != nullptr) |
8232 | { |
8233 | return AsOp()->gtOp1; |
8234 | } |
8235 | return AsOp()->gtOp2; |
8236 | } |
8237 | // TODO-Cleanup: Consider handling ReverseOps here, and then we wouldn't have to handle it in |
8238 | // fgGetFirstNode(). However, it seems that it causes loop hoisting behavior to change. |
8239 | if (childNum == 0) |
8240 | { |
8241 | return AsOp()->gtOp1; |
8242 | } |
8243 | else |
8244 | { |
8245 | return AsOp()->gtOp2; |
8246 | } |
8247 | } |
8248 | else |
8249 | { |
8250 | // Special |
8251 | switch (OperGet()) |
8252 | { |
8253 | case GT_CMPXCHG: |
8254 | switch (childNum) |
8255 | { |
8256 | case 0: |
8257 | return AsCmpXchg()->gtOpLocation; |
8258 | case 1: |
8259 | return AsCmpXchg()->gtOpValue; |
8260 | case 2: |
8261 | return AsCmpXchg()->gtOpComparand; |
8262 | default: |
8263 | unreached(); |
8264 | } |
8265 | case GT_ARR_BOUNDS_CHECK: |
8266 | #ifdef FEATURE_SIMD |
8267 | case GT_SIMD_CHK: |
8268 | #endif // FEATURE_SIMD |
8269 | #ifdef FEATURE_HW_INTRINSICS |
8270 | case GT_HW_INTRINSIC_CHK: |
8271 | #endif // FEATURE_HW_INTRINSICS |
8272 | switch (childNum) |
8273 | { |
8274 | case 0: |
8275 | return AsBoundsChk()->gtIndex; |
8276 | case 1: |
8277 | return AsBoundsChk()->gtArrLen; |
8278 | default: |
8279 | unreached(); |
8280 | } |
8281 | |
8282 | case GT_STORE_DYN_BLK: |
8283 | switch (childNum) |
8284 | { |
8285 | case 0: |
8286 | return AsDynBlk()->Addr(); |
8287 | case 1: |
8288 | return AsDynBlk()->Data(); |
8289 | case 2: |
8290 | return AsDynBlk()->gtDynamicSize; |
8291 | default: |
8292 | unreached(); |
8293 | } |
8294 | case GT_DYN_BLK: |
8295 | switch (childNum) |
8296 | { |
8297 | case 0: |
8298 | return AsDynBlk()->gtEvalSizeFirst ? AsDynBlk()->gtDynamicSize : AsDynBlk()->Addr(); |
8299 | case 1: |
8300 | return AsDynBlk()->gtEvalSizeFirst ? AsDynBlk()->Addr() : AsDynBlk()->gtDynamicSize; |
8301 | default: |
8302 | unreached(); |
8303 | } |
8304 | |
8305 | case GT_FIELD: |
8306 | return AsField()->gtFldObj; |
8307 | |
8308 | case GT_STMT: |
8309 | return AsStmt()->gtStmtExpr; |
8310 | |
8311 | case GT_ARR_ELEM: |
8312 | if (childNum == 0) |
8313 | { |
8314 | return AsArrElem()->gtArrObj; |
8315 | } |
8316 | else |
8317 | { |
8318 | return AsArrElem()->gtArrInds[childNum - 1]; |
8319 | } |
8320 | |
8321 | case GT_ARR_OFFSET: |
8322 | switch (childNum) |
8323 | { |
8324 | case 0: |
8325 | return AsArrOffs()->gtOffset; |
8326 | case 1: |
8327 | return AsArrOffs()->gtIndex; |
8328 | case 2: |
8329 | return AsArrOffs()->gtArrObj; |
8330 | default: |
8331 | unreached(); |
8332 | } |
8333 | |
8334 | case GT_CALL: |
8335 | { |
8336 | // The if chain below assumes that all possible children are non-null. |
8337 | // If some are null, "virtually skip them." |
8338 | // If there isn't "virtually skip it." |
8339 | GenTreeCall* call = AsCall(); |
8340 | |
8341 | if (call->gtCallObjp == nullptr) |
8342 | { |
8343 | childNum++; |
8344 | } |
8345 | if (childNum >= 1 && call->gtCallArgs == nullptr) |
8346 | { |
8347 | childNum++; |
8348 | } |
8349 | if (childNum >= 2 && call->gtCallLateArgs == nullptr) |
8350 | { |
8351 | childNum++; |
8352 | } |
8353 | if (childNum >= 3 && call->gtControlExpr == nullptr) |
8354 | { |
8355 | childNum++; |
8356 | } |
8357 | if (call->gtCallType == CT_INDIRECT) |
8358 | { |
8359 | if (childNum >= 4 && call->gtCallCookie == nullptr) |
8360 | { |
8361 | childNum++; |
8362 | } |
8363 | } |
8364 | |
8365 | if (childNum == 0) |
8366 | { |
8367 | return call->gtCallObjp; |
8368 | } |
8369 | else if (childNum == 1) |
8370 | { |
8371 | return call->gtCallArgs; |
8372 | } |
8373 | else if (childNum == 2) |
8374 | { |
8375 | return call->gtCallLateArgs; |
8376 | } |
8377 | else if (childNum == 3) |
8378 | { |
8379 | return call->gtControlExpr; |
8380 | } |
8381 | else |
8382 | { |
8383 | assert(call->gtCallType == CT_INDIRECT); |
8384 | if (childNum == 4) |
8385 | { |
8386 | return call->gtCallCookie; |
8387 | } |
8388 | else |
8389 | { |
8390 | assert(childNum == 5); |
8391 | return call->gtCallAddr; |
8392 | } |
8393 | } |
8394 | } |
8395 | case GT_NONE: |
8396 | unreached(); |
8397 | default: |
8398 | unreached(); |
8399 | } |
8400 | } |
8401 | } |
8402 | |
8403 | GenTreeUseEdgeIterator::GenTreeUseEdgeIterator() |
8404 | : m_advance(nullptr), m_node(nullptr), m_edge(nullptr), m_argList(nullptr), m_state(-1) |
8405 | { |
8406 | } |
8407 | |
8408 | GenTreeUseEdgeIterator::GenTreeUseEdgeIterator(GenTree* node) |
8409 | : m_advance(nullptr), m_node(node), m_edge(nullptr), m_argList(nullptr), m_state(0) |
8410 | { |
8411 | assert(m_node != nullptr); |
8412 | |
8413 | // NOTE: the switch statement below must be updated when introducing new nodes. |
8414 | |
8415 | switch (m_node->OperGet()) |
8416 | { |
8417 | // Leaf nodes |
8418 | case GT_LCL_VAR: |
8419 | case GT_LCL_FLD: |
8420 | case GT_LCL_VAR_ADDR: |
8421 | case GT_LCL_FLD_ADDR: |
8422 | case GT_CATCH_ARG: |
8423 | case GT_LABEL: |
8424 | case GT_FTN_ADDR: |
8425 | case GT_RET_EXPR: |
8426 | case GT_CNS_INT: |
8427 | case GT_CNS_LNG: |
8428 | case GT_CNS_DBL: |
8429 | case GT_CNS_STR: |
8430 | case GT_MEMORYBARRIER: |
8431 | case GT_JMP: |
8432 | case GT_JCC: |
8433 | case GT_SETCC: |
8434 | case GT_NO_OP: |
8435 | case GT_START_NONGC: |
8436 | case GT_PROF_HOOK: |
8437 | #if !FEATURE_EH_FUNCLETS |
8438 | case GT_END_LFIN: |
8439 | #endif // !FEATURE_EH_FUNCLETS |
8440 | case GT_PHI_ARG: |
8441 | case GT_JMPTABLE: |
8442 | case GT_CLS_VAR: |
8443 | case GT_CLS_VAR_ADDR: |
8444 | case GT_ARGPLACE: |
8445 | case GT_PHYSREG: |
8446 | case GT_EMITNOP: |
8447 | case GT_PINVOKE_PROLOG: |
8448 | case GT_PINVOKE_EPILOG: |
8449 | case GT_IL_OFFSET: |
8450 | m_state = -1; |
8451 | return; |
8452 | |
8453 | // Standard unary operators |
8454 | case GT_STORE_LCL_VAR: |
8455 | case GT_STORE_LCL_FLD: |
8456 | case GT_NOT: |
8457 | case GT_NEG: |
8458 | case GT_COPY: |
8459 | case GT_RELOAD: |
8460 | case GT_ARR_LENGTH: |
8461 | case GT_CAST: |
8462 | case GT_BITCAST: |
8463 | case GT_CKFINITE: |
8464 | case GT_LCLHEAP: |
8465 | case GT_ADDR: |
8466 | case GT_IND: |
8467 | case GT_OBJ: |
8468 | case GT_BLK: |
8469 | case GT_BOX: |
8470 | case GT_ALLOCOBJ: |
8471 | case GT_RUNTIMELOOKUP: |
8472 | case GT_INIT_VAL: |
8473 | case GT_JTRUE: |
8474 | case GT_SWITCH: |
8475 | case GT_NULLCHECK: |
8476 | case GT_PUTARG_REG: |
8477 | case GT_PUTARG_STK: |
8478 | case GT_BSWAP: |
8479 | case GT_BSWAP16: |
8480 | #if FEATURE_ARG_SPLIT |
8481 | case GT_PUTARG_SPLIT: |
8482 | #endif // FEATURE_ARG_SPLIT |
8483 | case GT_RETURNTRAP: |
8484 | m_edge = &m_node->AsUnOp()->gtOp1; |
8485 | assert(*m_edge != nullptr); |
8486 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8487 | return; |
8488 | |
8489 | // Unary operators with an optional operand |
8490 | case GT_NOP: |
8491 | case GT_RETURN: |
8492 | case GT_RETFILT: |
8493 | if (m_node->AsUnOp()->gtOp1 == nullptr) |
8494 | { |
8495 | assert(m_node->NullOp1Legal()); |
8496 | m_state = -1; |
8497 | } |
8498 | else |
8499 | { |
8500 | m_edge = &m_node->AsUnOp()->gtOp1; |
8501 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8502 | } |
8503 | return; |
8504 | |
8505 | // Variadic nodes |
8506 | case GT_PHI: |
8507 | SetEntryStateForList(m_node->AsUnOp()->gtOp1); |
8508 | return; |
8509 | |
8510 | case GT_FIELD_LIST: |
8511 | SetEntryStateForList(m_node); |
8512 | return; |
8513 | |
8514 | #ifdef FEATURE_SIMD |
8515 | case GT_SIMD: |
8516 | if (m_node->AsSIMD()->gtSIMDIntrinsicID == SIMDIntrinsicInitN) |
8517 | { |
8518 | SetEntryStateForList(m_node->AsSIMD()->gtOp1); |
8519 | } |
8520 | else |
8521 | { |
8522 | SetEntryStateForBinOp(); |
8523 | } |
8524 | return; |
8525 | #endif // FEATURE_SIMD |
8526 | |
8527 | #ifdef FEATURE_HW_INTRINSICS |
8528 | case GT_HWIntrinsic: |
8529 | if (m_node->AsHWIntrinsic()->gtOp1 == nullptr) |
8530 | { |
8531 | assert(m_node->NullOp1Legal()); |
8532 | m_state = -1; |
8533 | } |
8534 | else if (m_node->AsHWIntrinsic()->gtOp1->OperIsList()) |
8535 | { |
8536 | SetEntryStateForList(m_node->AsHWIntrinsic()->gtOp1); |
8537 | } |
8538 | else |
8539 | { |
8540 | SetEntryStateForBinOp(); |
8541 | } |
8542 | return; |
8543 | #endif // FEATURE_HW_INTRINSICS |
8544 | |
8545 | // LEA, which may have no first operand |
8546 | case GT_LEA: |
8547 | if (m_node->AsAddrMode()->gtOp1 == nullptr) |
8548 | { |
8549 | m_edge = &m_node->AsAddrMode()->gtOp2; |
8550 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8551 | } |
8552 | else |
8553 | { |
8554 | SetEntryStateForBinOp(); |
8555 | } |
8556 | return; |
8557 | |
8558 | // Special nodes |
8559 | case GT_CMPXCHG: |
8560 | m_edge = &m_node->AsCmpXchg()->gtOpLocation; |
8561 | assert(*m_edge != nullptr); |
8562 | m_advance = &GenTreeUseEdgeIterator::AdvanceCmpXchg; |
8563 | return; |
8564 | |
8565 | case GT_ARR_BOUNDS_CHECK: |
8566 | #ifdef FEATURE_SIMD |
8567 | case GT_SIMD_CHK: |
8568 | #endif // FEATURE_SIMD |
8569 | #ifdef FEATURE_HW_INTRINSICS |
8570 | case GT_HW_INTRINSIC_CHK: |
8571 | #endif // FEATURE_HW_INTRINSICS |
8572 | m_edge = &m_node->AsBoundsChk()->gtIndex; |
8573 | assert(*m_edge != nullptr); |
8574 | m_advance = &GenTreeUseEdgeIterator::AdvanceBoundsChk; |
8575 | return; |
8576 | |
8577 | case GT_FIELD: |
8578 | if (m_node->AsField()->gtFldObj == nullptr) |
8579 | { |
8580 | m_state = -1; |
8581 | } |
8582 | else |
8583 | { |
8584 | m_edge = &m_node->AsField()->gtFldObj; |
8585 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8586 | } |
8587 | return; |
8588 | |
8589 | case GT_STMT: |
8590 | if (m_node->AsStmt()->gtStmtExpr == nullptr) |
8591 | { |
8592 | m_state = -1; |
8593 | } |
8594 | else |
8595 | { |
8596 | m_edge = &m_node->AsStmt()->gtStmtExpr; |
8597 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8598 | } |
8599 | return; |
8600 | |
8601 | case GT_ARR_ELEM: |
8602 | m_edge = &m_node->AsArrElem()->gtArrObj; |
8603 | assert(*m_edge != nullptr); |
8604 | m_advance = &GenTreeUseEdgeIterator::AdvanceArrElem; |
8605 | return; |
8606 | |
8607 | case GT_ARR_OFFSET: |
8608 | m_edge = &m_node->AsArrOffs()->gtOffset; |
8609 | assert(*m_edge != nullptr); |
8610 | m_advance = &GenTreeUseEdgeIterator::AdvanceArrOffset; |
8611 | return; |
8612 | |
8613 | case GT_DYN_BLK: |
8614 | { |
8615 | GenTreeDynBlk* const dynBlock = m_node->AsDynBlk(); |
8616 | m_edge = dynBlock->gtEvalSizeFirst ? &dynBlock->gtDynamicSize : &dynBlock->gtOp1; |
8617 | assert(*m_edge != nullptr); |
8618 | m_advance = &GenTreeUseEdgeIterator::AdvanceDynBlk; |
8619 | } |
8620 | return; |
8621 | |
8622 | case GT_STORE_DYN_BLK: |
8623 | { |
8624 | GenTreeDynBlk* const dynBlock = m_node->AsDynBlk(); |
8625 | if (dynBlock->gtEvalSizeFirst) |
8626 | { |
8627 | m_edge = &dynBlock->gtDynamicSize; |
8628 | } |
8629 | else |
8630 | { |
8631 | m_edge = dynBlock->IsReverseOp() ? &dynBlock->gtOp2 : &dynBlock->gtOp1; |
8632 | } |
8633 | assert(*m_edge != nullptr); |
8634 | |
8635 | m_advance = &GenTreeUseEdgeIterator::AdvanceStoreDynBlk; |
8636 | } |
8637 | return; |
8638 | |
8639 | case GT_CALL: |
8640 | AdvanceCall<CALL_INSTANCE>(); |
8641 | return; |
8642 | |
8643 | // Binary nodes |
8644 | default: |
8645 | assert(m_node->OperIsBinary()); |
8646 | SetEntryStateForBinOp(); |
8647 | return; |
8648 | } |
8649 | } |
8650 | |
8651 | //------------------------------------------------------------------------ |
8652 | // GenTreeUseEdgeIterator::AdvanceCmpXchg: produces the next operand of a CmpXchg node and advances the state. |
8653 | // |
8654 | void GenTreeUseEdgeIterator::AdvanceCmpXchg() |
8655 | { |
8656 | switch (m_state) |
8657 | { |
8658 | case 0: |
8659 | m_edge = &m_node->AsCmpXchg()->gtOpValue; |
8660 | m_state = 1; |
8661 | break; |
8662 | case 1: |
8663 | m_edge = &m_node->AsCmpXchg()->gtOpComparand; |
8664 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8665 | break; |
8666 | default: |
8667 | unreached(); |
8668 | } |
8669 | |
8670 | assert(*m_edge != nullptr); |
8671 | } |
8672 | |
8673 | //------------------------------------------------------------------------ |
8674 | // GenTreeUseEdgeIterator::AdvanceBoundsChk: produces the next operand of a BoundsChk node and advances the state. |
8675 | // |
8676 | void GenTreeUseEdgeIterator::AdvanceBoundsChk() |
8677 | { |
8678 | m_edge = &m_node->AsBoundsChk()->gtArrLen; |
8679 | assert(*m_edge != nullptr); |
8680 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8681 | } |
8682 | |
8683 | //------------------------------------------------------------------------ |
8684 | // GenTreeUseEdgeIterator::AdvanceArrElem: produces the next operand of a ArrElem node and advances the state. |
8685 | // |
8686 | // Because these nodes are variadic, this function uses `m_state` to index into the list of array indices. |
8687 | // |
8688 | void GenTreeUseEdgeIterator::AdvanceArrElem() |
8689 | { |
8690 | if (m_state < m_node->AsArrElem()->gtArrRank) |
8691 | { |
8692 | m_edge = &m_node->AsArrElem()->gtArrInds[m_state]; |
8693 | assert(*m_edge != nullptr); |
8694 | m_state++; |
8695 | } |
8696 | else |
8697 | { |
8698 | m_state = -1; |
8699 | } |
8700 | } |
8701 | |
8702 | //------------------------------------------------------------------------ |
8703 | // GenTreeUseEdgeIterator::AdvanceArrOffset: produces the next operand of a ArrOffset node and advances the state. |
8704 | // |
8705 | void GenTreeUseEdgeIterator::AdvanceArrOffset() |
8706 | { |
8707 | switch (m_state) |
8708 | { |
8709 | case 0: |
8710 | m_edge = &m_node->AsArrOffs()->gtIndex; |
8711 | m_state = 1; |
8712 | break; |
8713 | case 1: |
8714 | m_edge = &m_node->AsArrOffs()->gtArrObj; |
8715 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8716 | break; |
8717 | default: |
8718 | unreached(); |
8719 | } |
8720 | |
8721 | assert(*m_edge != nullptr); |
8722 | } |
8723 | |
8724 | //------------------------------------------------------------------------ |
8725 | // GenTreeUseEdgeIterator::AdvanceDynBlk: produces the next operand of a DynBlk node and advances the state. |
8726 | // |
8727 | void GenTreeUseEdgeIterator::AdvanceDynBlk() |
8728 | { |
8729 | GenTreeDynBlk* const dynBlock = m_node->AsDynBlk(); |
8730 | |
8731 | m_edge = dynBlock->gtEvalSizeFirst ? &dynBlock->gtOp1 : &dynBlock->gtDynamicSize; |
8732 | assert(*m_edge != nullptr); |
8733 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8734 | } |
8735 | |
8736 | //------------------------------------------------------------------------ |
8737 | // GenTreeUseEdgeIterator::AdvanceStoreDynBlk: produces the next operand of a StoreDynBlk node and advances the state. |
8738 | // |
8739 | // These nodes are moderately complicated but rare enough that templating this function is probably not |
8740 | // worth the extra complexity. |
8741 | // |
8742 | void GenTreeUseEdgeIterator::AdvanceStoreDynBlk() |
8743 | { |
8744 | GenTreeDynBlk* const dynBlock = m_node->AsDynBlk(); |
8745 | if (dynBlock->gtEvalSizeFirst) |
8746 | { |
8747 | switch (m_state) |
8748 | { |
8749 | case 0: |
8750 | m_edge = dynBlock->IsReverseOp() ? &dynBlock->gtOp2 : &dynBlock->gtOp1; |
8751 | m_state = 1; |
8752 | break; |
8753 | case 1: |
8754 | m_edge = dynBlock->IsReverseOp() ? &dynBlock->gtOp1 : &dynBlock->gtOp2; |
8755 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8756 | break; |
8757 | default: |
8758 | unreached(); |
8759 | } |
8760 | } |
8761 | else |
8762 | { |
8763 | switch (m_state) |
8764 | { |
8765 | case 0: |
8766 | m_edge = dynBlock->IsReverseOp() ? &dynBlock->gtOp1 : &dynBlock->gtOp2; |
8767 | m_state = 1; |
8768 | break; |
8769 | case 1: |
8770 | m_edge = &dynBlock->gtDynamicSize; |
8771 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8772 | break; |
8773 | default: |
8774 | unreached(); |
8775 | } |
8776 | } |
8777 | |
8778 | assert(*m_edge != nullptr); |
8779 | } |
8780 | |
8781 | //------------------------------------------------------------------------ |
8782 | // GenTreeUseEdgeIterator::AdvanceBinOp: produces the next operand of a binary node and advances the state. |
8783 | // |
8784 | // This function must be instantiated s.t. `ReverseOperands` is `true` iff the node is marked with the |
8785 | // `GTF_REVERSE_OPS` flag. |
8786 | // |
8787 | template <bool ReverseOperands> |
8788 | void GenTreeUseEdgeIterator::AdvanceBinOp() |
8789 | { |
8790 | assert(ReverseOperands == ((m_node->gtFlags & GTF_REVERSE_OPS) != 0)); |
8791 | |
8792 | m_edge = !ReverseOperands ? &m_node->AsOp()->gtOp2 : &m_node->AsOp()->gtOp1; |
8793 | assert(*m_edge != nullptr); |
8794 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8795 | } |
8796 | |
8797 | //------------------------------------------------------------------------ |
8798 | // GenTreeUseEdgeIterator::SetEntryStateForBinOp: produces the first operand of a binary node and chooses |
8799 | // the appropriate advance function. |
8800 | // |
8801 | void GenTreeUseEdgeIterator::SetEntryStateForBinOp() |
8802 | { |
8803 | assert(m_node != nullptr); |
8804 | assert(m_node->OperIsBinary()); |
8805 | |
8806 | GenTreeOp* const node = m_node->AsOp(); |
8807 | |
8808 | if (node->gtOp2 == nullptr) |
8809 | { |
8810 | assert(node->gtOp1 != nullptr); |
8811 | assert(node->NullOp2Legal()); |
8812 | m_edge = &node->gtOp1; |
8813 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8814 | } |
8815 | else if ((node->gtFlags & GTF_REVERSE_OPS) != 0) |
8816 | { |
8817 | m_edge = &m_node->AsOp()->gtOp2; |
8818 | m_advance = &GenTreeUseEdgeIterator::AdvanceBinOp<true>; |
8819 | } |
8820 | else |
8821 | { |
8822 | m_edge = &m_node->AsOp()->gtOp1; |
8823 | m_advance = &GenTreeUseEdgeIterator::AdvanceBinOp<false>; |
8824 | } |
8825 | } |
8826 | |
8827 | //------------------------------------------------------------------------ |
8828 | // GenTreeUseEdgeIterator::AdvanceList: produces the next operand of a variadic node and advances the state. |
8829 | // |
8830 | // This function does not use `m_state` for anything meaningful; it simply walks the `m_argList` until |
8831 | // there are no further entries. |
8832 | // |
8833 | void GenTreeUseEdgeIterator::AdvanceList() |
8834 | { |
8835 | assert(m_state == 0); |
8836 | |
8837 | if (m_argList == nullptr) |
8838 | { |
8839 | m_state = -1; |
8840 | } |
8841 | else |
8842 | { |
8843 | GenTreeArgList* listNode = m_argList->AsArgList(); |
8844 | m_edge = &listNode->gtOp1; |
8845 | m_argList = listNode->Rest(); |
8846 | } |
8847 | } |
8848 | |
8849 | //------------------------------------------------------------------------ |
8850 | // GenTreeUseEdgeIterator::SetEntryStateForList: produces the first operand of a list node. |
8851 | // |
8852 | void GenTreeUseEdgeIterator::SetEntryStateForList(GenTree* list) |
8853 | { |
8854 | m_argList = list; |
8855 | m_advance = &GenTreeUseEdgeIterator::AdvanceList; |
8856 | AdvanceList(); |
8857 | } |
8858 | |
8859 | //------------------------------------------------------------------------ |
8860 | // GenTreeUseEdgeIterator::AdvanceCall: produces the next operand of a call node and advances the state. |
8861 | // |
8862 | // This function is a bit tricky: in order to avoid doing unnecessary work, it is instantiated with the |
8863 | // state number the iterator will be in when it is called. For example, `AdvanceCall<CALL_INSTANCE>` |
8864 | // is the instantiation used when the iterator is at the `CALL_INSTANCE` state (i.e. the entry state). |
8865 | // This sort of templating allows each state to avoid processing earlier states without unnecessary |
8866 | // duplication of code. |
8867 | // |
8868 | // Note that this method expands the argument lists (`gtCallArgs` and `gtCallLateArgs`) into their |
8869 | // component operands. |
8870 | // |
8871 | template <int state> |
8872 | void GenTreeUseEdgeIterator::AdvanceCall() |
8873 | { |
8874 | GenTreeCall* const call = m_node->AsCall(); |
8875 | |
8876 | switch (state) |
8877 | { |
8878 | case CALL_INSTANCE: |
8879 | m_argList = call->gtCallArgs; |
8880 | m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_ARGS>; |
8881 | if (call->gtCallObjp != nullptr) |
8882 | { |
8883 | m_edge = &call->gtCallObjp; |
8884 | return; |
8885 | } |
8886 | __fallthrough; |
8887 | |
8888 | case CALL_ARGS: |
8889 | if (m_argList != nullptr) |
8890 | { |
8891 | GenTreeArgList* argNode = m_argList->AsArgList(); |
8892 | m_edge = &argNode->gtOp1; |
8893 | m_argList = argNode->Rest(); |
8894 | return; |
8895 | } |
8896 | m_argList = call->gtCallLateArgs; |
8897 | m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_LATE_ARGS>; |
8898 | __fallthrough; |
8899 | |
8900 | case CALL_LATE_ARGS: |
8901 | if (m_argList != nullptr) |
8902 | { |
8903 | GenTreeArgList* argNode = m_argList->AsArgList(); |
8904 | m_edge = &argNode->gtOp1; |
8905 | m_argList = argNode->Rest(); |
8906 | return; |
8907 | } |
8908 | m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_CONTROL_EXPR>; |
8909 | __fallthrough; |
8910 | |
8911 | case CALL_CONTROL_EXPR: |
8912 | if (call->gtControlExpr != nullptr) |
8913 | { |
8914 | if (call->gtCallType == CT_INDIRECT) |
8915 | { |
8916 | m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_COOKIE>; |
8917 | } |
8918 | else |
8919 | { |
8920 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8921 | } |
8922 | m_edge = &call->gtControlExpr; |
8923 | return; |
8924 | } |
8925 | else if (call->gtCallType != CT_INDIRECT) |
8926 | { |
8927 | m_state = -1; |
8928 | return; |
8929 | } |
8930 | __fallthrough; |
8931 | |
8932 | case CALL_COOKIE: |
8933 | assert(call->gtCallType == CT_INDIRECT); |
8934 | |
8935 | m_advance = &GenTreeUseEdgeIterator::AdvanceCall<CALL_ADDRESS>; |
8936 | if (call->gtCallCookie != nullptr) |
8937 | { |
8938 | m_edge = &call->gtCallCookie; |
8939 | return; |
8940 | } |
8941 | __fallthrough; |
8942 | |
8943 | case CALL_ADDRESS: |
8944 | assert(call->gtCallType == CT_INDIRECT); |
8945 | |
8946 | m_advance = &GenTreeUseEdgeIterator::Terminate; |
8947 | if (call->gtCallAddr != nullptr) |
8948 | { |
8949 | m_edge = &call->gtCallAddr; |
8950 | } |
8951 | return; |
8952 | |
8953 | default: |
8954 | unreached(); |
8955 | } |
8956 | } |
8957 | |
8958 | //------------------------------------------------------------------------ |
8959 | // GenTreeUseEdgeIterator::Terminate: advances the iterator to the terminal state. |
8960 | // |
8961 | void GenTreeUseEdgeIterator::Terminate() |
8962 | { |
8963 | m_state = -1; |
8964 | } |
8965 | |
8966 | //------------------------------------------------------------------------ |
8967 | // GenTreeUseEdgeIterator::operator++: advances the iterator to the next operand. |
8968 | // |
8969 | GenTreeUseEdgeIterator& GenTreeUseEdgeIterator::operator++() |
8970 | { |
8971 | // If we've reached the terminal state, do nothing. |
8972 | if (m_state != -1) |
8973 | { |
8974 | (this->*m_advance)(); |
8975 | } |
8976 | |
8977 | return *this; |
8978 | } |
8979 | |
8980 | GenTreeUseEdgeIterator GenTree::UseEdgesBegin() |
8981 | { |
8982 | return GenTreeUseEdgeIterator(this); |
8983 | } |
8984 | |
8985 | GenTreeUseEdgeIterator GenTree::UseEdgesEnd() |
8986 | { |
8987 | return GenTreeUseEdgeIterator(); |
8988 | } |
8989 | |
8990 | IteratorPair<GenTreeUseEdgeIterator> GenTree::UseEdges() |
8991 | { |
8992 | return MakeIteratorPair(UseEdgesBegin(), UseEdgesEnd()); |
8993 | } |
8994 | |
8995 | GenTreeOperandIterator GenTree::OperandsBegin() |
8996 | { |
8997 | return GenTreeOperandIterator(this); |
8998 | } |
8999 | |
9000 | GenTreeOperandIterator GenTree::OperandsEnd() |
9001 | { |
9002 | return GenTreeOperandIterator(); |
9003 | } |
9004 | |
9005 | IteratorPair<GenTreeOperandIterator> GenTree::Operands() |
9006 | { |
9007 | return MakeIteratorPair(OperandsBegin(), OperandsEnd()); |
9008 | } |
9009 | |
9010 | bool GenTree::Precedes(GenTree* other) |
9011 | { |
9012 | assert(other != nullptr); |
9013 | |
9014 | for (GenTree* node = gtNext; node != nullptr; node = node->gtNext) |
9015 | { |
9016 | if (node == other) |
9017 | { |
9018 | return true; |
9019 | } |
9020 | } |
9021 | |
9022 | return false; |
9023 | } |
9024 | |
9025 | #ifdef DEBUG |
9026 | |
9027 | /* static */ int GenTree::gtDispFlags(unsigned flags, unsigned debugFlags) |
9028 | { |
9029 | int charsDisplayed = 11; // 11 is the "baseline" number of flag characters displayed |
9030 | |
9031 | printf("%c" , (flags & GTF_ASG) ? 'A' : (IsContained(flags) ? 'c' : '-')); |
9032 | printf("%c" , (flags & GTF_CALL) ? 'C' : '-'); |
9033 | printf("%c" , (flags & GTF_EXCEPT) ? 'X' : '-'); |
9034 | printf("%c" , (flags & GTF_GLOB_REF) ? 'G' : '-'); |
9035 | printf("%c" , (debugFlags & GTF_DEBUG_NODE_MORPHED) ? '+' : // First print '+' if GTF_DEBUG_NODE_MORPHED is set |
9036 | (flags & GTF_ORDER_SIDEEFF) ? 'O' : '-'); // otherwise print 'O' or '-' |
9037 | printf("%c" , (flags & GTF_COLON_COND) ? '?' : '-'); |
9038 | printf("%c" , (flags & GTF_DONT_CSE) ? 'N' : // N is for No cse |
9039 | (flags & GTF_MAKE_CSE) ? 'H' : '-'); // H is for Hoist this expr |
9040 | printf("%c" , (flags & GTF_REVERSE_OPS) ? 'R' : '-'); |
9041 | printf("%c" , (flags & GTF_UNSIGNED) ? 'U' : (flags & GTF_BOOLEAN) ? 'B' : '-'); |
9042 | #if FEATURE_SET_FLAGS |
9043 | printf("%c" , (flags & GTF_SET_FLAGS) ? 'S' : '-'); |
9044 | ++charsDisplayed; |
9045 | #endif |
9046 | printf("%c" , (flags & GTF_LATE_ARG) ? 'L' : '-'); |
9047 | printf("%c" , (flags & GTF_SPILLED) ? 'z' : (flags & GTF_SPILL) ? 'Z' : '-'); |
9048 | |
9049 | return charsDisplayed; |
9050 | } |
9051 | |
9052 | /*****************************************************************************/ |
9053 | |
9054 | void Compiler::gtDispNodeName(GenTree* tree) |
9055 | { |
9056 | /* print the node name */ |
9057 | |
9058 | const char* name; |
9059 | |
9060 | assert(tree); |
9061 | if (tree->gtOper < GT_COUNT) |
9062 | { |
9063 | name = GenTree::OpName(tree->OperGet()); |
9064 | } |
9065 | else |
9066 | { |
9067 | name = "<ERROR>" ; |
9068 | } |
9069 | char buf[32]; |
9070 | char* bufp = &buf[0]; |
9071 | |
9072 | if ((tree->gtOper == GT_CNS_INT) && tree->IsIconHandle()) |
9073 | { |
9074 | sprintf_s(bufp, sizeof(buf), " %s(h)%c" , name, 0); |
9075 | } |
9076 | else if (tree->gtOper == GT_PUTARG_STK) |
9077 | { |
9078 | sprintf_s(bufp, sizeof(buf), " %s [+0x%02x]%c" , name, tree->AsPutArgStk()->getArgOffset(), 0); |
9079 | } |
9080 | else if (tree->gtOper == GT_CALL) |
9081 | { |
9082 | const char* callType = "CALL" ; |
9083 | const char* gtfType = "" ; |
9084 | const char* ctType = "" ; |
9085 | char gtfTypeBuf[100]; |
9086 | |
9087 | if (tree->gtCall.gtCallType == CT_USER_FUNC) |
9088 | { |
9089 | if (tree->gtCall.IsVirtual()) |
9090 | { |
9091 | callType = "CALLV" ; |
9092 | } |
9093 | } |
9094 | else if (tree->gtCall.gtCallType == CT_HELPER) |
9095 | { |
9096 | ctType = " help" ; |
9097 | } |
9098 | else if (tree->gtCall.gtCallType == CT_INDIRECT) |
9099 | { |
9100 | ctType = " ind" ; |
9101 | } |
9102 | else |
9103 | { |
9104 | assert(!"Unknown gtCallType" ); |
9105 | } |
9106 | |
9107 | if (tree->gtFlags & GTF_CALL_NULLCHECK) |
9108 | { |
9109 | gtfType = " nullcheck" ; |
9110 | } |
9111 | if (tree->gtCall.IsVirtualVtable()) |
9112 | { |
9113 | gtfType = " ind" ; |
9114 | } |
9115 | else if (tree->gtCall.IsVirtualStub()) |
9116 | { |
9117 | gtfType = " stub" ; |
9118 | } |
9119 | #ifdef FEATURE_READYTORUN_COMPILER |
9120 | else if (tree->gtCall.IsR2RRelativeIndir()) |
9121 | { |
9122 | gtfType = " r2r_ind" ; |
9123 | } |
9124 | #endif // FEATURE_READYTORUN_COMPILER |
9125 | else if (tree->gtFlags & GTF_CALL_UNMANAGED) |
9126 | { |
9127 | char* gtfTypeBufWalk = gtfTypeBuf; |
9128 | gtfTypeBufWalk += SimpleSprintf_s(gtfTypeBufWalk, gtfTypeBuf, sizeof(gtfTypeBuf), " unman" ); |
9129 | if (tree->gtFlags & GTF_CALL_POP_ARGS) |
9130 | { |
9131 | gtfTypeBufWalk += SimpleSprintf_s(gtfTypeBufWalk, gtfTypeBuf, sizeof(gtfTypeBuf), " popargs" ); |
9132 | } |
9133 | if (tree->gtCall.gtCallMoreFlags & GTF_CALL_M_UNMGD_THISCALL) |
9134 | { |
9135 | gtfTypeBufWalk += SimpleSprintf_s(gtfTypeBufWalk, gtfTypeBuf, sizeof(gtfTypeBuf), " thiscall" ); |
9136 | } |
9137 | gtfType = gtfTypeBuf; |
9138 | } |
9139 | |
9140 | sprintf_s(bufp, sizeof(buf), " %s%s%s%c" , callType, ctType, gtfType, 0); |
9141 | } |
9142 | else if (tree->gtOper == GT_ARR_ELEM) |
9143 | { |
9144 | bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), " %s[" , name); |
9145 | for (unsigned rank = tree->gtArrElem.gtArrRank - 1; rank; rank--) |
9146 | { |
9147 | bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "," ); |
9148 | } |
9149 | SimpleSprintf_s(bufp, buf, sizeof(buf), "]" ); |
9150 | } |
9151 | else if (tree->gtOper == GT_ARR_OFFSET || tree->gtOper == GT_ARR_INDEX) |
9152 | { |
9153 | bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), " %s[" , name); |
9154 | unsigned char currDim; |
9155 | unsigned char rank; |
9156 | if (tree->gtOper == GT_ARR_OFFSET) |
9157 | { |
9158 | currDim = tree->gtArrOffs.gtCurrDim; |
9159 | rank = tree->gtArrOffs.gtArrRank; |
9160 | } |
9161 | else |
9162 | { |
9163 | currDim = tree->gtArrIndex.gtCurrDim; |
9164 | rank = tree->gtArrIndex.gtArrRank; |
9165 | } |
9166 | |
9167 | for (unsigned char dim = 0; dim < rank; dim++) |
9168 | { |
9169 | // Use a defacto standard i,j,k for the dimensions. |
9170 | // Note that we only support up to rank 3 arrays with these nodes, so we won't run out of characters. |
9171 | char dimChar = '*'; |
9172 | if (dim == currDim) |
9173 | { |
9174 | dimChar = 'i' + dim; |
9175 | } |
9176 | else if (dim > currDim) |
9177 | { |
9178 | dimChar = ' '; |
9179 | } |
9180 | |
9181 | bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "%c" , dimChar); |
9182 | if (dim != rank - 1) |
9183 | { |
9184 | bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "," ); |
9185 | } |
9186 | } |
9187 | SimpleSprintf_s(bufp, buf, sizeof(buf), "]" ); |
9188 | } |
9189 | else if (tree->gtOper == GT_LEA) |
9190 | { |
9191 | GenTreeAddrMode* lea = tree->AsAddrMode(); |
9192 | bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), " %s(" , name); |
9193 | if (lea->Base() != nullptr) |
9194 | { |
9195 | bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "b+" ); |
9196 | } |
9197 | if (lea->Index() != nullptr) |
9198 | { |
9199 | bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "(i*%d)+" , lea->gtScale); |
9200 | } |
9201 | bufp += SimpleSprintf_s(bufp, buf, sizeof(buf), "%d)" , lea->Offset()); |
9202 | } |
9203 | else if (tree->gtOper == GT_ARR_BOUNDS_CHECK) |
9204 | { |
9205 | switch (tree->gtBoundsChk.gtThrowKind) |
9206 | { |
9207 | case SCK_RNGCHK_FAIL: |
9208 | sprintf_s(bufp, sizeof(buf), " %s_Rng" , name); |
9209 | break; |
9210 | case SCK_ARG_EXCPN: |
9211 | sprintf_s(bufp, sizeof(buf), " %s_Arg" , name); |
9212 | break; |
9213 | case SCK_ARG_RNG_EXCPN: |
9214 | sprintf_s(bufp, sizeof(buf), " %s_ArgRng" , name); |
9215 | break; |
9216 | default: |
9217 | unreached(); |
9218 | } |
9219 | } |
9220 | else if (tree->gtOverflowEx()) |
9221 | { |
9222 | sprintf_s(bufp, sizeof(buf), " %s_ovfl%c" , name, 0); |
9223 | } |
9224 | else if (tree->OperIsBlk() && !tree->OperIsDynBlk()) |
9225 | { |
9226 | sprintf_s(bufp, sizeof(buf), " %s(%d)" , name, tree->AsBlk()->gtBlkSize); |
9227 | } |
9228 | else |
9229 | { |
9230 | sprintf_s(bufp, sizeof(buf), " %s%c" , name, 0); |
9231 | } |
9232 | |
9233 | if (strlen(buf) < 10) |
9234 | { |
9235 | printf(" %-10s" , buf); |
9236 | } |
9237 | else |
9238 | { |
9239 | printf(" %s" , buf); |
9240 | } |
9241 | } |
9242 | |
9243 | void Compiler::gtDispVN(GenTree* tree) |
9244 | { |
9245 | if (tree->gtVNPair.GetLiberal() != ValueNumStore::NoVN) |
9246 | { |
9247 | assert(tree->gtVNPair.GetConservative() != ValueNumStore::NoVN); |
9248 | printf(" " ); |
9249 | vnpPrint(tree->gtVNPair, 0); |
9250 | } |
9251 | } |
9252 | |
9253 | //------------------------------------------------------------------------ |
9254 | // gtDispNode: Print a tree to jitstdout. |
9255 | // |
9256 | // Arguments: |
9257 | // tree - the tree to be printed |
9258 | // indentStack - the specification for the current level of indentation & arcs |
9259 | // msg - a contextual method (i.e. from the parent) to print |
9260 | // |
9261 | // Return Value: |
9262 | // None. |
9263 | // |
9264 | // Notes: |
9265 | // 'indentStack' may be null, in which case no indentation or arcs are printed |
9266 | // 'msg' may be null |
9267 | |
9268 | void Compiler::gtDispNode(GenTree* tree, IndentStack* indentStack, __in __in_z __in_opt const char* msg, bool isLIR) |
9269 | { |
9270 | bool printPointer = true; // always true.. |
9271 | bool printFlags = true; // always true.. |
9272 | bool printCost = true; // always true.. |
9273 | |
9274 | int msgLength = 25; |
9275 | |
9276 | GenTree* prev; |
9277 | |
9278 | if (tree->gtSeqNum) |
9279 | { |
9280 | printf("N%03u " , tree->gtSeqNum); |
9281 | if (tree->gtCostsInitialized) |
9282 | { |
9283 | printf("(%3u,%3u) " , tree->gtCostEx, tree->gtCostSz); |
9284 | } |
9285 | else |
9286 | { |
9287 | printf("(???" |
9288 | ",???" |
9289 | ") " ); // This probably indicates a bug: the node has a sequence number, but not costs. |
9290 | } |
9291 | } |
9292 | else |
9293 | { |
9294 | if (tree->gtOper == GT_STMT) |
9295 | { |
9296 | prev = tree->gtStmt.gtStmtExpr; |
9297 | } |
9298 | else |
9299 | { |
9300 | prev = tree; |
9301 | } |
9302 | |
9303 | bool hasSeqNum = true; |
9304 | unsigned dotNum = 0; |
9305 | do |
9306 | { |
9307 | dotNum++; |
9308 | prev = prev->gtPrev; |
9309 | |
9310 | if ((prev == nullptr) || (prev == tree)) |
9311 | { |
9312 | hasSeqNum = false; |
9313 | break; |
9314 | } |
9315 | |
9316 | assert(prev); |
9317 | } while (prev->gtSeqNum == 0); |
9318 | |
9319 | // If we have an indent stack, don't add additional characters, |
9320 | // as it will mess up the alignment. |
9321 | bool displayDotNum = tree->gtOper != GT_STMT && hasSeqNum && (indentStack == nullptr); |
9322 | if (displayDotNum) |
9323 | { |
9324 | printf("N%03u.%02u " , prev->gtSeqNum, dotNum); |
9325 | } |
9326 | else |
9327 | { |
9328 | printf(" " ); |
9329 | } |
9330 | |
9331 | if (tree->gtCostsInitialized) |
9332 | { |
9333 | printf("(%3u,%3u) " , tree->gtCostEx, tree->gtCostSz); |
9334 | } |
9335 | else |
9336 | { |
9337 | if (displayDotNum) |
9338 | { |
9339 | // Do better alignment in this case |
9340 | printf(" " ); |
9341 | } |
9342 | else |
9343 | { |
9344 | printf(" " ); |
9345 | } |
9346 | } |
9347 | } |
9348 | |
9349 | if (optValnumCSE_phase) |
9350 | { |
9351 | if (IS_CSE_INDEX(tree->gtCSEnum)) |
9352 | { |
9353 | printf("CSE #%02d (%s)" , GET_CSE_INDEX(tree->gtCSEnum), (IS_CSE_USE(tree->gtCSEnum) ? "use" : "def" )); |
9354 | } |
9355 | else |
9356 | { |
9357 | printf(" " ); |
9358 | } |
9359 | } |
9360 | |
9361 | /* Print the node ID */ |
9362 | printTreeID(tree); |
9363 | printf(" " ); |
9364 | |
9365 | if (tree->gtOper >= GT_COUNT) |
9366 | { |
9367 | printf(" **** ILLEGAL NODE ****" ); |
9368 | return; |
9369 | } |
9370 | |
9371 | if (printFlags) |
9372 | { |
9373 | /* First print the flags associated with the node */ |
9374 | switch (tree->gtOper) |
9375 | { |
9376 | case GT_LEA: |
9377 | case GT_BLK: |
9378 | case GT_OBJ: |
9379 | case GT_DYN_BLK: |
9380 | case GT_STORE_BLK: |
9381 | case GT_STORE_OBJ: |
9382 | case GT_STORE_DYN_BLK: |
9383 | |
9384 | case GT_IND: |
9385 | // We prefer printing V or U |
9386 | if ((tree->gtFlags & (GTF_IND_VOLATILE | GTF_IND_UNALIGNED)) == 0) |
9387 | { |
9388 | if (tree->gtFlags & GTF_IND_TGTANYWHERE) |
9389 | { |
9390 | printf("*" ); |
9391 | --msgLength; |
9392 | break; |
9393 | } |
9394 | if (tree->gtFlags & GTF_IND_INVARIANT) |
9395 | { |
9396 | printf("#" ); |
9397 | --msgLength; |
9398 | break; |
9399 | } |
9400 | if (tree->gtFlags & GTF_IND_ARR_INDEX) |
9401 | { |
9402 | printf("a" ); |
9403 | --msgLength; |
9404 | break; |
9405 | } |
9406 | if (tree->gtFlags & GTF_IND_NONFAULTING) |
9407 | { |
9408 | printf("n" ); // print a n for non-faulting |
9409 | --msgLength; |
9410 | break; |
9411 | } |
9412 | if (tree->gtFlags & GTF_IND_ASG_LHS) |
9413 | { |
9414 | printf("D" ); // print a D for definition |
9415 | --msgLength; |
9416 | break; |
9417 | } |
9418 | } |
9419 | __fallthrough; |
9420 | |
9421 | case GT_INDEX: |
9422 | case GT_INDEX_ADDR: |
9423 | |
9424 | if ((tree->gtFlags & (GTF_IND_VOLATILE | GTF_IND_UNALIGNED)) == 0) // We prefer printing V or U over R |
9425 | { |
9426 | if (tree->gtFlags & GTF_INX_REFARR_LAYOUT) |
9427 | { |
9428 | printf("R" ); |
9429 | --msgLength; |
9430 | break; |
9431 | } // R means RefArray |
9432 | } |
9433 | __fallthrough; |
9434 | |
9435 | case GT_FIELD: |
9436 | case GT_CLS_VAR: |
9437 | if (tree->gtFlags & GTF_IND_VOLATILE) |
9438 | { |
9439 | printf("V" ); |
9440 | --msgLength; |
9441 | break; |
9442 | } |
9443 | if (tree->gtFlags & GTF_IND_UNALIGNED) |
9444 | { |
9445 | printf("U" ); |
9446 | --msgLength; |
9447 | break; |
9448 | } |
9449 | goto DASH; |
9450 | |
9451 | case GT_ASG: |
9452 | if (tree->OperIsInitBlkOp()) |
9453 | { |
9454 | printf("I" ); |
9455 | --msgLength; |
9456 | break; |
9457 | } |
9458 | goto DASH; |
9459 | |
9460 | case GT_CALL: |
9461 | if (tree->gtCall.IsInlineCandidate()) |
9462 | { |
9463 | if (tree->gtCall.IsGuardedDevirtualizationCandidate()) |
9464 | { |
9465 | printf("&" ); |
9466 | } |
9467 | else |
9468 | { |
9469 | printf("I" ); |
9470 | } |
9471 | --msgLength; |
9472 | break; |
9473 | } |
9474 | else if (tree->gtCall.IsGuardedDevirtualizationCandidate()) |
9475 | { |
9476 | printf("G" ); |
9477 | --msgLength; |
9478 | break; |
9479 | } |
9480 | if (tree->gtCall.gtCallMoreFlags & GTF_CALL_M_RETBUFFARG) |
9481 | { |
9482 | printf("S" ); |
9483 | --msgLength; |
9484 | break; |
9485 | } |
9486 | if (tree->gtFlags & GTF_CALL_HOISTABLE) |
9487 | { |
9488 | printf("H" ); |
9489 | --msgLength; |
9490 | break; |
9491 | } |
9492 | |
9493 | goto DASH; |
9494 | |
9495 | case GT_MUL: |
9496 | #if !defined(_TARGET_64BIT_) |
9497 | case GT_MUL_LONG: |
9498 | #endif |
9499 | if (tree->gtFlags & GTF_MUL_64RSLT) |
9500 | { |
9501 | printf("L" ); |
9502 | --msgLength; |
9503 | break; |
9504 | } |
9505 | goto DASH; |
9506 | |
9507 | case GT_ADDR: |
9508 | if (tree->gtFlags & GTF_ADDR_ONSTACK) |
9509 | { |
9510 | printf("L" ); |
9511 | --msgLength; |
9512 | break; |
9513 | } // L means LclVar |
9514 | goto DASH; |
9515 | |
9516 | case GT_LCL_FLD: |
9517 | case GT_LCL_VAR: |
9518 | case GT_LCL_VAR_ADDR: |
9519 | case GT_LCL_FLD_ADDR: |
9520 | case GT_STORE_LCL_FLD: |
9521 | case GT_STORE_LCL_VAR: |
9522 | if (tree->gtFlags & GTF_VAR_USEASG) |
9523 | { |
9524 | printf("U" ); |
9525 | --msgLength; |
9526 | break; |
9527 | } |
9528 | if (tree->gtFlags & GTF_VAR_DEF) |
9529 | { |
9530 | printf("D" ); |
9531 | --msgLength; |
9532 | break; |
9533 | } |
9534 | if (tree->gtFlags & GTF_VAR_CAST) |
9535 | { |
9536 | printf("C" ); |
9537 | --msgLength; |
9538 | break; |
9539 | } |
9540 | if (tree->gtFlags & GTF_VAR_ARR_INDEX) |
9541 | { |
9542 | printf("i" ); |
9543 | --msgLength; |
9544 | break; |
9545 | } |
9546 | goto DASH; |
9547 | |
9548 | case GT_EQ: |
9549 | case GT_NE: |
9550 | case GT_LT: |
9551 | case GT_LE: |
9552 | case GT_GE: |
9553 | case GT_GT: |
9554 | case GT_TEST_EQ: |
9555 | case GT_TEST_NE: |
9556 | if (tree->gtFlags & GTF_RELOP_NAN_UN) |
9557 | { |
9558 | printf("N" ); |
9559 | --msgLength; |
9560 | break; |
9561 | } |
9562 | if (tree->gtFlags & GTF_RELOP_JMP_USED) |
9563 | { |
9564 | printf("J" ); |
9565 | --msgLength; |
9566 | break; |
9567 | } |
9568 | if (tree->gtFlags & GTF_RELOP_QMARK) |
9569 | { |
9570 | printf("Q" ); |
9571 | --msgLength; |
9572 | break; |
9573 | } |
9574 | goto DASH; |
9575 | |
9576 | case GT_JCMP: |
9577 | printf((tree->gtFlags & GTF_JCMP_TST) ? "T" : "C" ); |
9578 | printf((tree->gtFlags & GTF_JCMP_EQ) ? "EQ" : "NE" ); |
9579 | goto DASH; |
9580 | |
9581 | case GT_FIELD_LIST: |
9582 | if (tree->gtFlags & GTF_FIELD_LIST_HEAD) |
9583 | { |
9584 | printf("H" ); |
9585 | --msgLength; |
9586 | break; |
9587 | } |
9588 | goto DASH; |
9589 | |
9590 | default: |
9591 | DASH: |
9592 | printf("-" ); |
9593 | --msgLength; |
9594 | break; |
9595 | } |
9596 | |
9597 | /* Then print the general purpose flags */ |
9598 | unsigned flags = tree->gtFlags; |
9599 | |
9600 | if (tree->OperIsBinary()) |
9601 | { |
9602 | genTreeOps oper = tree->OperGet(); |
9603 | |
9604 | // Check for GTF_ADDRMODE_NO_CSE flag on add/mul/shl Binary Operators |
9605 | if ((oper == GT_ADD) || (oper == GT_MUL) || (oper == GT_LSH)) |
9606 | { |
9607 | if ((tree->gtFlags & GTF_ADDRMODE_NO_CSE) != 0) |
9608 | { |
9609 | flags |= GTF_DONT_CSE; // Force the GTF_ADDRMODE_NO_CSE flag to print out like GTF_DONT_CSE |
9610 | } |
9611 | } |
9612 | } |
9613 | else // !tree->OperIsBinary() |
9614 | { |
9615 | // the GTF_REVERSE flag only applies to binary operations |
9616 | flags &= ~GTF_REVERSE_OPS; // we use this value for GTF_VAR_ARR_INDEX above |
9617 | } |
9618 | |
9619 | msgLength -= GenTree::gtDispFlags(flags, tree->gtDebugFlags); |
9620 | /* |
9621 | printf("%c", (flags & GTF_ASG ) ? 'A' : '-'); |
9622 | printf("%c", (flags & GTF_CALL ) ? 'C' : '-'); |
9623 | printf("%c", (flags & GTF_EXCEPT ) ? 'X' : '-'); |
9624 | printf("%c", (flags & GTF_GLOB_REF ) ? 'G' : '-'); |
9625 | printf("%c", (flags & GTF_ORDER_SIDEEFF ) ? 'O' : '-'); |
9626 | printf("%c", (flags & GTF_COLON_COND ) ? '?' : '-'); |
9627 | printf("%c", (flags & GTF_DONT_CSE ) ? 'N' : // N is for No cse |
9628 | (flags & GTF_MAKE_CSE ) ? 'H' : '-'); // H is for Hoist this expr |
9629 | printf("%c", (flags & GTF_REVERSE_OPS ) ? 'R' : '-'); |
9630 | printf("%c", (flags & GTF_UNSIGNED ) ? 'U' : |
9631 | (flags & GTF_BOOLEAN ) ? 'B' : '-'); |
9632 | printf("%c", (flags & GTF_SET_FLAGS ) ? 'S' : '-'); |
9633 | printf("%c", (flags & GTF_SPILLED ) ? 'z' : '-'); |
9634 | printf("%c", (flags & GTF_SPILL ) ? 'Z' : '-'); |
9635 | */ |
9636 | } |
9637 | |
9638 | // If we're printing a node for LIR, we use the space normally associated with the message |
9639 | // to display the node's temp name (if any) |
9640 | const bool hasOperands = tree->OperandsBegin() != tree->OperandsEnd(); |
9641 | if (isLIR) |
9642 | { |
9643 | assert(msg == nullptr); |
9644 | |
9645 | // If the tree does not have any operands, we do not display the indent stack. This gives us |
9646 | // two additional characters for alignment. |
9647 | if (!hasOperands) |
9648 | { |
9649 | msgLength += 1; |
9650 | } |
9651 | |
9652 | if (tree->IsValue()) |
9653 | { |
9654 | const size_t bufLength = msgLength - 1; |
9655 | msg = reinterpret_cast<char*>(alloca(bufLength * sizeof(char))); |
9656 | sprintf_s(const_cast<char*>(msg), bufLength, "t%d = %s" , tree->gtTreeID, hasOperands ? "" : " " ); |
9657 | } |
9658 | } |
9659 | |
9660 | /* print the msg associated with the node */ |
9661 | |
9662 | if (msg == nullptr) |
9663 | { |
9664 | msg = "" ; |
9665 | } |
9666 | if (msgLength < 0) |
9667 | { |
9668 | msgLength = 0; |
9669 | } |
9670 | |
9671 | printf(isLIR ? " %+*s" : " %-*s" , msgLength, msg); |
9672 | |
9673 | /* Indent the node accordingly */ |
9674 | if (!isLIR || hasOperands) |
9675 | { |
9676 | printIndent(indentStack); |
9677 | } |
9678 | |
9679 | gtDispNodeName(tree); |
9680 | |
9681 | assert(tree == nullptr || tree->gtOper < GT_COUNT); |
9682 | |
9683 | if (tree) |
9684 | { |
9685 | /* print the type of the node */ |
9686 | if (tree->gtOper != GT_CAST) |
9687 | { |
9688 | printf(" %-6s" , varTypeName(tree->TypeGet())); |
9689 | if (tree->gtOper == GT_LCL_VAR || tree->gtOper == GT_STORE_LCL_VAR) |
9690 | { |
9691 | LclVarDsc* varDsc = &lvaTable[tree->gtLclVarCommon.gtLclNum]; |
9692 | if (varDsc->lvAddrExposed) |
9693 | { |
9694 | printf("(AX)" ); // Variable has address exposed. |
9695 | } |
9696 | |
9697 | if (varDsc->lvUnusedStruct) |
9698 | { |
9699 | assert(varDsc->lvPromoted); |
9700 | printf("(U)" ); // Unused struct |
9701 | } |
9702 | else if (varDsc->lvPromoted) |
9703 | { |
9704 | if (varTypeIsPromotable(varDsc)) |
9705 | { |
9706 | printf("(P)" ); // Promoted struct |
9707 | } |
9708 | else |
9709 | { |
9710 | // Promoted implicit by-refs can have this state during |
9711 | // global morph while they are being rewritten |
9712 | assert(fgGlobalMorph); |
9713 | printf("(P?!)" ); // Promoted struct |
9714 | } |
9715 | } |
9716 | } |
9717 | |
9718 | if (tree->gtOper == GT_STMT) |
9719 | { |
9720 | if (opts.compDbgInfo) |
9721 | { |
9722 | IL_OFFSET endIL = tree->gtStmt.gtStmtLastILoffs; |
9723 | |
9724 | printf("(IL " ); |
9725 | if (tree->gtStmt.gtStmtILoffsx == BAD_IL_OFFSET) |
9726 | { |
9727 | printf(" ???" ); |
9728 | } |
9729 | else |
9730 | { |
9731 | printf("0x%03X" , jitGetILoffs(tree->gtStmt.gtStmtILoffsx)); |
9732 | } |
9733 | printf("..." ); |
9734 | if (endIL == BAD_IL_OFFSET) |
9735 | { |
9736 | printf(" ???" ); |
9737 | } |
9738 | else |
9739 | { |
9740 | printf("0x%03X" , endIL); |
9741 | } |
9742 | printf(")" ); |
9743 | } |
9744 | } |
9745 | |
9746 | if (tree->IsArgPlaceHolderNode() && (tree->gtArgPlace.gtArgPlaceClsHnd != nullptr)) |
9747 | { |
9748 | printf(" => [clsHnd=%08X]" , dspPtr(tree->gtArgPlace.gtArgPlaceClsHnd)); |
9749 | } |
9750 | |
9751 | if (tree->gtOper == GT_RUNTIMELOOKUP) |
9752 | { |
9753 | #ifdef _TARGET_64BIT_ |
9754 | printf(" 0x%llx" , dspPtr(tree->gtRuntimeLookup.gtHnd)); |
9755 | #else |
9756 | printf(" 0x%x" , dspPtr(tree->gtRuntimeLookup.gtHnd)); |
9757 | #endif |
9758 | |
9759 | switch (tree->gtRuntimeLookup.gtHndType) |
9760 | { |
9761 | case CORINFO_HANDLETYPE_CLASS: |
9762 | printf(" class" ); |
9763 | break; |
9764 | case CORINFO_HANDLETYPE_METHOD: |
9765 | printf(" method" ); |
9766 | break; |
9767 | case CORINFO_HANDLETYPE_FIELD: |
9768 | printf(" field" ); |
9769 | break; |
9770 | default: |
9771 | printf(" unknown" ); |
9772 | break; |
9773 | } |
9774 | } |
9775 | } |
9776 | |
9777 | // for tracking down problems in reguse prediction or liveness tracking |
9778 | |
9779 | if (verbose && 0) |
9780 | { |
9781 | printf(" RR=" ); |
9782 | dspRegMask(tree->gtRsvdRegs); |
9783 | printf("\n" ); |
9784 | } |
9785 | } |
9786 | } |
9787 | |
9788 | void Compiler::gtDispRegVal(GenTree* tree) |
9789 | { |
9790 | switch (tree->GetRegTag()) |
9791 | { |
9792 | // Don't display NOREG; the absence of this tag will imply this state |
9793 | // case GenTree::GT_REGTAG_NONE: printf(" NOREG"); break; |
9794 | |
9795 | case GenTree::GT_REGTAG_REG: |
9796 | printf(" REG %s" , compRegVarName(tree->gtRegNum)); |
9797 | break; |
9798 | |
9799 | default: |
9800 | break; |
9801 | } |
9802 | |
9803 | if (tree->IsMultiRegCall()) |
9804 | { |
9805 | // 0th reg is gtRegNum, which is already printed above. |
9806 | // Print the remaining regs of a multi-reg call node. |
9807 | GenTreeCall* call = tree->AsCall(); |
9808 | unsigned regCount = call->GetReturnTypeDesc()->TryGetReturnRegCount(); |
9809 | for (unsigned i = 1; i < regCount; ++i) |
9810 | { |
9811 | printf(",%s" , compRegVarName(call->GetRegNumByIdx(i))); |
9812 | } |
9813 | } |
9814 | else if (tree->IsCopyOrReloadOfMultiRegCall()) |
9815 | { |
9816 | GenTreeCopyOrReload* copyOrReload = tree->AsCopyOrReload(); |
9817 | GenTreeCall* call = tree->gtGetOp1()->AsCall(); |
9818 | unsigned regCount = call->GetReturnTypeDesc()->TryGetReturnRegCount(); |
9819 | for (unsigned i = 1; i < regCount; ++i) |
9820 | { |
9821 | printf(",%s" , compRegVarName(copyOrReload->GetRegNumByIdx(i))); |
9822 | } |
9823 | } |
9824 | |
9825 | #if FEATURE_MULTIREG_RET |
9826 | if (tree->IsCopyOrReload()) |
9827 | { |
9828 | for (int i = 1; i < MAX_RET_REG_COUNT; i++) |
9829 | { |
9830 | regNumber reg = (regNumber)tree->AsCopyOrReload()->GetRegNumByIdx(i); |
9831 | if (reg == REG_NA) |
9832 | { |
9833 | break; |
9834 | } |
9835 | printf(",%s" , compRegVarName(reg)); |
9836 | } |
9837 | } |
9838 | #endif |
9839 | |
9840 | #if defined(_TARGET_ARM_) |
9841 | if (tree->OperIsMultiRegOp() && (tree->AsMultiRegOp()->gtOtherReg != REG_NA)) |
9842 | { |
9843 | printf(",%s" , compRegVarName(tree->AsMultiRegOp()->gtOtherReg)); |
9844 | } |
9845 | #endif |
9846 | } |
9847 | |
9848 | // We usually/commonly don't expect to print anything longer than this string, |
9849 | #define LONGEST_COMMON_LCL_VAR_DISPLAY "V99 PInvokeFrame" |
9850 | #define LONGEST_COMMON_LCL_VAR_DISPLAY_LENGTH (sizeof(LONGEST_COMMON_LCL_VAR_DISPLAY)) |
9851 | #define BUF_SIZE (LONGEST_COMMON_LCL_VAR_DISPLAY_LENGTH * 2) |
9852 | |
9853 | void Compiler::gtGetLclVarNameInfo(unsigned lclNum, const char** ilKindOut, const char** ilNameOut, unsigned* ilNumOut) |
9854 | { |
9855 | const char* ilKind = nullptr; |
9856 | const char* ilName = nullptr; |
9857 | |
9858 | unsigned ilNum = compMap2ILvarNum(lclNum); |
9859 | |
9860 | if (ilNum == (unsigned)ICorDebugInfo::RETBUF_ILNUM) |
9861 | { |
9862 | ilName = "RetBuf" ; |
9863 | } |
9864 | else if (ilNum == (unsigned)ICorDebugInfo::VARARGS_HND_ILNUM) |
9865 | { |
9866 | ilName = "VarArgHandle" ; |
9867 | } |
9868 | else if (ilNum == (unsigned)ICorDebugInfo::TYPECTXT_ILNUM) |
9869 | { |
9870 | ilName = "TypeCtx" ; |
9871 | } |
9872 | else if (ilNum == (unsigned)ICorDebugInfo::UNKNOWN_ILNUM) |
9873 | { |
9874 | #if FEATURE_ANYCSE |
9875 | if (lclNumIsTrueCSE(lclNum)) |
9876 | { |
9877 | ilKind = "cse" ; |
9878 | ilNum = lclNum - optCSEstart; |
9879 | } |
9880 | else if (lclNum >= optCSEstart) |
9881 | { |
9882 | // Currently any new LclVar's introduced after the CSE phase |
9883 | // are believed to be created by the "rationalizer" that is what is meant by the "rat" prefix. |
9884 | ilKind = "rat" ; |
9885 | ilNum = lclNum - (optCSEstart + optCSEcount); |
9886 | } |
9887 | else |
9888 | #endif // FEATURE_ANYCSE |
9889 | { |
9890 | if (lclNum == info.compLvFrameListRoot) |
9891 | { |
9892 | ilName = "FramesRoot" ; |
9893 | } |
9894 | else if (lclNum == lvaInlinedPInvokeFrameVar) |
9895 | { |
9896 | ilName = "PInvokeFrame" ; |
9897 | } |
9898 | else if (lclNum == lvaGSSecurityCookie) |
9899 | { |
9900 | ilName = "GsCookie" ; |
9901 | } |
9902 | #if FEATURE_FIXED_OUT_ARGS |
9903 | else if (lclNum == lvaPInvokeFrameRegSaveVar) |
9904 | { |
9905 | ilName = "PInvokeFrameRegSave" ; |
9906 | } |
9907 | else if (lclNum == lvaOutgoingArgSpaceVar) |
9908 | { |
9909 | ilName = "OutArgs" ; |
9910 | } |
9911 | #endif // FEATURE_FIXED_OUT_ARGS |
9912 | #ifdef _TARGET_ARM_ |
9913 | else if (lclNum == lvaPromotedStructAssemblyScratchVar) |
9914 | { |
9915 | ilName = "PromotedStructScratch" ; |
9916 | } |
9917 | #endif // _TARGET_ARM_ |
9918 | #if !FEATURE_EH_FUNCLETS |
9919 | else if (lclNum == lvaShadowSPslotsVar) |
9920 | { |
9921 | ilName = "EHSlots" ; |
9922 | } |
9923 | #endif // !FEATURE_EH_FUNCLETS |
9924 | #ifdef JIT32_GCENCODER |
9925 | else if (lclNum == lvaLocAllocSPvar) |
9926 | { |
9927 | ilName = "LocAllocSP" ; |
9928 | } |
9929 | #endif // JIT32_GCENCODER |
9930 | #if FEATURE_EH_FUNCLETS |
9931 | else if (lclNum == lvaPSPSym) |
9932 | { |
9933 | ilName = "PSPSym" ; |
9934 | } |
9935 | #endif // FEATURE_EH_FUNCLETS |
9936 | else |
9937 | { |
9938 | ilKind = "tmp" ; |
9939 | if (compIsForInlining()) |
9940 | { |
9941 | ilNum = lclNum - impInlineInfo->InlinerCompiler->info.compLocalsCount; |
9942 | } |
9943 | else |
9944 | { |
9945 | ilNum = lclNum - info.compLocalsCount; |
9946 | } |
9947 | } |
9948 | } |
9949 | } |
9950 | else if (lclNum < (compIsForInlining() ? impInlineInfo->InlinerCompiler->info.compArgsCount : info.compArgsCount)) |
9951 | { |
9952 | if (ilNum == 0 && !info.compIsStatic) |
9953 | { |
9954 | ilName = "this" ; |
9955 | } |
9956 | else |
9957 | { |
9958 | ilKind = "arg" ; |
9959 | } |
9960 | } |
9961 | else |
9962 | { |
9963 | if (!lvaTable[lclNum].lvIsStructField) |
9964 | { |
9965 | ilKind = "loc" ; |
9966 | } |
9967 | if (compIsForInlining()) |
9968 | { |
9969 | ilNum -= impInlineInfo->InlinerCompiler->info.compILargsCount; |
9970 | } |
9971 | else |
9972 | { |
9973 | ilNum -= info.compILargsCount; |
9974 | } |
9975 | } |
9976 | |
9977 | *ilKindOut = ilKind; |
9978 | *ilNameOut = ilName; |
9979 | *ilNumOut = ilNum; |
9980 | } |
9981 | |
9982 | /*****************************************************************************/ |
9983 | int Compiler::gtGetLclVarName(unsigned lclNum, char* buf, unsigned buf_remaining) |
9984 | { |
9985 | char* bufp_next = buf; |
9986 | unsigned charsPrinted = 0; |
9987 | int sprintf_result; |
9988 | |
9989 | sprintf_result = sprintf_s(bufp_next, buf_remaining, "V%02u" , lclNum); |
9990 | |
9991 | if (sprintf_result < 0) |
9992 | { |
9993 | return sprintf_result; |
9994 | } |
9995 | |
9996 | charsPrinted += sprintf_result; |
9997 | bufp_next += sprintf_result; |
9998 | buf_remaining -= sprintf_result; |
9999 | |
10000 | const char* ilKind = nullptr; |
10001 | const char* ilName = nullptr; |
10002 | unsigned ilNum = 0; |
10003 | |
10004 | gtGetLclVarNameInfo(lclNum, &ilKind, &ilName, &ilNum); |
10005 | |
10006 | if (ilName != nullptr) |
10007 | { |
10008 | sprintf_result = sprintf_s(bufp_next, buf_remaining, " %s" , ilName); |
10009 | if (sprintf_result < 0) |
10010 | { |
10011 | return sprintf_result; |
10012 | } |
10013 | charsPrinted += sprintf_result; |
10014 | bufp_next += sprintf_result; |
10015 | buf_remaining -= sprintf_result; |
10016 | } |
10017 | else if (ilKind != nullptr) |
10018 | { |
10019 | sprintf_result = sprintf_s(bufp_next, buf_remaining, " %s%d" , ilKind, ilNum); |
10020 | if (sprintf_result < 0) |
10021 | { |
10022 | return sprintf_result; |
10023 | } |
10024 | charsPrinted += sprintf_result; |
10025 | bufp_next += sprintf_result; |
10026 | buf_remaining -= sprintf_result; |
10027 | } |
10028 | |
10029 | assert(charsPrinted > 0); |
10030 | assert(buf_remaining > 0); |
10031 | |
10032 | return (int)charsPrinted; |
10033 | } |
10034 | |
10035 | /***************************************************************************** |
10036 | * Get the local var name, and create a copy of the string that can be used in debug output. |
10037 | */ |
10038 | char* Compiler::gtGetLclVarName(unsigned lclNum) |
10039 | { |
10040 | char buf[BUF_SIZE]; |
10041 | int charsPrinted = gtGetLclVarName(lclNum, buf, _countof(buf)); |
10042 | if (charsPrinted < 0) |
10043 | { |
10044 | return nullptr; |
10045 | } |
10046 | |
10047 | char* retBuf = new (this, CMK_DebugOnly) char[charsPrinted + 1]; |
10048 | strcpy_s(retBuf, charsPrinted + 1, buf); |
10049 | return retBuf; |
10050 | } |
10051 | |
10052 | /*****************************************************************************/ |
10053 | void Compiler::gtDispLclVar(unsigned lclNum, bool padForBiggestDisp) |
10054 | { |
10055 | char buf[BUF_SIZE]; |
10056 | int charsPrinted = gtGetLclVarName(lclNum, buf, _countof(buf)); |
10057 | |
10058 | if (charsPrinted < 0) |
10059 | { |
10060 | return; |
10061 | } |
10062 | |
10063 | printf("%s" , buf); |
10064 | |
10065 | if (padForBiggestDisp && (charsPrinted < LONGEST_COMMON_LCL_VAR_DISPLAY_LENGTH)) |
10066 | { |
10067 | printf("%*c" , LONGEST_COMMON_LCL_VAR_DISPLAY_LENGTH - charsPrinted, ' '); |
10068 | } |
10069 | } |
10070 | |
10071 | /*****************************************************************************/ |
10072 | void Compiler::gtDispConst(GenTree* tree) |
10073 | { |
10074 | assert(tree->OperKind() & GTK_CONST); |
10075 | |
10076 | switch (tree->gtOper) |
10077 | { |
10078 | case GT_CNS_INT: |
10079 | if (tree->IsIconHandle(GTF_ICON_STR_HDL)) |
10080 | { |
10081 | const wchar_t* str = eeGetCPString(tree->gtIntCon.gtIconVal); |
10082 | if (str != nullptr) |
10083 | { |
10084 | printf(" 0x%X \"%S\"" , dspPtr(tree->gtIntCon.gtIconVal), str); |
10085 | } |
10086 | else |
10087 | { |
10088 | // Note that eGetCPString isn't currently implemented on Linux/ARM |
10089 | // and instead always returns nullptr |
10090 | printf(" 0x%X [ICON_STR_HDL]" , dspPtr(tree->gtIntCon.gtIconVal)); |
10091 | } |
10092 | } |
10093 | else |
10094 | { |
10095 | ssize_t dspIconVal = tree->IsIconHandle() ? dspPtr(tree->gtIntCon.gtIconVal) : tree->gtIntCon.gtIconVal; |
10096 | |
10097 | if (tree->TypeGet() == TYP_REF) |
10098 | { |
10099 | assert(tree->gtIntCon.gtIconVal == 0); |
10100 | printf(" null" ); |
10101 | } |
10102 | else if ((tree->gtIntCon.gtIconVal > -1000) && (tree->gtIntCon.gtIconVal < 1000)) |
10103 | { |
10104 | printf(" %ld" , dspIconVal); |
10105 | #ifdef _TARGET_64BIT_ |
10106 | } |
10107 | else if ((tree->gtIntCon.gtIconVal & 0xFFFFFFFF00000000LL) != 0) |
10108 | { |
10109 | printf(" 0x%llx" , dspIconVal); |
10110 | #endif |
10111 | } |
10112 | else |
10113 | { |
10114 | printf(" 0x%X" , dspIconVal); |
10115 | } |
10116 | |
10117 | if (tree->IsIconHandle()) |
10118 | { |
10119 | switch (tree->GetIconHandleFlag()) |
10120 | { |
10121 | case GTF_ICON_SCOPE_HDL: |
10122 | printf(" scope" ); |
10123 | break; |
10124 | case GTF_ICON_CLASS_HDL: |
10125 | printf(" class" ); |
10126 | break; |
10127 | case GTF_ICON_METHOD_HDL: |
10128 | printf(" method" ); |
10129 | break; |
10130 | case GTF_ICON_FIELD_HDL: |
10131 | printf(" field" ); |
10132 | break; |
10133 | case GTF_ICON_STATIC_HDL: |
10134 | printf(" static" ); |
10135 | break; |
10136 | case GTF_ICON_STR_HDL: |
10137 | unreached(); // This case is handled above |
10138 | break; |
10139 | case GTF_ICON_PSTR_HDL: |
10140 | printf(" pstr" ); |
10141 | break; |
10142 | case GTF_ICON_PTR_HDL: |
10143 | printf(" ptr" ); |
10144 | break; |
10145 | case GTF_ICON_VARG_HDL: |
10146 | printf(" vararg" ); |
10147 | break; |
10148 | case GTF_ICON_PINVKI_HDL: |
10149 | printf(" pinvoke" ); |
10150 | break; |
10151 | case GTF_ICON_TOKEN_HDL: |
10152 | printf(" token" ); |
10153 | break; |
10154 | case GTF_ICON_TLS_HDL: |
10155 | printf(" tls" ); |
10156 | break; |
10157 | case GTF_ICON_FTN_ADDR: |
10158 | printf(" ftn" ); |
10159 | break; |
10160 | case GTF_ICON_CIDMID_HDL: |
10161 | printf(" cid/mid" ); |
10162 | break; |
10163 | case GTF_ICON_BBC_PTR: |
10164 | printf(" bbc" ); |
10165 | break; |
10166 | default: |
10167 | printf(" UNKNOWN" ); |
10168 | break; |
10169 | } |
10170 | } |
10171 | |
10172 | if ((tree->gtFlags & GTF_ICON_FIELD_OFF) != 0) |
10173 | { |
10174 | printf(" field offset" ); |
10175 | } |
10176 | |
10177 | #ifdef FEATURE_SIMD |
10178 | if ((tree->gtFlags & GTF_ICON_SIMD_COUNT) != 0) |
10179 | { |
10180 | printf(" Vector<T>.Count" ); |
10181 | } |
10182 | #endif |
10183 | |
10184 | if ((tree->IsReuseRegVal()) != 0) |
10185 | { |
10186 | printf(" reuse reg val" ); |
10187 | } |
10188 | } |
10189 | |
10190 | gtDispFieldSeq(tree->gtIntCon.gtFieldSeq); |
10191 | |
10192 | break; |
10193 | |
10194 | case GT_CNS_LNG: |
10195 | printf(" 0x%016I64x" , tree->gtLngCon.gtLconVal); |
10196 | break; |
10197 | |
10198 | case GT_CNS_DBL: |
10199 | if (*((__int64*)&tree->gtDblCon.gtDconVal) == (__int64)I64(0x8000000000000000)) |
10200 | { |
10201 | printf(" -0.00000" ); |
10202 | } |
10203 | else |
10204 | { |
10205 | printf(" %#.17g" , tree->gtDblCon.gtDconVal); |
10206 | } |
10207 | break; |
10208 | case GT_CNS_STR: |
10209 | printf("<string constant>" ); |
10210 | break; |
10211 | default: |
10212 | assert(!"unexpected constant node" ); |
10213 | } |
10214 | |
10215 | gtDispRegVal(tree); |
10216 | } |
10217 | |
10218 | void Compiler::gtDispFieldSeq(FieldSeqNode* pfsn) |
10219 | { |
10220 | if (pfsn == FieldSeqStore::NotAField() || (pfsn == nullptr)) |
10221 | { |
10222 | return; |
10223 | } |
10224 | |
10225 | // Otherwise... |
10226 | printf(" Fseq[" ); |
10227 | while (pfsn != nullptr) |
10228 | { |
10229 | assert(pfsn != FieldSeqStore::NotAField()); // Can't exist in a field sequence list except alone |
10230 | CORINFO_FIELD_HANDLE fldHnd = pfsn->m_fieldHnd; |
10231 | // First check the "pseudo" field handles... |
10232 | if (fldHnd == FieldSeqStore::FirstElemPseudoField) |
10233 | { |
10234 | printf("#FirstElem" ); |
10235 | } |
10236 | else if (fldHnd == FieldSeqStore::ConstantIndexPseudoField) |
10237 | { |
10238 | printf("#ConstantIndex" ); |
10239 | } |
10240 | else |
10241 | { |
10242 | printf("%s" , eeGetFieldName(fldHnd)); |
10243 | } |
10244 | pfsn = pfsn->m_next; |
10245 | if (pfsn != nullptr) |
10246 | { |
10247 | printf(", " ); |
10248 | } |
10249 | } |
10250 | printf("]" ); |
10251 | } |
10252 | |
10253 | //------------------------------------------------------------------------ |
10254 | // gtDispLeaf: Print a single leaf node to jitstdout. |
10255 | // |
10256 | // Arguments: |
10257 | // tree - the tree to be printed |
10258 | // indentStack - the specification for the current level of indentation & arcs |
10259 | // |
10260 | // Return Value: |
10261 | // None. |
10262 | // |
10263 | // Notes: |
10264 | // 'indentStack' may be null, in which case no indentation or arcs are printed |
10265 | |
10266 | void Compiler::gtDispLeaf(GenTree* tree, IndentStack* indentStack) |
10267 | { |
10268 | if (tree->OperKind() & GTK_CONST) |
10269 | { |
10270 | gtDispConst(tree); |
10271 | return; |
10272 | } |
10273 | |
10274 | bool isLclFld = false; |
10275 | |
10276 | switch (tree->gtOper) |
10277 | { |
10278 | unsigned varNum; |
10279 | LclVarDsc* varDsc; |
10280 | |
10281 | case GT_LCL_FLD: |
10282 | case GT_LCL_FLD_ADDR: |
10283 | case GT_STORE_LCL_FLD: |
10284 | isLclFld = true; |
10285 | __fallthrough; |
10286 | |
10287 | case GT_PHI_ARG: |
10288 | case GT_LCL_VAR: |
10289 | case GT_LCL_VAR_ADDR: |
10290 | case GT_STORE_LCL_VAR: |
10291 | printf(" " ); |
10292 | varNum = tree->gtLclVarCommon.gtLclNum; |
10293 | varDsc = &lvaTable[varNum]; |
10294 | gtDispLclVar(varNum); |
10295 | if (tree->gtLclVarCommon.HasSsaName()) |
10296 | { |
10297 | if (tree->gtFlags & GTF_VAR_USEASG) |
10298 | { |
10299 | assert(tree->gtFlags & GTF_VAR_DEF); |
10300 | printf("ud:%d->%d" , tree->gtLclVarCommon.gtSsaNum, GetSsaNumForLocalVarDef(tree)); |
10301 | } |
10302 | else |
10303 | { |
10304 | printf("%s:%d" , (tree->gtFlags & GTF_VAR_DEF) ? "d" : "u" , tree->gtLclVarCommon.gtSsaNum); |
10305 | } |
10306 | } |
10307 | |
10308 | if (isLclFld) |
10309 | { |
10310 | printf("[+%u]" , tree->gtLclFld.gtLclOffs); |
10311 | gtDispFieldSeq(tree->gtLclFld.gtFieldSeq); |
10312 | } |
10313 | |
10314 | if (varDsc->lvRegister) |
10315 | { |
10316 | printf(" " ); |
10317 | varDsc->PrintVarReg(); |
10318 | } |
10319 | else if (tree->InReg()) |
10320 | { |
10321 | printf(" %s" , compRegVarName(tree->gtRegNum)); |
10322 | } |
10323 | |
10324 | if (varDsc->lvPromoted) |
10325 | { |
10326 | if (!varTypeIsPromotable(varDsc) && !varDsc->lvUnusedStruct) |
10327 | { |
10328 | // Promoted implicit byrefs can get in this state while they are being rewritten |
10329 | // in global morph. |
10330 | assert(fgGlobalMorph); |
10331 | } |
10332 | else |
10333 | { |
10334 | CORINFO_CLASS_HANDLE typeHnd = varDsc->lvVerTypeInfo.GetClassHandle(); |
10335 | CORINFO_FIELD_HANDLE fldHnd; |
10336 | |
10337 | for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i) |
10338 | { |
10339 | LclVarDsc* fieldVarDsc = &lvaTable[i]; |
10340 | const char* fieldName; |
10341 | #if !defined(_TARGET_64BIT_) |
10342 | if (varTypeIsLong(varDsc)) |
10343 | { |
10344 | fieldName = (i == 0) ? "lo" : "hi" ; |
10345 | } |
10346 | else |
10347 | #endif // !defined(_TARGET_64BIT_) |
10348 | { |
10349 | fldHnd = info.compCompHnd->getFieldInClass(typeHnd, fieldVarDsc->lvFldOrdinal); |
10350 | fieldName = eeGetFieldName(fldHnd); |
10351 | } |
10352 | |
10353 | printf("\n" ); |
10354 | printf(" " ); |
10355 | printIndent(indentStack); |
10356 | printf(" %-6s V%02u.%s (offs=0x%02x) -> " , varTypeName(fieldVarDsc->TypeGet()), |
10357 | tree->gtLclVarCommon.gtLclNum, fieldName, fieldVarDsc->lvFldOffset); |
10358 | gtDispLclVar(i); |
10359 | |
10360 | if (fieldVarDsc->lvRegister) |
10361 | { |
10362 | printf(" " ); |
10363 | fieldVarDsc->PrintVarReg(); |
10364 | } |
10365 | |
10366 | if (fieldVarDsc->lvTracked && fgLocalVarLivenessDone && // Includes local variable liveness |
10367 | ((tree->gtFlags & GTF_VAR_DEATH) != 0)) |
10368 | { |
10369 | printf(" (last use)" ); |
10370 | } |
10371 | } |
10372 | } |
10373 | } |
10374 | else // a normal not-promoted lclvar |
10375 | { |
10376 | if (varDsc->lvTracked && fgLocalVarLivenessDone && ((tree->gtFlags & GTF_VAR_DEATH) != 0)) |
10377 | { |
10378 | printf(" (last use)" ); |
10379 | } |
10380 | } |
10381 | break; |
10382 | |
10383 | case GT_JMP: |
10384 | { |
10385 | const char* methodName; |
10386 | const char* className; |
10387 | |
10388 | methodName = eeGetMethodName((CORINFO_METHOD_HANDLE)tree->gtVal.gtVal1, &className); |
10389 | printf(" %s.%s\n" , className, methodName); |
10390 | } |
10391 | break; |
10392 | |
10393 | case GT_CLS_VAR: |
10394 | printf(" Hnd=%#x" , dspPtr(tree->gtClsVar.gtClsVarHnd)); |
10395 | gtDispFieldSeq(tree->gtClsVar.gtFieldSeq); |
10396 | break; |
10397 | |
10398 | case GT_CLS_VAR_ADDR: |
10399 | printf(" Hnd=%#x" , dspPtr(tree->gtClsVar.gtClsVarHnd)); |
10400 | break; |
10401 | |
10402 | case GT_LABEL: |
10403 | if (tree->gtLabel.gtLabBB) |
10404 | { |
10405 | printf(" dst=" FMT_BB, tree->gtLabel.gtLabBB->bbNum); |
10406 | } |
10407 | else |
10408 | { |
10409 | printf(" dst=<null>" ); |
10410 | } |
10411 | |
10412 | break; |
10413 | |
10414 | case GT_FTN_ADDR: |
10415 | { |
10416 | const char* methodName; |
10417 | const char* className; |
10418 | |
10419 | methodName = eeGetMethodName((CORINFO_METHOD_HANDLE)tree->gtFptrVal.gtFptrMethod, &className); |
10420 | printf(" %s.%s\n" , className, methodName); |
10421 | } |
10422 | break; |
10423 | |
10424 | #if !FEATURE_EH_FUNCLETS |
10425 | case GT_END_LFIN: |
10426 | printf(" endNstLvl=%d" , tree->gtVal.gtVal1); |
10427 | break; |
10428 | #endif // !FEATURE_EH_FUNCLETS |
10429 | |
10430 | // Vanilla leaves. No qualifying information available. So do nothing |
10431 | |
10432 | case GT_NO_OP: |
10433 | case GT_START_NONGC: |
10434 | case GT_PROF_HOOK: |
10435 | case GT_CATCH_ARG: |
10436 | case GT_MEMORYBARRIER: |
10437 | case GT_ARGPLACE: |
10438 | case GT_PINVOKE_PROLOG: |
10439 | case GT_JMPTABLE: |
10440 | break; |
10441 | |
10442 | case GT_RET_EXPR: |
10443 | printf("(inl return from call " ); |
10444 | printTreeID(tree->gtRetExpr.gtInlineCandidate); |
10445 | printf(")" ); |
10446 | break; |
10447 | |
10448 | case GT_PHYSREG: |
10449 | printf(" %s" , getRegName(tree->gtPhysReg.gtSrcReg, varTypeIsFloating(tree))); |
10450 | break; |
10451 | |
10452 | case GT_IL_OFFSET: |
10453 | printf(" IL offset: " ); |
10454 | if (tree->gtStmt.gtStmtILoffsx == BAD_IL_OFFSET) |
10455 | { |
10456 | printf("???" ); |
10457 | } |
10458 | else |
10459 | { |
10460 | printf("0x%x" , jitGetILoffs(tree->gtStmt.gtStmtILoffsx)); |
10461 | } |
10462 | break; |
10463 | |
10464 | case GT_JCC: |
10465 | case GT_SETCC: |
10466 | printf(" cond=%s" , GenTree::OpName(tree->AsCC()->gtCondition)); |
10467 | break; |
10468 | case GT_JCMP: |
10469 | printf(" cond=%s%s" , (tree->gtFlags & GTF_JCMP_TST) ? "TEST_" : "" , |
10470 | (tree->gtFlags & GTF_JCMP_EQ) ? "EQ" : "NE" ); |
10471 | |
10472 | default: |
10473 | assert(!"don't know how to display tree leaf node" ); |
10474 | } |
10475 | |
10476 | gtDispRegVal(tree); |
10477 | } |
10478 | |
10479 | //------------------------------------------------------------------------ |
10480 | // gtDispLeaf: Print a child node to jitstdout. |
10481 | // |
10482 | // Arguments: |
10483 | // tree - the tree to be printed |
10484 | // indentStack - the specification for the current level of indentation & arcs |
10485 | // arcType - the type of arc to use for this child |
10486 | // msg - a contextual method (i.e. from the parent) to print |
10487 | // topOnly - a boolean indicating whether to print the children, or just the top node |
10488 | // |
10489 | // Return Value: |
10490 | // None. |
10491 | // |
10492 | // Notes: |
10493 | // 'indentStack' may be null, in which case no indentation or arcs are printed |
10494 | // 'msg' has a default value of null |
10495 | // 'topOnly' is an optional argument that defaults to false |
10496 | |
10497 | void Compiler::gtDispChild(GenTree* child, |
10498 | IndentStack* indentStack, |
10499 | IndentInfo arcType, |
10500 | __in_opt const char* msg, /* = nullptr */ |
10501 | bool topOnly) /* = false */ |
10502 | { |
10503 | indentStack->Push(arcType); |
10504 | gtDispTree(child, indentStack, msg, topOnly); |
10505 | indentStack->Pop(); |
10506 | } |
10507 | |
10508 | #ifdef FEATURE_SIMD |
10509 | // Intrinsic Id to name map |
10510 | extern const char* const simdIntrinsicNames[] = { |
10511 | #define SIMD_INTRINSIC(mname, inst, id, name, r, ac, arg1, arg2, arg3, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) name, |
10512 | #include "simdintrinsiclist.h" |
10513 | }; |
10514 | #endif // FEATURE_SIMD |
10515 | |
10516 | /*****************************************************************************/ |
10517 | |
10518 | void Compiler::gtDispTree(GenTree* tree, |
10519 | IndentStack* indentStack, /* = nullptr */ |
10520 | __in __in_z __in_opt const char* msg, /* = nullptr */ |
10521 | bool topOnly, /* = false */ |
10522 | bool isLIR) /* = false */ |
10523 | { |
10524 | if (tree == nullptr) |
10525 | { |
10526 | printf(" [%08X] <NULL>\n" , tree); |
10527 | printf("" ); // null string means flush |
10528 | return; |
10529 | } |
10530 | |
10531 | if (indentStack == nullptr) |
10532 | { |
10533 | indentStack = new (this, CMK_DebugOnly) IndentStack(this); |
10534 | } |
10535 | |
10536 | if (IsUninitialized(tree)) |
10537 | { |
10538 | /* Value used to initalize nodes */ |
10539 | printf("Uninitialized tree node!" ); |
10540 | return; |
10541 | } |
10542 | |
10543 | if (tree->gtOper >= GT_COUNT) |
10544 | { |
10545 | gtDispNode(tree, indentStack, msg, isLIR); |
10546 | printf("Bogus operator!" ); |
10547 | return; |
10548 | } |
10549 | |
10550 | /* Is tree a leaf node? */ |
10551 | |
10552 | if (tree->OperIsLeaf() || tree->OperIsLocalStore()) // local stores used to be leaves |
10553 | { |
10554 | gtDispNode(tree, indentStack, msg, isLIR); |
10555 | gtDispLeaf(tree, indentStack); |
10556 | gtDispVN(tree); |
10557 | printf("\n" ); |
10558 | if (tree->OperIsLocalStore() && !topOnly) |
10559 | { |
10560 | gtDispChild(tree->gtOp.gtOp1, indentStack, IINone); |
10561 | } |
10562 | return; |
10563 | } |
10564 | |
10565 | // Determine what kind of arc to propagate. |
10566 | IndentInfo myArc = IINone; |
10567 | IndentInfo lowerArc = IINone; |
10568 | if (indentStack->Depth() > 0) |
10569 | { |
10570 | myArc = indentStack->Pop(); |
10571 | switch (myArc) |
10572 | { |
10573 | case IIArcBottom: |
10574 | indentStack->Push(IIArc); |
10575 | lowerArc = IINone; |
10576 | break; |
10577 | case IIArc: |
10578 | indentStack->Push(IIArc); |
10579 | lowerArc = IIArc; |
10580 | break; |
10581 | case IIArcTop: |
10582 | indentStack->Push(IINone); |
10583 | lowerArc = IIArc; |
10584 | break; |
10585 | case IIEmbedded: |
10586 | indentStack->Push(IIEmbedded); |
10587 | lowerArc = IIEmbedded; |
10588 | break; |
10589 | case IINone: |
10590 | indentStack->Push(IINone); |
10591 | lowerArc = IINone; |
10592 | break; |
10593 | default: |
10594 | unreached(); |
10595 | break; |
10596 | } |
10597 | } |
10598 | |
10599 | // Special case formatting for PHI nodes -- arg lists like calls. |
10600 | |
10601 | if (tree->OperGet() == GT_PHI) |
10602 | { |
10603 | gtDispNode(tree, indentStack, msg, isLIR); |
10604 | gtDispVN(tree); |
10605 | printf("\n" ); |
10606 | |
10607 | if (!topOnly) |
10608 | { |
10609 | if (tree->gtOp.gtOp1 != nullptr) |
10610 | { |
10611 | IndentInfo arcType = IIArcTop; |
10612 | for (GenTreeArgList* args = tree->gtOp.gtOp1->AsArgList(); args != nullptr; args = args->Rest()) |
10613 | { |
10614 | if (args->Rest() == nullptr) |
10615 | { |
10616 | arcType = IIArcBottom; |
10617 | } |
10618 | gtDispChild(args->Current(), indentStack, arcType); |
10619 | arcType = IIArc; |
10620 | } |
10621 | } |
10622 | } |
10623 | return; |
10624 | } |
10625 | |
10626 | /* Is it a 'simple' unary/binary operator? */ |
10627 | |
10628 | const char* childMsg = nullptr; |
10629 | |
10630 | if (tree->OperIsSimple()) |
10631 | { |
10632 | if (!topOnly) |
10633 | { |
10634 | if (tree->gtGetOp2IfPresent()) |
10635 | { |
10636 | // Label the childMsgs of the GT_COLON operator |
10637 | // op2 is the then part |
10638 | |
10639 | if (tree->gtOper == GT_COLON) |
10640 | { |
10641 | childMsg = "then" ; |
10642 | } |
10643 | gtDispChild(tree->gtOp.gtOp2, indentStack, IIArcTop, childMsg, topOnly); |
10644 | } |
10645 | } |
10646 | |
10647 | // Now, get the right type of arc for this node |
10648 | if (myArc != IINone) |
10649 | { |
10650 | indentStack->Pop(); |
10651 | indentStack->Push(myArc); |
10652 | } |
10653 | |
10654 | gtDispNode(tree, indentStack, msg, isLIR); |
10655 | |
10656 | // Propagate lowerArc to the lower children. |
10657 | if (indentStack->Depth() > 0) |
10658 | { |
10659 | (void)indentStack->Pop(); |
10660 | indentStack->Push(lowerArc); |
10661 | } |
10662 | |
10663 | if (tree->gtOper == GT_CAST) |
10664 | { |
10665 | /* Format a message that explains the effect of this GT_CAST */ |
10666 | |
10667 | var_types fromType = genActualType(tree->gtCast.CastOp()->TypeGet()); |
10668 | var_types toType = tree->CastToType(); |
10669 | var_types finalType = tree->TypeGet(); |
10670 | |
10671 | /* if GTF_UNSIGNED is set then force fromType to an unsigned type */ |
10672 | if (tree->gtFlags & GTF_UNSIGNED) |
10673 | { |
10674 | fromType = genUnsignedType(fromType); |
10675 | } |
10676 | |
10677 | if (finalType != toType) |
10678 | { |
10679 | printf(" %s <-" , varTypeName(finalType)); |
10680 | } |
10681 | |
10682 | printf(" %s <- %s" , varTypeName(toType), varTypeName(fromType)); |
10683 | } |
10684 | |
10685 | if (tree->gtOper == GT_OBJ && (tree->gtFlags & GTF_VAR_DEATH)) |
10686 | { |
10687 | printf(" (last use)" ); |
10688 | } |
10689 | if (tree->OperIsBlkOp()) |
10690 | { |
10691 | if (tree->OperIsCopyBlkOp()) |
10692 | { |
10693 | printf(" (copy)" ); |
10694 | } |
10695 | else if (tree->OperIsInitBlkOp()) |
10696 | { |
10697 | printf(" (init)" ); |
10698 | } |
10699 | if (tree->OperIsStoreBlk() && (tree->AsBlk()->gtBlkOpKind != GenTreeBlk::BlkOpKindInvalid)) |
10700 | { |
10701 | switch (tree->AsBlk()->gtBlkOpKind) |
10702 | { |
10703 | case GenTreeBlk::BlkOpKindRepInstr: |
10704 | printf(" (RepInstr)" ); |
10705 | break; |
10706 | case GenTreeBlk::BlkOpKindUnroll: |
10707 | printf(" (Unroll)" ); |
10708 | break; |
10709 | case GenTreeBlk::BlkOpKindHelper: |
10710 | printf(" (Helper)" ); |
10711 | break; |
10712 | default: |
10713 | unreached(); |
10714 | } |
10715 | } |
10716 | } |
10717 | else if (tree->OperIsFieldList()) |
10718 | { |
10719 | printf(" %s at offset %d" , varTypeName(tree->AsFieldList()->gtFieldType), |
10720 | tree->AsFieldList()->gtFieldOffset); |
10721 | } |
10722 | #if FEATURE_PUT_STRUCT_ARG_STK |
10723 | else if (tree->OperGet() == GT_PUTARG_STK) |
10724 | { |
10725 | printf(" (%d slots)" , tree->AsPutArgStk()->gtNumSlots); |
10726 | if (tree->AsPutArgStk()->gtPutArgStkKind != GenTreePutArgStk::Kind::Invalid) |
10727 | { |
10728 | switch (tree->AsPutArgStk()->gtPutArgStkKind) |
10729 | { |
10730 | case GenTreePutArgStk::Kind::RepInstr: |
10731 | printf(" (RepInstr)" ); |
10732 | break; |
10733 | case GenTreePutArgStk::Kind::Unroll: |
10734 | printf(" (Unroll)" ); |
10735 | break; |
10736 | case GenTreePutArgStk::Kind::Push: |
10737 | printf(" (Push)" ); |
10738 | break; |
10739 | case GenTreePutArgStk::Kind::PushAllSlots: |
10740 | printf(" (PushAllSlots)" ); |
10741 | break; |
10742 | default: |
10743 | unreached(); |
10744 | } |
10745 | } |
10746 | } |
10747 | #endif // FEATURE_PUT_STRUCT_ARG_STK |
10748 | |
10749 | if (tree->gtOper == GT_INTRINSIC) |
10750 | { |
10751 | switch (tree->gtIntrinsic.gtIntrinsicId) |
10752 | { |
10753 | case CORINFO_INTRINSIC_Sin: |
10754 | printf(" sin" ); |
10755 | break; |
10756 | case CORINFO_INTRINSIC_Cos: |
10757 | printf(" cos" ); |
10758 | break; |
10759 | case CORINFO_INTRINSIC_Cbrt: |
10760 | printf(" cbrt" ); |
10761 | break; |
10762 | case CORINFO_INTRINSIC_Sqrt: |
10763 | printf(" sqrt" ); |
10764 | break; |
10765 | case CORINFO_INTRINSIC_Abs: |
10766 | printf(" abs" ); |
10767 | break; |
10768 | case CORINFO_INTRINSIC_Round: |
10769 | printf(" round" ); |
10770 | break; |
10771 | case CORINFO_INTRINSIC_Cosh: |
10772 | printf(" cosh" ); |
10773 | break; |
10774 | case CORINFO_INTRINSIC_Sinh: |
10775 | printf(" sinh" ); |
10776 | break; |
10777 | case CORINFO_INTRINSIC_Tan: |
10778 | printf(" tan" ); |
10779 | break; |
10780 | case CORINFO_INTRINSIC_Tanh: |
10781 | printf(" tanh" ); |
10782 | break; |
10783 | case CORINFO_INTRINSIC_Asin: |
10784 | printf(" asin" ); |
10785 | break; |
10786 | case CORINFO_INTRINSIC_Asinh: |
10787 | printf(" asinh" ); |
10788 | break; |
10789 | case CORINFO_INTRINSIC_Acos: |
10790 | printf(" acos" ); |
10791 | break; |
10792 | case CORINFO_INTRINSIC_Acosh: |
10793 | printf(" acosh" ); |
10794 | break; |
10795 | case CORINFO_INTRINSIC_Atan: |
10796 | printf(" atan" ); |
10797 | break; |
10798 | case CORINFO_INTRINSIC_Atan2: |
10799 | printf(" atan2" ); |
10800 | break; |
10801 | case CORINFO_INTRINSIC_Atanh: |
10802 | printf(" atanh" ); |
10803 | break; |
10804 | case CORINFO_INTRINSIC_Log10: |
10805 | printf(" log10" ); |
10806 | break; |
10807 | case CORINFO_INTRINSIC_Pow: |
10808 | printf(" pow" ); |
10809 | break; |
10810 | case CORINFO_INTRINSIC_Exp: |
10811 | printf(" exp" ); |
10812 | break; |
10813 | case CORINFO_INTRINSIC_Ceiling: |
10814 | printf(" ceiling" ); |
10815 | break; |
10816 | case CORINFO_INTRINSIC_Floor: |
10817 | printf(" floor" ); |
10818 | break; |
10819 | case CORINFO_INTRINSIC_Object_GetType: |
10820 | printf(" objGetType" ); |
10821 | break; |
10822 | |
10823 | default: |
10824 | unreached(); |
10825 | } |
10826 | } |
10827 | |
10828 | #ifdef FEATURE_SIMD |
10829 | if (tree->gtOper == GT_SIMD) |
10830 | { |
10831 | printf(" %s %s" , varTypeName(tree->gtSIMD.gtSIMDBaseType), |
10832 | simdIntrinsicNames[tree->gtSIMD.gtSIMDIntrinsicID]); |
10833 | } |
10834 | #endif // FEATURE_SIMD |
10835 | |
10836 | #ifdef FEATURE_HW_INTRINSICS |
10837 | if (tree->gtOper == GT_HWIntrinsic) |
10838 | { |
10839 | printf(" %s %s" , |
10840 | tree->gtHWIntrinsic.gtSIMDBaseType == TYP_UNKNOWN ? "" |
10841 | : varTypeName(tree->gtHWIntrinsic.gtSIMDBaseType), |
10842 | HWIntrinsicInfo::lookupName(tree->gtHWIntrinsic.gtHWIntrinsicId)); |
10843 | } |
10844 | #endif // FEATURE_HW_INTRINSICS |
10845 | |
10846 | gtDispRegVal(tree); |
10847 | gtDispVN(tree); |
10848 | printf("\n" ); |
10849 | |
10850 | if (!topOnly && tree->gtOp.gtOp1) |
10851 | { |
10852 | |
10853 | // Label the child of the GT_COLON operator |
10854 | // op1 is the else part |
10855 | |
10856 | if (tree->gtOper == GT_COLON) |
10857 | { |
10858 | childMsg = "else" ; |
10859 | } |
10860 | else if (tree->gtOper == GT_QMARK) |
10861 | { |
10862 | childMsg = " if" ; |
10863 | } |
10864 | gtDispChild(tree->gtOp.gtOp1, indentStack, IIArcBottom, childMsg, topOnly); |
10865 | } |
10866 | |
10867 | return; |
10868 | } |
10869 | |
10870 | // Now, get the right type of arc for this node |
10871 | if (myArc != IINone) |
10872 | { |
10873 | indentStack->Pop(); |
10874 | indentStack->Push(myArc); |
10875 | } |
10876 | gtDispNode(tree, indentStack, msg, isLIR); |
10877 | |
10878 | // Propagate lowerArc to the lower children. |
10879 | if (indentStack->Depth() > 0) |
10880 | { |
10881 | (void)indentStack->Pop(); |
10882 | indentStack->Push(lowerArc); |
10883 | } |
10884 | |
10885 | // See what kind of a special operator we have here, and handle its special children. |
10886 | |
10887 | switch (tree->gtOper) |
10888 | { |
10889 | case GT_FIELD: |
10890 | if (FieldSeqStore::IsPseudoField(tree->gtField.gtFldHnd)) |
10891 | { |
10892 | printf(" #PseudoField:0x%x" , tree->gtField.gtFldOffset); |
10893 | } |
10894 | else |
10895 | { |
10896 | printf(" %s" , eeGetFieldName(tree->gtField.gtFldHnd), 0); |
10897 | } |
10898 | |
10899 | if (tree->gtField.gtFldObj && !topOnly) |
10900 | { |
10901 | gtDispVN(tree); |
10902 | printf("\n" ); |
10903 | gtDispChild(tree->gtField.gtFldObj, indentStack, IIArcBottom); |
10904 | } |
10905 | else |
10906 | { |
10907 | gtDispRegVal(tree); |
10908 | gtDispVN(tree); |
10909 | printf("\n" ); |
10910 | } |
10911 | break; |
10912 | |
10913 | case GT_CALL: |
10914 | { |
10915 | GenTreeCall* call = tree->AsCall(); |
10916 | assert(call->gtFlags & GTF_CALL); |
10917 | unsigned numChildren = call->NumChildren(); |
10918 | GenTree* lastChild = nullptr; |
10919 | if (numChildren != 0) |
10920 | { |
10921 | lastChild = call->GetChild(numChildren - 1); |
10922 | } |
10923 | |
10924 | if (call->gtCallType != CT_INDIRECT) |
10925 | { |
10926 | const char* methodName; |
10927 | const char* className; |
10928 | |
10929 | methodName = eeGetMethodName(call->gtCallMethHnd, &className); |
10930 | |
10931 | printf(" %s.%s" , className, methodName); |
10932 | } |
10933 | |
10934 | if ((call->gtFlags & GTF_CALL_UNMANAGED) && (call->gtCallMoreFlags & GTF_CALL_M_FRAME_VAR_DEATH)) |
10935 | { |
10936 | printf(" (FramesRoot last use)" ); |
10937 | } |
10938 | |
10939 | if (((call->gtFlags & GTF_CALL_INLINE_CANDIDATE) != 0) && (call->gtInlineCandidateInfo != nullptr) && |
10940 | (call->gtInlineCandidateInfo->exactContextHnd != nullptr)) |
10941 | { |
10942 | printf(" (exactContextHnd=0x%p)" , dspPtr(call->gtInlineCandidateInfo->exactContextHnd)); |
10943 | } |
10944 | |
10945 | gtDispVN(call); |
10946 | if (call->IsMultiRegCall()) |
10947 | { |
10948 | gtDispRegVal(call); |
10949 | } |
10950 | printf("\n" ); |
10951 | |
10952 | if (!topOnly) |
10953 | { |
10954 | char buf[64]; |
10955 | char* bufp; |
10956 | |
10957 | bufp = &buf[0]; |
10958 | |
10959 | if ((call->gtCallObjp != nullptr) && (call->gtCallObjp->gtOper != GT_NOP) && |
10960 | (!call->gtCallObjp->IsArgPlaceHolderNode())) |
10961 | { |
10962 | if (call->gtCallObjp->gtOper == GT_ASG) |
10963 | { |
10964 | sprintf_s(bufp, sizeof(buf), "this SETUP%c" , 0); |
10965 | } |
10966 | else |
10967 | { |
10968 | sprintf_s(bufp, sizeof(buf), "this in %s%c" , compRegVarName(REG_ARG_0), 0); |
10969 | } |
10970 | gtDispChild(call->gtCallObjp, indentStack, (call->gtCallObjp == lastChild) ? IIArcBottom : IIArc, |
10971 | bufp, topOnly); |
10972 | } |
10973 | |
10974 | if (call->gtCallArgs) |
10975 | { |
10976 | gtDispArgList(call, indentStack); |
10977 | } |
10978 | |
10979 | if (call->gtCallType == CT_INDIRECT) |
10980 | { |
10981 | gtDispChild(call->gtCallAddr, indentStack, (call->gtCallAddr == lastChild) ? IIArcBottom : IIArc, |
10982 | "calli tgt" , topOnly); |
10983 | } |
10984 | |
10985 | if (call->gtControlExpr != nullptr) |
10986 | { |
10987 | gtDispChild(call->gtControlExpr, indentStack, |
10988 | (call->gtControlExpr == lastChild) ? IIArcBottom : IIArc, "control expr" , topOnly); |
10989 | } |
10990 | |
10991 | #if !FEATURE_FIXED_OUT_ARGS |
10992 | regList list = call->regArgList; |
10993 | #endif |
10994 | /* process the late argument list */ |
10995 | int lateArgIndex = 0; |
10996 | for (GenTreeArgList* lateArgs = call->gtCallLateArgs; lateArgs; |
10997 | (lateArgIndex++, lateArgs = lateArgs->Rest())) |
10998 | { |
10999 | GenTree* argx; |
11000 | |
11001 | argx = lateArgs->Current(); |
11002 | |
11003 | IndentInfo arcType = (lateArgs->Rest() == nullptr) ? IIArcBottom : IIArc; |
11004 | gtGetLateArgMsg(call, argx, lateArgIndex, -1, bufp, sizeof(buf)); |
11005 | gtDispChild(argx, indentStack, arcType, bufp, topOnly); |
11006 | } |
11007 | } |
11008 | } |
11009 | break; |
11010 | |
11011 | case GT_STMT: |
11012 | printf("\n" ); |
11013 | |
11014 | if (!topOnly) |
11015 | { |
11016 | gtDispChild(tree->gtStmt.gtStmtExpr, indentStack, IIArcBottom); |
11017 | } |
11018 | break; |
11019 | |
11020 | case GT_ARR_ELEM: |
11021 | gtDispVN(tree); |
11022 | printf("\n" ); |
11023 | |
11024 | if (!topOnly) |
11025 | { |
11026 | gtDispChild(tree->gtArrElem.gtArrObj, indentStack, IIArc, nullptr, topOnly); |
11027 | |
11028 | unsigned dim; |
11029 | for (dim = 0; dim < tree->gtArrElem.gtArrRank; dim++) |
11030 | { |
11031 | IndentInfo arcType = ((dim + 1) == tree->gtArrElem.gtArrRank) ? IIArcBottom : IIArc; |
11032 | gtDispChild(tree->gtArrElem.gtArrInds[dim], indentStack, arcType, nullptr, topOnly); |
11033 | } |
11034 | } |
11035 | break; |
11036 | |
11037 | case GT_ARR_OFFSET: |
11038 | gtDispVN(tree); |
11039 | printf("\n" ); |
11040 | if (!topOnly) |
11041 | { |
11042 | gtDispChild(tree->gtArrOffs.gtOffset, indentStack, IIArc, nullptr, topOnly); |
11043 | gtDispChild(tree->gtArrOffs.gtIndex, indentStack, IIArc, nullptr, topOnly); |
11044 | gtDispChild(tree->gtArrOffs.gtArrObj, indentStack, IIArcBottom, nullptr, topOnly); |
11045 | } |
11046 | break; |
11047 | |
11048 | case GT_CMPXCHG: |
11049 | gtDispVN(tree); |
11050 | printf("\n" ); |
11051 | if (!topOnly) |
11052 | { |
11053 | gtDispChild(tree->gtCmpXchg.gtOpLocation, indentStack, IIArc, nullptr, topOnly); |
11054 | gtDispChild(tree->gtCmpXchg.gtOpValue, indentStack, IIArc, nullptr, topOnly); |
11055 | gtDispChild(tree->gtCmpXchg.gtOpComparand, indentStack, IIArcBottom, nullptr, topOnly); |
11056 | } |
11057 | break; |
11058 | |
11059 | case GT_ARR_BOUNDS_CHECK: |
11060 | #ifdef FEATURE_SIMD |
11061 | case GT_SIMD_CHK: |
11062 | #endif // FEATURE_SIMD |
11063 | #ifdef FEATURE_HW_INTRINSICS |
11064 | case GT_HW_INTRINSIC_CHK: |
11065 | #endif // FEATURE_HW_INTRINSICS |
11066 | gtDispVN(tree); |
11067 | printf("\n" ); |
11068 | if (!topOnly) |
11069 | { |
11070 | gtDispChild(tree->gtBoundsChk.gtIndex, indentStack, IIArc, nullptr, topOnly); |
11071 | gtDispChild(tree->gtBoundsChk.gtArrLen, indentStack, IIArcBottom, nullptr, topOnly); |
11072 | } |
11073 | break; |
11074 | |
11075 | case GT_STORE_DYN_BLK: |
11076 | case GT_DYN_BLK: |
11077 | if (tree->OperIsCopyBlkOp()) |
11078 | { |
11079 | printf(" (copy)" ); |
11080 | } |
11081 | else if (tree->OperIsInitBlkOp()) |
11082 | { |
11083 | printf(" (init)" ); |
11084 | } |
11085 | gtDispVN(tree); |
11086 | printf("\n" ); |
11087 | if (!topOnly) |
11088 | { |
11089 | if (tree->gtDynBlk.Data() != nullptr) |
11090 | { |
11091 | gtDispChild(tree->gtDynBlk.Data(), indentStack, IIArc, nullptr, topOnly); |
11092 | } |
11093 | gtDispChild(tree->gtDynBlk.Addr(), indentStack, IIArc, nullptr, topOnly); |
11094 | gtDispChild(tree->gtDynBlk.gtDynamicSize, indentStack, IIArcBottom, nullptr, topOnly); |
11095 | } |
11096 | break; |
11097 | |
11098 | default: |
11099 | printf("<DON'T KNOW HOW TO DISPLAY THIS NODE> :" ); |
11100 | printf("" ); // null string means flush |
11101 | break; |
11102 | } |
11103 | } |
11104 | |
11105 | //------------------------------------------------------------------------ |
11106 | // gtGetArgMsg: Construct a message about the given argument |
11107 | // |
11108 | // Arguments: |
11109 | // call - The call for which 'arg' is an argument |
11110 | // arg - The argument for which a message should be constructed |
11111 | // argNum - The ordinal number of the arg in the argument list |
11112 | // listCount - When printing in LIR form this is the count for a GT_FIELD_LIST |
11113 | // or -1 if we are not printing in LIR form |
11114 | // bufp - A pointer to the buffer into which the message is written |
11115 | // bufLength - The length of the buffer pointed to by bufp |
11116 | // |
11117 | // Return Value: |
11118 | // No return value, but bufp is written. |
11119 | // |
11120 | // Assumptions: |
11121 | // 'call' must be a call node |
11122 | // 'arg' must be an argument to 'call' (else gtArgEntryByNode will assert) |
11123 | |
11124 | void Compiler::gtGetArgMsg( |
11125 | GenTreeCall* call, GenTree* arg, unsigned argNum, int listCount, char* bufp, unsigned bufLength) |
11126 | { |
11127 | if (call->gtCallLateArgs != nullptr) |
11128 | { |
11129 | fgArgTabEntry* curArgTabEntry = gtArgEntryByArgNum(call, argNum); |
11130 | assert(curArgTabEntry); |
11131 | |
11132 | if (arg->gtFlags & GTF_LATE_ARG) |
11133 | { |
11134 | sprintf_s(bufp, bufLength, "arg%d SETUP%c" , argNum, 0); |
11135 | } |
11136 | else |
11137 | { |
11138 | #ifdef _TARGET_ARM_ |
11139 | if (curArgTabEntry->isSplit) |
11140 | { |
11141 | regNumber firstReg = curArgTabEntry->regNum; |
11142 | if (listCount == -1) |
11143 | { |
11144 | if (curArgTabEntry->numRegs == 1) |
11145 | { |
11146 | sprintf_s(bufp, bufLength, "arg%d %s out+%02x%c" , argNum, compRegVarName(firstReg), |
11147 | (curArgTabEntry->slotNum) * TARGET_POINTER_SIZE, 0); |
11148 | } |
11149 | else |
11150 | { |
11151 | regNumber lastReg = REG_STK; |
11152 | char separator = (curArgTabEntry->numRegs == 2) ? ',' : '-'; |
11153 | if (curArgTabEntry->isHfaRegArg) |
11154 | { |
11155 | unsigned lastRegNum = genMapFloatRegNumToRegArgNum(firstReg) + curArgTabEntry->numRegs - 1; |
11156 | lastReg = genMapFloatRegArgNumToRegNum(lastRegNum); |
11157 | } |
11158 | else |
11159 | { |
11160 | unsigned lastRegNum = genMapIntRegNumToRegArgNum(firstReg) + curArgTabEntry->numRegs - 1; |
11161 | lastReg = genMapIntRegArgNumToRegNum(lastRegNum); |
11162 | } |
11163 | sprintf_s(bufp, bufLength, "arg%d %s%c%s out+%02x%c" , argNum, compRegVarName(firstReg), |
11164 | separator, compRegVarName(lastReg), (curArgTabEntry->slotNum) * TARGET_POINTER_SIZE, |
11165 | 0); |
11166 | } |
11167 | } |
11168 | else |
11169 | { |
11170 | unsigned curArgNum = BAD_VAR_NUM; |
11171 | bool isFloat = curArgTabEntry->isHfaRegArg; |
11172 | if (isFloat) |
11173 | { |
11174 | curArgNum = genMapFloatRegNumToRegArgNum(firstReg) + listCount; |
11175 | } |
11176 | else |
11177 | { |
11178 | curArgNum = genMapIntRegNumToRegArgNum(firstReg) + listCount; |
11179 | } |
11180 | |
11181 | if (!isFloat && curArgNum < MAX_REG_ARG) |
11182 | { |
11183 | regNumber curReg = genMapIntRegArgNumToRegNum(curArgNum); |
11184 | sprintf_s(bufp, bufLength, "arg%d m%d %s%c" , argNum, listCount, compRegVarName(curReg), 0); |
11185 | } |
11186 | else if (isFloat && curArgNum < MAX_FLOAT_REG_ARG) |
11187 | { |
11188 | regNumber curReg = genMapFloatRegArgNumToRegNum(curArgNum); |
11189 | sprintf_s(bufp, bufLength, "arg%d m%d %s%c" , argNum, listCount, compRegVarName(curReg), 0); |
11190 | } |
11191 | else |
11192 | { |
11193 | unsigned stackSlot = listCount - curArgTabEntry->numRegs; |
11194 | sprintf_s(bufp, bufLength, "arg%d m%d out+%02x%c" , argNum, listCount, |
11195 | stackSlot * TARGET_POINTER_SIZE, 0); |
11196 | } |
11197 | } |
11198 | return; |
11199 | } |
11200 | #endif // _TARGET_ARM_ |
11201 | #if FEATURE_FIXED_OUT_ARGS |
11202 | if (listCount == -1) |
11203 | { |
11204 | sprintf_s(bufp, bufLength, "arg%d out+%02x%c" , argNum, curArgTabEntry->slotNum * TARGET_POINTER_SIZE, |
11205 | 0); |
11206 | } |
11207 | else // listCount is 0,1,2 or 3 |
11208 | { |
11209 | assert(listCount <= MAX_ARG_REG_COUNT); |
11210 | sprintf_s(bufp, bufLength, "arg%d out+%02x%c" , argNum, |
11211 | (curArgTabEntry->slotNum + listCount) * TARGET_POINTER_SIZE, 0); |
11212 | } |
11213 | #else |
11214 | sprintf_s(bufp, bufLength, "arg%d on STK%c" , argNum, 0); |
11215 | #endif |
11216 | } |
11217 | } |
11218 | else |
11219 | { |
11220 | sprintf_s(bufp, bufLength, "arg%d%c" , argNum, 0); |
11221 | } |
11222 | } |
11223 | |
11224 | //------------------------------------------------------------------------ |
11225 | // gtGetLateArgMsg: Construct a message about the given argument |
11226 | // |
11227 | // Arguments: |
11228 | // call - The call for which 'arg' is an argument |
11229 | // argx - The argument for which a message should be constructed |
11230 | // lateArgIndex - The ordinal number of the arg in the lastArg list |
11231 | // listCount - When printing in LIR form this is the count for a multireg GT_FIELD_LIST |
11232 | // or -1 if we are not printing in LIR form |
11233 | // bufp - A pointer to the buffer into which the message is written |
11234 | // bufLength - The length of the buffer pointed to by bufp |
11235 | // |
11236 | // Return Value: |
11237 | // No return value, but bufp is written. |
11238 | // |
11239 | // Assumptions: |
11240 | // 'call' must be a call node |
11241 | // 'arg' must be an argument to 'call' (else gtArgEntryByNode will assert) |
11242 | |
11243 | void Compiler::gtGetLateArgMsg( |
11244 | GenTreeCall* call, GenTree* argx, int lateArgIndex, int listCount, char* bufp, unsigned bufLength) |
11245 | { |
11246 | assert(!argx->IsArgPlaceHolderNode()); // No place holders nodes are in gtCallLateArgs; |
11247 | |
11248 | fgArgTabEntry* curArgTabEntry = gtArgEntryByLateArgIndex(call, lateArgIndex); |
11249 | assert(curArgTabEntry); |
11250 | regNumber argReg = curArgTabEntry->regNum; |
11251 | |
11252 | #if !FEATURE_FIXED_OUT_ARGS |
11253 | assert(lateArgIndex < call->regArgListCount); |
11254 | assert(argReg == call->regArgList[lateArgIndex]); |
11255 | #else |
11256 | if (argReg == REG_STK) |
11257 | { |
11258 | sprintf_s(bufp, bufLength, "arg%d in out+%02x%c" , curArgTabEntry->argNum, |
11259 | curArgTabEntry->slotNum * TARGET_POINTER_SIZE, 0); |
11260 | } |
11261 | else |
11262 | #endif |
11263 | { |
11264 | if (gtArgIsThisPtr(curArgTabEntry)) |
11265 | { |
11266 | sprintf_s(bufp, bufLength, "this in %s%c" , compRegVarName(argReg), 0); |
11267 | } |
11268 | #ifdef _TARGET_ARM_ |
11269 | else if (curArgTabEntry->isSplit) |
11270 | { |
11271 | regNumber firstReg = curArgTabEntry->regNum; |
11272 | unsigned argNum = curArgTabEntry->argNum; |
11273 | if (listCount == -1) |
11274 | { |
11275 | if (curArgTabEntry->numRegs == 1) |
11276 | { |
11277 | sprintf_s(bufp, bufLength, "arg%d %s out+%02x%c" , argNum, compRegVarName(firstReg), |
11278 | (curArgTabEntry->slotNum) * TARGET_POINTER_SIZE, 0); |
11279 | } |
11280 | else |
11281 | { |
11282 | regNumber lastReg = REG_STK; |
11283 | char separator = (curArgTabEntry->numRegs == 2) ? ',' : '-'; |
11284 | if (curArgTabEntry->isHfaRegArg) |
11285 | { |
11286 | unsigned lastRegNum = genMapFloatRegNumToRegArgNum(firstReg) + curArgTabEntry->numRegs - 1; |
11287 | lastReg = genMapFloatRegArgNumToRegNum(lastRegNum); |
11288 | } |
11289 | else |
11290 | { |
11291 | unsigned lastRegNum = genMapIntRegNumToRegArgNum(firstReg) + curArgTabEntry->numRegs - 1; |
11292 | lastReg = genMapIntRegArgNumToRegNum(lastRegNum); |
11293 | } |
11294 | sprintf_s(bufp, bufLength, "arg%d %s%c%s out+%02x%c" , argNum, compRegVarName(firstReg), separator, |
11295 | compRegVarName(lastReg), (curArgTabEntry->slotNum) * TARGET_POINTER_SIZE, 0); |
11296 | } |
11297 | } |
11298 | else |
11299 | { |
11300 | unsigned curArgNum = BAD_VAR_NUM; |
11301 | bool isFloat = curArgTabEntry->isHfaRegArg; |
11302 | if (isFloat) |
11303 | { |
11304 | curArgNum = genMapFloatRegNumToRegArgNum(firstReg) + listCount; |
11305 | } |
11306 | else |
11307 | { |
11308 | curArgNum = genMapIntRegNumToRegArgNum(firstReg) + listCount; |
11309 | } |
11310 | |
11311 | if (!isFloat && curArgNum < MAX_REG_ARG) |
11312 | { |
11313 | regNumber curReg = genMapIntRegArgNumToRegNum(curArgNum); |
11314 | sprintf_s(bufp, bufLength, "arg%d m%d %s%c" , argNum, listCount, compRegVarName(curReg), 0); |
11315 | } |
11316 | else if (isFloat && curArgNum < MAX_FLOAT_REG_ARG) |
11317 | { |
11318 | regNumber curReg = genMapFloatRegArgNumToRegNum(curArgNum); |
11319 | sprintf_s(bufp, bufLength, "arg%d m%d %s%c" , argNum, listCount, compRegVarName(curReg), 0); |
11320 | } |
11321 | else |
11322 | { |
11323 | unsigned stackSlot = listCount - curArgTabEntry->numRegs; |
11324 | sprintf_s(bufp, bufLength, "arg%d m%d out+%02x%c" , argNum, listCount, |
11325 | stackSlot * TARGET_POINTER_SIZE, 0); |
11326 | } |
11327 | } |
11328 | return; |
11329 | } |
11330 | #endif // _TARGET_ARM_ |
11331 | else |
11332 | { |
11333 | #if FEATURE_MULTIREG_ARGS |
11334 | if (curArgTabEntry->numRegs >= 2) |
11335 | { |
11336 | // listCount could be -1 but it is signed, so this comparison is OK. |
11337 | assert(listCount <= MAX_ARG_REG_COUNT); |
11338 | char separator = (curArgTabEntry->numRegs == 2) ? ',' : '-'; |
11339 | sprintf_s(bufp, bufLength, "arg%d %s%c%s%c" , curArgTabEntry->argNum, compRegVarName(argReg), separator, |
11340 | compRegVarName(curArgTabEntry->getRegNum(curArgTabEntry->numRegs - 1)), 0); |
11341 | } |
11342 | else |
11343 | #endif |
11344 | { |
11345 | sprintf_s(bufp, bufLength, "arg%d in %s%c" , curArgTabEntry->argNum, compRegVarName(argReg), 0); |
11346 | } |
11347 | } |
11348 | } |
11349 | } |
11350 | |
11351 | //------------------------------------------------------------------------ |
11352 | // gtDispArgList: Dump the tree for a call arg list |
11353 | // |
11354 | // Arguments: |
11355 | // call - The call to dump arguments for |
11356 | // indentStack - the specification for the current level of indentation & arcs |
11357 | // |
11358 | // Return Value: |
11359 | // None. |
11360 | // |
11361 | void Compiler::gtDispArgList(GenTreeCall* call, IndentStack* indentStack) |
11362 | { |
11363 | GenTree* args = call->gtCallArgs; |
11364 | unsigned argnum = 0; |
11365 | const int BufLength = 256; |
11366 | char buf[BufLength]; |
11367 | char* bufp = &buf[0]; |
11368 | unsigned numChildren = call->NumChildren(); |
11369 | assert(numChildren != 0); |
11370 | bool argListIsLastChild = (args == call->GetChild(numChildren - 1)); |
11371 | |
11372 | IndentInfo arcType = IIArc; |
11373 | if (call->gtCallObjp != nullptr) |
11374 | { |
11375 | argnum++; |
11376 | } |
11377 | |
11378 | while (args != nullptr) |
11379 | { |
11380 | assert(args->gtOper == GT_LIST); |
11381 | GenTree* arg = args->gtOp.gtOp1; |
11382 | if (!arg->IsNothingNode() && !arg->IsArgPlaceHolderNode()) |
11383 | { |
11384 | gtGetArgMsg(call, arg, argnum, -1, bufp, BufLength); |
11385 | if (argListIsLastChild && (args->gtOp.gtOp2 == nullptr)) |
11386 | { |
11387 | arcType = IIArcBottom; |
11388 | } |
11389 | gtDispChild(arg, indentStack, arcType, bufp, false); |
11390 | } |
11391 | args = args->gtOp.gtOp2; |
11392 | argnum++; |
11393 | } |
11394 | } |
11395 | |
11396 | //------------------------------------------------------------------------ |
11397 | // gtDispArgList: Dump the tree for a call arg list |
11398 | // |
11399 | // Arguments: |
11400 | // tree - The call for which 'arg' is an argument |
11401 | // indentStack - the specification for the current level of indentation & arcs |
11402 | // |
11403 | // Return Value: |
11404 | // None. |
11405 | // |
11406 | // Assumptions: |
11407 | // 'tree' must be a GT_LIST node |
11408 | |
11409 | void Compiler::gtDispTreeList(GenTree* tree, IndentStack* indentStack /* = nullptr */) |
11410 | { |
11411 | for (/*--*/; tree != nullptr; tree = tree->gtNext) |
11412 | { |
11413 | gtDispTree(tree, indentStack); |
11414 | printf("\n" ); |
11415 | } |
11416 | } |
11417 | |
11418 | //------------------------------------------------------------------------ |
11419 | // Compiler::gtDispRange: dumps a range of LIR. |
11420 | // |
11421 | // Arguments: |
11422 | // range - the range of LIR to display. |
11423 | // |
11424 | void Compiler::gtDispRange(LIR::ReadOnlyRange const& range) |
11425 | { |
11426 | for (GenTree* node : range) |
11427 | { |
11428 | gtDispLIRNode(node); |
11429 | } |
11430 | } |
11431 | |
11432 | //------------------------------------------------------------------------ |
11433 | // Compiler::gtDispTreeRange: dumps the LIR range that contains all of the |
11434 | // nodes in the dataflow tree rooted at a given |
11435 | // node. |
11436 | // |
11437 | // Arguments: |
11438 | // containingRange - the LIR range that contains the root node. |
11439 | // tree - the root of the dataflow tree. |
11440 | // |
11441 | void Compiler::gtDispTreeRange(LIR::Range& containingRange, GenTree* tree) |
11442 | { |
11443 | bool unused; |
11444 | gtDispRange(containingRange.GetTreeRange(tree, &unused)); |
11445 | } |
11446 | |
11447 | //------------------------------------------------------------------------ |
11448 | // Compiler::gtDispLIRNode: dumps a single LIR node. |
11449 | // |
11450 | // Arguments: |
11451 | // node - the LIR node to dump. |
11452 | // prefixMsg - an optional prefix for each line of output. |
11453 | // |
11454 | void Compiler::gtDispLIRNode(GenTree* node, const char* prefixMsg /* = nullptr */) |
11455 | { |
11456 | auto displayOperand = [](GenTree* operand, const char* message, IndentInfo operandArc, IndentStack& indentStack, |
11457 | size_t prefixIndent) { |
11458 | assert(operand != nullptr); |
11459 | assert(message != nullptr); |
11460 | |
11461 | if (prefixIndent != 0) |
11462 | { |
11463 | printf("%*s" , (int)prefixIndent, "" ); |
11464 | } |
11465 | |
11466 | // 49 spaces for alignment |
11467 | printf("%-49s" , "" ); |
11468 | #if FEATURE_SET_FLAGS |
11469 | // additional flag enlarges the flag field by one character |
11470 | printf(" " ); |
11471 | #endif |
11472 | |
11473 | indentStack.Push(operandArc); |
11474 | indentStack.print(); |
11475 | indentStack.Pop(); |
11476 | operandArc = IIArc; |
11477 | |
11478 | printf(" t%-5d %-6s %s\n" , operand->gtTreeID, varTypeName(operand->TypeGet()), message); |
11479 | }; |
11480 | |
11481 | IndentStack indentStack(this); |
11482 | |
11483 | size_t prefixIndent = 0; |
11484 | if (prefixMsg != nullptr) |
11485 | { |
11486 | prefixIndent = strlen(prefixMsg); |
11487 | } |
11488 | |
11489 | const int bufLength = 256; |
11490 | char buf[bufLength]; |
11491 | |
11492 | const bool nodeIsCall = node->IsCall(); |
11493 | |
11494 | // Visit operands |
11495 | IndentInfo operandArc = IIArcTop; |
11496 | for (GenTree* operand : node->Operands()) |
11497 | { |
11498 | if (operand->IsArgPlaceHolderNode() || !operand->IsValue()) |
11499 | { |
11500 | // Either of these situations may happen with calls. |
11501 | continue; |
11502 | } |
11503 | |
11504 | if (nodeIsCall) |
11505 | { |
11506 | GenTreeCall* call = node->AsCall(); |
11507 | if (operand == call->gtCallObjp) |
11508 | { |
11509 | sprintf_s(buf, sizeof(buf), "this in %s" , compRegVarName(REG_ARG_0)); |
11510 | displayOperand(operand, buf, operandArc, indentStack, prefixIndent); |
11511 | } |
11512 | else if (operand == call->gtCallAddr) |
11513 | { |
11514 | displayOperand(operand, "calli tgt" , operandArc, indentStack, prefixIndent); |
11515 | } |
11516 | else if (operand == call->gtControlExpr) |
11517 | { |
11518 | displayOperand(operand, "control expr" , operandArc, indentStack, prefixIndent); |
11519 | } |
11520 | else if (operand == call->gtCallCookie) |
11521 | { |
11522 | displayOperand(operand, "cookie" , operandArc, indentStack, prefixIndent); |
11523 | } |
11524 | else |
11525 | { |
11526 | fgArgTabEntry* curArgTabEntry = gtArgEntryByNode(call, operand); |
11527 | assert(curArgTabEntry); |
11528 | |
11529 | if (operand->OperGet() == GT_LIST) |
11530 | { |
11531 | int listIndex = 0; |
11532 | for (GenTreeArgList* element = operand->AsArgList(); element != nullptr; element = element->Rest()) |
11533 | { |
11534 | operand = element->Current(); |
11535 | if (curArgTabEntry->lateArgInx == (unsigned)-1) |
11536 | { |
11537 | gtGetArgMsg(call, operand, curArgTabEntry->argNum, listIndex, buf, sizeof(buf)); |
11538 | } |
11539 | else |
11540 | { |
11541 | gtGetLateArgMsg(call, operand, curArgTabEntry->lateArgInx, listIndex, buf, sizeof(buf)); |
11542 | } |
11543 | |
11544 | displayOperand(operand, buf, operandArc, indentStack, prefixIndent); |
11545 | operandArc = IIArc; |
11546 | } |
11547 | } |
11548 | else |
11549 | { |
11550 | if (!curArgTabEntry->isLateArg()) |
11551 | { |
11552 | gtGetArgMsg(call, operand, curArgTabEntry->argNum, -1, buf, sizeof(buf)); |
11553 | } |
11554 | else |
11555 | { |
11556 | gtGetLateArgMsg(call, operand, curArgTabEntry->lateArgInx, -1, buf, sizeof(buf)); |
11557 | } |
11558 | |
11559 | displayOperand(operand, buf, operandArc, indentStack, prefixIndent); |
11560 | } |
11561 | } |
11562 | } |
11563 | else if (node->OperIsDynBlkOp()) |
11564 | { |
11565 | if (operand == node->AsBlk()->Addr()) |
11566 | { |
11567 | displayOperand(operand, "lhs" , operandArc, indentStack, prefixIndent); |
11568 | } |
11569 | else if (operand == node->AsBlk()->Data()) |
11570 | { |
11571 | displayOperand(operand, "rhs" , operandArc, indentStack, prefixIndent); |
11572 | } |
11573 | else |
11574 | { |
11575 | assert(operand == node->AsDynBlk()->gtDynamicSize); |
11576 | displayOperand(operand, "size" , operandArc, indentStack, prefixIndent); |
11577 | } |
11578 | } |
11579 | else if (node->OperGet() == GT_DYN_BLK) |
11580 | { |
11581 | if (operand == node->AsBlk()->Addr()) |
11582 | { |
11583 | displayOperand(operand, "lhs" , operandArc, indentStack, prefixIndent); |
11584 | } |
11585 | else |
11586 | { |
11587 | assert(operand == node->AsDynBlk()->gtDynamicSize); |
11588 | displayOperand(operand, "size" , operandArc, indentStack, prefixIndent); |
11589 | } |
11590 | } |
11591 | else if (node->OperIs(GT_ASG)) |
11592 | { |
11593 | if (operand == node->gtGetOp1()) |
11594 | { |
11595 | displayOperand(operand, "lhs" , operandArc, indentStack, prefixIndent); |
11596 | } |
11597 | else |
11598 | { |
11599 | displayOperand(operand, "rhs" , operandArc, indentStack, prefixIndent); |
11600 | } |
11601 | } |
11602 | else |
11603 | { |
11604 | displayOperand(operand, "" , operandArc, indentStack, prefixIndent); |
11605 | } |
11606 | |
11607 | operandArc = IIArc; |
11608 | } |
11609 | |
11610 | // Visit the operator |
11611 | |
11612 | if (prefixMsg != nullptr) |
11613 | { |
11614 | printf("%s" , prefixMsg); |
11615 | } |
11616 | |
11617 | const bool topOnly = true; |
11618 | const bool isLIR = true; |
11619 | gtDispTree(node, &indentStack, nullptr, topOnly, isLIR); |
11620 | } |
11621 | |
11622 | /*****************************************************************************/ |
11623 | #endif // DEBUG |
11624 | |
11625 | /***************************************************************************** |
11626 | * |
11627 | * Check if the given node can be folded, |
11628 | * and call the methods to perform the folding |
11629 | */ |
11630 | |
11631 | GenTree* Compiler::gtFoldExpr(GenTree* tree) |
11632 | { |
11633 | unsigned kind = tree->OperKind(); |
11634 | |
11635 | /* We must have a simple operation to fold */ |
11636 | |
11637 | // If we're in CSE, it's not safe to perform tree |
11638 | // folding given that it can will potentially |
11639 | // change considered CSE candidates. |
11640 | if (optValnumCSE_phase) |
11641 | { |
11642 | return tree; |
11643 | } |
11644 | |
11645 | if (!(kind & GTK_SMPOP)) |
11646 | { |
11647 | return tree; |
11648 | } |
11649 | |
11650 | GenTree* op1 = tree->gtOp.gtOp1; |
11651 | |
11652 | /* Filter out non-foldable trees that can have constant children */ |
11653 | |
11654 | assert(kind & (GTK_UNOP | GTK_BINOP)); |
11655 | switch (tree->gtOper) |
11656 | { |
11657 | case GT_RETFILT: |
11658 | case GT_RETURN: |
11659 | case GT_IND: |
11660 | return tree; |
11661 | default: |
11662 | break; |
11663 | } |
11664 | |
11665 | /* try to fold the current node */ |
11666 | |
11667 | if ((kind & GTK_UNOP) && op1) |
11668 | { |
11669 | if (op1->OperKind() & GTK_CONST) |
11670 | { |
11671 | return gtFoldExprConst(tree); |
11672 | } |
11673 | } |
11674 | else if ((kind & GTK_BINOP) && op1 && tree->gtOp.gtOp2 && |
11675 | // Don't take out conditionals for debugging |
11676 | (opts.OptimizationEnabled() || !tree->OperIsCompare())) |
11677 | { |
11678 | GenTree* op2 = tree->gtOp.gtOp2; |
11679 | |
11680 | // The atomic operations are exempted here because they are never computable statically; |
11681 | // one of their arguments is an address. |
11682 | if (((op1->OperKind() & op2->OperKind()) & GTK_CONST) && !tree->OperIsAtomicOp()) |
11683 | { |
11684 | /* both nodes are constants - fold the expression */ |
11685 | return gtFoldExprConst(tree); |
11686 | } |
11687 | else if ((op1->OperKind() | op2->OperKind()) & GTK_CONST) |
11688 | { |
11689 | /* at least one is a constant - see if we have a |
11690 | * special operator that can use only one constant |
11691 | * to fold - e.g. booleans */ |
11692 | |
11693 | return gtFoldExprSpecial(tree); |
11694 | } |
11695 | else if (tree->OperIsCompare()) |
11696 | { |
11697 | /* comparisons of two local variables can sometimes be folded */ |
11698 | |
11699 | return gtFoldExprCompare(tree); |
11700 | } |
11701 | else if (op2->OperGet() == GT_COLON) |
11702 | { |
11703 | assert(tree->OperGet() == GT_QMARK); |
11704 | |
11705 | GenTree* colon_op1 = op2->gtOp.gtOp1; |
11706 | GenTree* colon_op2 = op2->gtOp.gtOp2; |
11707 | |
11708 | if (gtCompareTree(colon_op1, colon_op2)) |
11709 | { |
11710 | // Both sides of the GT_COLON are the same tree |
11711 | |
11712 | GenTree* sideEffList = nullptr; |
11713 | gtExtractSideEffList(op1, &sideEffList); |
11714 | |
11715 | // Clear colon flags only if the qmark itself is not conditionaly executed |
11716 | if ((tree->gtFlags & GTF_COLON_COND) == 0) |
11717 | { |
11718 | fgWalkTreePre(&colon_op2, gtClearColonCond); |
11719 | } |
11720 | |
11721 | if (sideEffList == nullptr) |
11722 | { |
11723 | // No side-effects, just return colon_op2 |
11724 | return colon_op2; |
11725 | } |
11726 | else |
11727 | { |
11728 | #ifdef DEBUG |
11729 | if (verbose) |
11730 | { |
11731 | printf("\nIdentical GT_COLON trees with side effects! Extracting side effects...\n" ); |
11732 | gtDispTree(sideEffList); |
11733 | printf("\n" ); |
11734 | } |
11735 | #endif |
11736 | // Change the GT_COLON into a GT_COMMA node with the side-effects |
11737 | op2->ChangeOper(GT_COMMA); |
11738 | op2->gtFlags |= (sideEffList->gtFlags & GTF_ALL_EFFECT); |
11739 | op2->gtOp.gtOp1 = sideEffList; |
11740 | return op2; |
11741 | } |
11742 | } |
11743 | } |
11744 | } |
11745 | |
11746 | /* Return the original node (folded/bashed or not) */ |
11747 | |
11748 | return tree; |
11749 | } |
11750 | |
11751 | //------------------------------------------------------------------------ |
11752 | // gtFoldExprCall: see if a call is foldable |
11753 | // |
11754 | // Arguments: |
11755 | // call - call to examine |
11756 | // |
11757 | // Returns: |
11758 | // The original call if no folding happened. |
11759 | // An alternative tree if folding happens. |
11760 | // |
11761 | // Notes: |
11762 | // Checks for calls to Type.op_Equality, Type.op_Inequality, and |
11763 | // Enum.HasFlag, and if the call is to one of these, |
11764 | // attempts to optimize. |
11765 | |
11766 | GenTree* Compiler::gtFoldExprCall(GenTreeCall* call) |
11767 | { |
11768 | // Can only fold calls to special intrinsics. |
11769 | if ((call->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) == 0) |
11770 | { |
11771 | return call; |
11772 | } |
11773 | |
11774 | // Defer folding if not optimizing. |
11775 | if (opts.OptimizationDisabled()) |
11776 | { |
11777 | return call; |
11778 | } |
11779 | |
11780 | // Fetch id of the intrinsic. |
11781 | const CorInfoIntrinsics methodID = info.compCompHnd->getIntrinsicID(call->gtCallMethHnd); |
11782 | |
11783 | switch (methodID) |
11784 | { |
11785 | case CORINFO_INTRINSIC_TypeEQ: |
11786 | case CORINFO_INTRINSIC_TypeNEQ: |
11787 | { |
11788 | noway_assert(call->TypeGet() == TYP_INT); |
11789 | GenTree* op1 = call->gtCallArgs->gtOp.gtOp1; |
11790 | GenTree* op2 = call->gtCallArgs->gtOp.gtOp2->gtOp.gtOp1; |
11791 | |
11792 | // If either operand is known to be a RuntimeType, this can be folded |
11793 | GenTree* result = gtFoldTypeEqualityCall(methodID, op1, op2); |
11794 | if (result != nullptr) |
11795 | { |
11796 | return result; |
11797 | } |
11798 | break; |
11799 | } |
11800 | |
11801 | default: |
11802 | break; |
11803 | } |
11804 | |
11805 | // Check for a new-style jit intrinsic. |
11806 | const NamedIntrinsic ni = lookupNamedIntrinsic(call->gtCallMethHnd); |
11807 | |
11808 | if (ni == NI_System_Enum_HasFlag) |
11809 | { |
11810 | GenTree* thisOp = call->gtCallObjp; |
11811 | GenTree* flagOp = call->gtCallArgs->gtOp.gtOp1; |
11812 | GenTree* result = gtOptimizeEnumHasFlag(thisOp, flagOp); |
11813 | |
11814 | if (result != nullptr) |
11815 | { |
11816 | return result; |
11817 | } |
11818 | } |
11819 | |
11820 | return call; |
11821 | } |
11822 | |
11823 | //------------------------------------------------------------------------ |
11824 | // gtFoldTypeEqualityCall: see if a (potential) type equality call is foldable |
11825 | // |
11826 | // Arguments: |
11827 | // methodID -- type equality intrinsic ID |
11828 | // op1 -- first argument to call |
11829 | // op2 -- second argument to call |
11830 | // |
11831 | // Returns: |
11832 | // nulltpr if no folding happened. |
11833 | // An alternative tree if folding happens. |
11834 | // |
11835 | // Notes: |
11836 | // If either operand is known to be a a RuntimeType, then the type |
11837 | // equality methods will simply check object identity and so we can |
11838 | // fold the call into a simple compare of the call's operands. |
11839 | |
11840 | GenTree* Compiler::gtFoldTypeEqualityCall(CorInfoIntrinsics methodID, GenTree* op1, GenTree* op2) |
11841 | { |
11842 | // The method must be be a type equality intrinsic |
11843 | assert(methodID == CORINFO_INTRINSIC_TypeEQ || methodID == CORINFO_INTRINSIC_TypeNEQ); |
11844 | |
11845 | if ((gtGetTypeProducerKind(op1) == TPK_Unknown) && (gtGetTypeProducerKind(op2) == TPK_Unknown)) |
11846 | { |
11847 | return nullptr; |
11848 | } |
11849 | |
11850 | const genTreeOps simpleOp = (methodID == CORINFO_INTRINSIC_TypeEQ) ? GT_EQ : GT_NE; |
11851 | |
11852 | JITDUMP("\nFolding call to Type:op_%s to a simple compare via %s\n" , |
11853 | methodID == CORINFO_INTRINSIC_TypeEQ ? "Equality" : "Inequality" , GenTree::OpName(simpleOp)); |
11854 | |
11855 | GenTree* compare = gtNewOperNode(simpleOp, TYP_INT, op1, op2); |
11856 | |
11857 | return compare; |
11858 | } |
11859 | |
11860 | /***************************************************************************** |
11861 | * |
11862 | * Some comparisons can be folded: |
11863 | * |
11864 | * locA == locA |
11865 | * classVarA == classVarA |
11866 | * locA + locB == locB + locA |
11867 | * |
11868 | */ |
11869 | |
11870 | GenTree* Compiler::gtFoldExprCompare(GenTree* tree) |
11871 | { |
11872 | GenTree* op1 = tree->gtOp.gtOp1; |
11873 | GenTree* op2 = tree->gtOp.gtOp2; |
11874 | |
11875 | assert(tree->OperIsCompare()); |
11876 | |
11877 | /* Filter out cases that cannot be folded here */ |
11878 | |
11879 | /* Do not fold floats or doubles (e.g. NaN != Nan) */ |
11880 | |
11881 | if (varTypeIsFloating(op1->TypeGet())) |
11882 | { |
11883 | return tree; |
11884 | } |
11885 | |
11886 | /* Currently we can only fold when the two subtrees exactly match */ |
11887 | |
11888 | if ((tree->gtFlags & GTF_SIDE_EFFECT) || GenTree::Compare(op1, op2, true) == false) |
11889 | { |
11890 | return tree; /* return unfolded tree */ |
11891 | } |
11892 | |
11893 | GenTree* cons; |
11894 | |
11895 | switch (tree->gtOper) |
11896 | { |
11897 | case GT_EQ: |
11898 | case GT_LE: |
11899 | case GT_GE: |
11900 | cons = gtNewIconNode(true); /* Folds to GT_CNS_INT(true) */ |
11901 | break; |
11902 | |
11903 | case GT_NE: |
11904 | case GT_LT: |
11905 | case GT_GT: |
11906 | cons = gtNewIconNode(false); /* Folds to GT_CNS_INT(false) */ |
11907 | break; |
11908 | |
11909 | default: |
11910 | assert(!"Unexpected relOp" ); |
11911 | return tree; |
11912 | } |
11913 | |
11914 | /* The node has beeen folded into 'cons' */ |
11915 | |
11916 | if (fgGlobalMorph) |
11917 | { |
11918 | fgMorphTreeDone(cons); |
11919 | } |
11920 | else |
11921 | { |
11922 | cons->gtNext = tree->gtNext; |
11923 | cons->gtPrev = tree->gtPrev; |
11924 | } |
11925 | |
11926 | return cons; |
11927 | } |
11928 | |
11929 | //------------------------------------------------------------------------ |
11930 | // gtCreateHandleCompare: generate a type handle comparison |
11931 | // |
11932 | // Arguments: |
11933 | // oper -- comparison operation (equal/not equal) |
11934 | // op1 -- first operand |
11935 | // op2 -- second operand |
11936 | // typeCheckInliningResult -- indicates how the comparison should happen |
11937 | // |
11938 | // Returns: |
11939 | // Type comparison tree |
11940 | // |
11941 | |
11942 | GenTree* Compiler::gtCreateHandleCompare(genTreeOps oper, |
11943 | GenTree* op1, |
11944 | GenTree* op2, |
11945 | CorInfoInlineTypeCheck typeCheckInliningResult) |
11946 | { |
11947 | // If we can compare pointers directly, just emit the binary operation |
11948 | if (typeCheckInliningResult == CORINFO_INLINE_TYPECHECK_PASS) |
11949 | { |
11950 | return gtNewOperNode(oper, TYP_INT, op1, op2); |
11951 | } |
11952 | |
11953 | assert(typeCheckInliningResult == CORINFO_INLINE_TYPECHECK_USE_HELPER); |
11954 | |
11955 | // Emit a call to a runtime helper |
11956 | GenTreeArgList* helperArgs = gtNewArgList(op1, op2); |
11957 | GenTree* ret = gtNewHelperCallNode(CORINFO_HELP_ARE_TYPES_EQUIVALENT, TYP_INT, helperArgs); |
11958 | if (oper == GT_EQ) |
11959 | { |
11960 | ret = gtNewOperNode(GT_NE, TYP_INT, ret, gtNewIconNode(0, TYP_INT)); |
11961 | } |
11962 | else |
11963 | { |
11964 | assert(oper == GT_NE); |
11965 | ret = gtNewOperNode(GT_EQ, TYP_INT, ret, gtNewIconNode(0, TYP_INT)); |
11966 | } |
11967 | |
11968 | return ret; |
11969 | } |
11970 | |
11971 | //------------------------------------------------------------------------ |
11972 | // gtFoldTypeCompare: see if a type comparison can be further simplified |
11973 | // |
11974 | // Arguments: |
11975 | // tree -- tree possibly comparing types |
11976 | // |
11977 | // Returns: |
11978 | // An alternative tree if folding happens. |
11979 | // Original tree otherwise. |
11980 | // |
11981 | // Notes: |
11982 | // Checks for |
11983 | // typeof(...) == obj.GetType() |
11984 | // typeof(...) == typeof(...) |
11985 | // |
11986 | // And potentially optimizes away the need to obtain actual |
11987 | // RuntimeType objects to do the comparison. |
11988 | |
11989 | GenTree* Compiler::gtFoldTypeCompare(GenTree* tree) |
11990 | { |
11991 | // Only handle EQ and NE |
11992 | // (maybe relop vs null someday) |
11993 | const genTreeOps oper = tree->OperGet(); |
11994 | if ((oper != GT_EQ) && (oper != GT_NE)) |
11995 | { |
11996 | return tree; |
11997 | } |
11998 | |
11999 | // Screen for the right kinds of operands |
12000 | GenTree* const op1 = tree->gtOp.gtOp1; |
12001 | const TypeProducerKind op1Kind = gtGetTypeProducerKind(op1); |
12002 | if (op1Kind == TPK_Unknown) |
12003 | { |
12004 | return tree; |
12005 | } |
12006 | |
12007 | GenTree* const op2 = tree->gtOp.gtOp2; |
12008 | const TypeProducerKind op2Kind = gtGetTypeProducerKind(op2); |
12009 | if (op2Kind == TPK_Unknown) |
12010 | { |
12011 | return tree; |
12012 | } |
12013 | |
12014 | // We must have a handle on one side or the other here to optimize, |
12015 | // otherwise we can't be sure that optimizing is sound. |
12016 | const bool op1IsFromHandle = (op1Kind == TPK_Handle); |
12017 | const bool op2IsFromHandle = (op2Kind == TPK_Handle); |
12018 | |
12019 | if (!(op1IsFromHandle || op2IsFromHandle)) |
12020 | { |
12021 | return tree; |
12022 | } |
12023 | |
12024 | // If both types are created via handles, we can simply compare |
12025 | // handles (or the indirection cells for handles) instead of the |
12026 | // types that they'd create. |
12027 | if (op1IsFromHandle && op2IsFromHandle) |
12028 | { |
12029 | JITDUMP("Optimizing compare of types-from-handles to instead compare handles\n" ); |
12030 | GenTree* op1ClassFromHandle = tree->gtOp.gtOp1->gtCall.gtCallArgs->gtOp.gtOp1; |
12031 | GenTree* op2ClassFromHandle = tree->gtOp.gtOp2->gtCall.gtCallArgs->gtOp.gtOp1; |
12032 | GenTree* op1TunneledHandle = nullptr; |
12033 | GenTree* op2TunneledHandle = nullptr; |
12034 | CORINFO_CLASS_HANDLE cls1Hnd = NO_CLASS_HANDLE; |
12035 | CORINFO_CLASS_HANDLE cls2Hnd = NO_CLASS_HANDLE; |
12036 | unsigned runtimeLookupCount = 0; |
12037 | |
12038 | // Try and find class handles from op1 and op2 |
12039 | cls1Hnd = gtGetHelperArgClassHandle(op1ClassFromHandle, &runtimeLookupCount, &op1TunneledHandle); |
12040 | cls2Hnd = gtGetHelperArgClassHandle(op2ClassFromHandle, &runtimeLookupCount, &op2TunneledHandle); |
12041 | |
12042 | // If we have both class handles, try and resolve the type equality test completely. |
12043 | bool resolveFailed = false; |
12044 | |
12045 | if ((cls1Hnd != NO_CLASS_HANDLE) && (cls2Hnd != NO_CLASS_HANDLE)) |
12046 | { |
12047 | JITDUMP("Asking runtime to compare %p (%s) and %p (%s) for equality\n" , dspPtr(cls1Hnd), |
12048 | info.compCompHnd->getClassName(cls1Hnd), dspPtr(cls2Hnd), info.compCompHnd->getClassName(cls2Hnd)); |
12049 | TypeCompareState s = info.compCompHnd->compareTypesForEquality(cls1Hnd, cls2Hnd); |
12050 | |
12051 | if (s != TypeCompareState::May) |
12052 | { |
12053 | // Type comparison result is known. |
12054 | const bool typesAreEqual = (s == TypeCompareState::Must); |
12055 | const bool operatorIsEQ = (oper == GT_EQ); |
12056 | const int compareResult = operatorIsEQ ^ typesAreEqual ? 0 : 1; |
12057 | JITDUMP("Runtime reports comparison is known at jit time: %u\n" , compareResult); |
12058 | GenTree* result = gtNewIconNode(compareResult); |
12059 | |
12060 | // Any runtime lookups that fed into this compare are |
12061 | // now dead code, so they no longer require the runtime context. |
12062 | assert(lvaGenericsContextUseCount >= runtimeLookupCount); |
12063 | lvaGenericsContextUseCount -= runtimeLookupCount; |
12064 | return result; |
12065 | } |
12066 | else |
12067 | { |
12068 | resolveFailed = true; |
12069 | } |
12070 | } |
12071 | |
12072 | if (resolveFailed) |
12073 | { |
12074 | JITDUMP("Runtime reports comparison is NOT known at jit time\n" ); |
12075 | } |
12076 | else |
12077 | { |
12078 | JITDUMP("Could not find handle for %s%s\n" , (cls1Hnd == NO_CLASS_HANDLE) ? " cls1" : "" , |
12079 | (cls2Hnd == NO_CLASS_HANDLE) ? " cls2" : "" ); |
12080 | } |
12081 | |
12082 | // We can't answer the equality comparison definitively at jit |
12083 | // time, but can still simplfy the comparison. |
12084 | // |
12085 | // Find out how we can compare the two handles. |
12086 | // NOTE: We're potentially passing NO_CLASS_HANDLE, but the runtime knows what to do with it here. |
12087 | CorInfoInlineTypeCheck inliningKind = |
12088 | info.compCompHnd->canInlineTypeCheck(cls1Hnd, CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN); |
12089 | |
12090 | // If the first type needs helper, check the other type: it might be okay with a simple compare. |
12091 | if (inliningKind == CORINFO_INLINE_TYPECHECK_USE_HELPER) |
12092 | { |
12093 | inliningKind = info.compCompHnd->canInlineTypeCheck(cls2Hnd, CORINFO_INLINE_TYPECHECK_SOURCE_TOKEN); |
12094 | } |
12095 | |
12096 | assert(inliningKind == CORINFO_INLINE_TYPECHECK_PASS || inliningKind == CORINFO_INLINE_TYPECHECK_USE_HELPER); |
12097 | |
12098 | // If we successfully tunneled through both operands, compare |
12099 | // the tunneled values, otherwise compare the original values. |
12100 | GenTree* compare; |
12101 | if ((op1TunneledHandle != nullptr) && (op2TunneledHandle != nullptr)) |
12102 | { |
12103 | compare = gtCreateHandleCompare(oper, op1TunneledHandle, op2TunneledHandle, inliningKind); |
12104 | } |
12105 | else |
12106 | { |
12107 | compare = gtCreateHandleCompare(oper, op1ClassFromHandle, op2ClassFromHandle, inliningKind); |
12108 | } |
12109 | |
12110 | // Drop any now-irrelvant flags |
12111 | compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE); |
12112 | |
12113 | return compare; |
12114 | } |
12115 | |
12116 | // Just one operand creates a type from a handle. |
12117 | // |
12118 | // If the other operand is fetching the type from an object, |
12119 | // we can sometimes optimize the type compare into a simpler |
12120 | // method table comparison. |
12121 | // |
12122 | // TODO: if other operand is null... |
12123 | if ((op1Kind != TPK_GetType) && (op2Kind != TPK_GetType)) |
12124 | { |
12125 | return tree; |
12126 | } |
12127 | |
12128 | GenTree* const opHandle = op1IsFromHandle ? op1 : op2; |
12129 | GenTree* const opOther = op1IsFromHandle ? op2 : op1; |
12130 | |
12131 | // Tunnel through the handle operand to get at the class handle involved. |
12132 | GenTree* const opHandleArgument = opHandle->gtCall.gtCallArgs->gtOp.gtOp1; |
12133 | CORINFO_CLASS_HANDLE clsHnd = gtGetHelperArgClassHandle(opHandleArgument); |
12134 | |
12135 | // If we couldn't find the class handle, give up. |
12136 | if (clsHnd == NO_CLASS_HANDLE) |
12137 | { |
12138 | return tree; |
12139 | } |
12140 | |
12141 | // Ask the VM if this type can be equality tested by a simple method |
12142 | // table comparison. |
12143 | CorInfoInlineTypeCheck typeCheckInliningResult = |
12144 | info.compCompHnd->canInlineTypeCheck(clsHnd, CORINFO_INLINE_TYPECHECK_SOURCE_VTABLE); |
12145 | if (typeCheckInliningResult == CORINFO_INLINE_TYPECHECK_NONE) |
12146 | { |
12147 | return tree; |
12148 | } |
12149 | |
12150 | // We're good to go. |
12151 | JITDUMP("Optimizing compare of obj.GetType()" |
12152 | " and type-from-handle to compare method table pointer\n" ); |
12153 | |
12154 | // opHandleArgument is the method table we're looking for. |
12155 | GenTree* const knownMT = opHandleArgument; |
12156 | |
12157 | // Fetch object method table from the object itself. |
12158 | GenTree* objOp = nullptr; |
12159 | |
12160 | // Note we may see intrinsified or regular calls to GetType |
12161 | if (opOther->OperGet() == GT_INTRINSIC) |
12162 | { |
12163 | objOp = opOther->gtUnOp.gtOp1; |
12164 | } |
12165 | else |
12166 | { |
12167 | assert(opOther->OperGet() == GT_CALL); |
12168 | objOp = opOther->gtCall.gtCallObjp; |
12169 | } |
12170 | |
12171 | GenTree* const objMT = gtNewOperNode(GT_IND, TYP_I_IMPL, objOp); |
12172 | |
12173 | // Update various flags |
12174 | objMT->gtFlags |= GTF_EXCEPT; |
12175 | compCurBB->bbFlags |= BBF_HAS_VTABREF; |
12176 | optMethodFlags |= OMF_HAS_VTABLEREF; |
12177 | |
12178 | // Compare the two method tables |
12179 | GenTree* const compare = gtCreateHandleCompare(oper, objMT, knownMT, typeCheckInliningResult); |
12180 | |
12181 | // Drop any now irrelevant flags |
12182 | compare->gtFlags |= tree->gtFlags & (GTF_RELOP_JMP_USED | GTF_RELOP_QMARK | GTF_DONT_CSE); |
12183 | |
12184 | // And we're done |
12185 | return compare; |
12186 | } |
12187 | |
12188 | //------------------------------------------------------------------------ |
12189 | // gtGetHelperArgClassHandle: find the compile time class handle from |
12190 | // a helper call argument tree |
12191 | // |
12192 | // Arguments: |
12193 | // tree - tree that passes the handle to the helper |
12194 | // runtimeLookupCount [optional, in/out] - incremented if tree was a runtime lookup |
12195 | // handleTree [optional, out] - set to the literal operand tree for indirect handles |
12196 | // |
12197 | // Returns: |
12198 | // The compile time class handle if known. |
12199 | // |
12200 | CORINFO_CLASS_HANDLE Compiler::gtGetHelperArgClassHandle(GenTree* tree, |
12201 | unsigned* runtimeLookupCount, |
12202 | GenTree** handleTree) |
12203 | { |
12204 | CORINFO_CLASS_HANDLE result = NO_CLASS_HANDLE; |
12205 | |
12206 | // Walk through any wrapping nop. |
12207 | if ((tree->gtOper == GT_NOP) && (tree->gtType == TYP_I_IMPL)) |
12208 | { |
12209 | tree = tree->gtOp.gtOp1; |
12210 | } |
12211 | |
12212 | // The handle could be a literal constant |
12213 | if ((tree->OperGet() == GT_CNS_INT) && (tree->TypeGet() == TYP_I_IMPL)) |
12214 | { |
12215 | assert(tree->IsIconHandle(GTF_ICON_CLASS_HDL)); |
12216 | result = (CORINFO_CLASS_HANDLE)tree->gtIntCon.gtCompileTimeHandle; |
12217 | } |
12218 | // Or the result of a runtime lookup |
12219 | else if (tree->OperGet() == GT_RUNTIMELOOKUP) |
12220 | { |
12221 | result = tree->AsRuntimeLookup()->GetClassHandle(); |
12222 | |
12223 | if (runtimeLookupCount != nullptr) |
12224 | { |
12225 | *runtimeLookupCount = *runtimeLookupCount + 1; |
12226 | } |
12227 | } |
12228 | // Or something reached indirectly |
12229 | else if (tree->gtOper == GT_IND) |
12230 | { |
12231 | // The handle indirs we are looking for will be marked as non-faulting. |
12232 | // Certain others (eg from refanytype) may not be. |
12233 | if (tree->gtFlags & GTF_IND_NONFAULTING) |
12234 | { |
12235 | GenTree* handleTreeInternal = tree->gtOp.gtOp1; |
12236 | |
12237 | if ((handleTreeInternal->OperGet() == GT_CNS_INT) && (handleTreeInternal->TypeGet() == TYP_I_IMPL)) |
12238 | { |
12239 | // These handle constants should be class handles. |
12240 | assert(handleTreeInternal->IsIconHandle(GTF_ICON_CLASS_HDL)); |
12241 | result = (CORINFO_CLASS_HANDLE)handleTreeInternal->gtIntCon.gtCompileTimeHandle; |
12242 | |
12243 | if (handleTree != nullptr) |
12244 | { |
12245 | *handleTree = handleTreeInternal; |
12246 | } |
12247 | } |
12248 | } |
12249 | } |
12250 | |
12251 | return result; |
12252 | } |
12253 | |
12254 | /***************************************************************************** |
12255 | * |
12256 | * Some binary operators can be folded even if they have only one |
12257 | * operand constant - e.g. boolean operators, add with 0 |
12258 | * multiply with 1, etc |
12259 | */ |
12260 | |
12261 | GenTree* Compiler::gtFoldExprSpecial(GenTree* tree) |
12262 | { |
12263 | GenTree* op1 = tree->gtOp.gtOp1; |
12264 | GenTree* op2 = tree->gtOp.gtOp2; |
12265 | genTreeOps oper = tree->OperGet(); |
12266 | |
12267 | GenTree* op; |
12268 | GenTree* cons; |
12269 | ssize_t val; |
12270 | |
12271 | assert(tree->OperKind() & GTK_BINOP); |
12272 | |
12273 | /* Filter out operators that cannot be folded here */ |
12274 | if (oper == GT_CAST) |
12275 | { |
12276 | return tree; |
12277 | } |
12278 | |
12279 | /* We only consider TYP_INT for folding |
12280 | * Do not fold pointer arithmetic (e.g. addressing modes!) */ |
12281 | |
12282 | if (oper != GT_QMARK && !varTypeIsIntOrI(tree->gtType)) |
12283 | { |
12284 | return tree; |
12285 | } |
12286 | |
12287 | /* Find out which is the constant node */ |
12288 | |
12289 | if (op1->IsCnsIntOrI()) |
12290 | { |
12291 | op = op2; |
12292 | cons = op1; |
12293 | } |
12294 | else if (op2->IsCnsIntOrI()) |
12295 | { |
12296 | op = op1; |
12297 | cons = op2; |
12298 | } |
12299 | else |
12300 | { |
12301 | return tree; |
12302 | } |
12303 | |
12304 | /* Get the constant value */ |
12305 | |
12306 | val = cons->gtIntConCommon.IconValue(); |
12307 | |
12308 | /* Here op is the non-constant operand, val is the constant, |
12309 | first is true if the constant is op1 */ |
12310 | |
12311 | switch (oper) |
12312 | { |
12313 | case GT_EQ: |
12314 | case GT_NE: |
12315 | case GT_GT: |
12316 | |
12317 | // Optimize boxed value classes; these are always false. This IL is |
12318 | // generated when a generic value is tested against null: |
12319 | // <T> ... foo(T x) { ... if ((object)x == null) ... |
12320 | if (val == 0 && op->IsBoxedValue()) |
12321 | { |
12322 | JITDUMP("\nAttempting to optimize BOX(valueType) %s null [%06u]\n" , GenTree::OpName(oper), |
12323 | dspTreeID(tree)); |
12324 | |
12325 | // We don't expect GT_GT with signed compares, and we |
12326 | // can't predict the result if we do see it, since the |
12327 | // boxed object addr could have its high bit set. |
12328 | if ((oper == GT_GT) && !tree->IsUnsigned()) |
12329 | { |
12330 | JITDUMP(" bailing; unexpected signed compare via GT_GT\n" ); |
12331 | } |
12332 | else |
12333 | { |
12334 | // The tree under the box must be side effect free |
12335 | // since we will drop it if we optimize. |
12336 | assert(!gtTreeHasSideEffects(op->gtBox.gtOp.gtOp1, GTF_SIDE_EFFECT)); |
12337 | |
12338 | // See if we can optimize away the box and related statements. |
12339 | GenTree* boxSourceTree = gtTryRemoveBoxUpstreamEffects(op); |
12340 | bool didOptimize = (boxSourceTree != nullptr); |
12341 | |
12342 | // If optimization succeeded, remove the box. |
12343 | if (didOptimize) |
12344 | { |
12345 | // Set up the result of the compare. |
12346 | int compareResult = 0; |
12347 | if (oper == GT_GT) |
12348 | { |
12349 | // GT_GT(null, box) == false |
12350 | // GT_GT(box, null) == true |
12351 | compareResult = (op1 == op); |
12352 | } |
12353 | else if (oper == GT_EQ) |
12354 | { |
12355 | // GT_EQ(box, null) == false |
12356 | // GT_EQ(null, box) == false |
12357 | compareResult = 0; |
12358 | } |
12359 | else |
12360 | { |
12361 | assert(oper == GT_NE); |
12362 | // GT_NE(box, null) == true |
12363 | // GT_NE(null, box) == true |
12364 | compareResult = 1; |
12365 | } |
12366 | |
12367 | JITDUMP("\nSuccess: replacing BOX(valueType) %s null with %d\n" , GenTree::OpName(oper), |
12368 | compareResult); |
12369 | |
12370 | op = gtNewIconNode(compareResult); |
12371 | |
12372 | if (fgGlobalMorph) |
12373 | { |
12374 | fgMorphTreeDone(op); |
12375 | } |
12376 | else |
12377 | { |
12378 | op->gtNext = tree->gtNext; |
12379 | op->gtPrev = tree->gtPrev; |
12380 | } |
12381 | |
12382 | return op; |
12383 | } |
12384 | } |
12385 | } |
12386 | |
12387 | break; |
12388 | |
12389 | case GT_ADD: |
12390 | if (val == 0) |
12391 | { |
12392 | goto DONE_FOLD; |
12393 | } |
12394 | break; |
12395 | |
12396 | case GT_MUL: |
12397 | if (val == 1) |
12398 | { |
12399 | goto DONE_FOLD; |
12400 | } |
12401 | else if (val == 0) |
12402 | { |
12403 | /* Multiply by zero - return the 'zero' node, but not if side effects */ |
12404 | if (!(op->gtFlags & GTF_SIDE_EFFECT)) |
12405 | { |
12406 | op = cons; |
12407 | goto DONE_FOLD; |
12408 | } |
12409 | } |
12410 | break; |
12411 | |
12412 | case GT_DIV: |
12413 | case GT_UDIV: |
12414 | if ((op2 == cons) && (val == 1) && !(op1->OperKind() & GTK_CONST)) |
12415 | { |
12416 | goto DONE_FOLD; |
12417 | } |
12418 | break; |
12419 | |
12420 | case GT_SUB: |
12421 | if ((op2 == cons) && (val == 0) && !(op1->OperKind() & GTK_CONST)) |
12422 | { |
12423 | goto DONE_FOLD; |
12424 | } |
12425 | break; |
12426 | |
12427 | case GT_AND: |
12428 | if (val == 0) |
12429 | { |
12430 | /* AND with zero - return the 'zero' node, but not if side effects */ |
12431 | |
12432 | if (!(op->gtFlags & GTF_SIDE_EFFECT)) |
12433 | { |
12434 | op = cons; |
12435 | goto DONE_FOLD; |
12436 | } |
12437 | } |
12438 | else |
12439 | { |
12440 | /* The GTF_BOOLEAN flag is set for nodes that are part |
12441 | * of a boolean expression, thus all their children |
12442 | * are known to evaluate to only 0 or 1 */ |
12443 | |
12444 | if (tree->gtFlags & GTF_BOOLEAN) |
12445 | { |
12446 | |
12447 | /* The constant value must be 1 |
12448 | * AND with 1 stays the same */ |
12449 | assert(val == 1); |
12450 | goto DONE_FOLD; |
12451 | } |
12452 | } |
12453 | break; |
12454 | |
12455 | case GT_OR: |
12456 | if (val == 0) |
12457 | { |
12458 | goto DONE_FOLD; |
12459 | } |
12460 | else if (tree->gtFlags & GTF_BOOLEAN) |
12461 | { |
12462 | /* The constant value must be 1 - OR with 1 is 1 */ |
12463 | |
12464 | assert(val == 1); |
12465 | |
12466 | /* OR with one - return the 'one' node, but not if side effects */ |
12467 | |
12468 | if (!(op->gtFlags & GTF_SIDE_EFFECT)) |
12469 | { |
12470 | op = cons; |
12471 | goto DONE_FOLD; |
12472 | } |
12473 | } |
12474 | break; |
12475 | |
12476 | case GT_LSH: |
12477 | case GT_RSH: |
12478 | case GT_RSZ: |
12479 | case GT_ROL: |
12480 | case GT_ROR: |
12481 | if (val == 0) |
12482 | { |
12483 | if (op2 == cons) |
12484 | { |
12485 | goto DONE_FOLD; |
12486 | } |
12487 | else if (!(op->gtFlags & GTF_SIDE_EFFECT)) |
12488 | { |
12489 | op = cons; |
12490 | goto DONE_FOLD; |
12491 | } |
12492 | } |
12493 | break; |
12494 | |
12495 | case GT_QMARK: |
12496 | { |
12497 | assert(op1 == cons && op2 == op && op2->gtOper == GT_COLON); |
12498 | assert(op2->gtOp.gtOp1 && op2->gtOp.gtOp2); |
12499 | |
12500 | assert(val == 0 || val == 1); |
12501 | |
12502 | GenTree* opToDelete; |
12503 | if (val) |
12504 | { |
12505 | op = op2->AsColon()->ThenNode(); |
12506 | opToDelete = op2->AsColon()->ElseNode(); |
12507 | } |
12508 | else |
12509 | { |
12510 | op = op2->AsColon()->ElseNode(); |
12511 | opToDelete = op2->AsColon()->ThenNode(); |
12512 | } |
12513 | |
12514 | // Clear colon flags only if the qmark itself is not conditionaly executed |
12515 | if ((tree->gtFlags & GTF_COLON_COND) == 0) |
12516 | { |
12517 | fgWalkTreePre(&op, gtClearColonCond); |
12518 | } |
12519 | } |
12520 | |
12521 | goto DONE_FOLD; |
12522 | |
12523 | default: |
12524 | break; |
12525 | } |
12526 | |
12527 | /* The node is not foldable */ |
12528 | |
12529 | return tree; |
12530 | |
12531 | DONE_FOLD: |
12532 | |
12533 | /* The node has beeen folded into 'op' */ |
12534 | |
12535 | // If there was an assigment update, we just morphed it into |
12536 | // a use, update the flags appropriately |
12537 | if (op->gtOper == GT_LCL_VAR) |
12538 | { |
12539 | assert(tree->OperIs(GT_ASG) || (op->gtFlags & (GTF_VAR_USEASG | GTF_VAR_DEF)) == 0); |
12540 | |
12541 | op->gtFlags &= ~(GTF_VAR_USEASG | GTF_VAR_DEF); |
12542 | } |
12543 | |
12544 | op->gtNext = tree->gtNext; |
12545 | op->gtPrev = tree->gtPrev; |
12546 | |
12547 | return op; |
12548 | } |
12549 | |
12550 | //------------------------------------------------------------------------ |
12551 | // gtTryRemoveBoxUpstreamEffects: given an unused value type box, |
12552 | // try and remove the upstream allocation and unnecessary parts of |
12553 | // the copy. |
12554 | // |
12555 | // Arguments: |
12556 | // op - the box node to optimize |
12557 | // options - controls whether and how trees are modified |
12558 | // (see notes) |
12559 | // |
12560 | // Return Value: |
12561 | // A tree representing the original value to box, if removal |
12562 | // is successful/possible (but see note). nullptr if removal fails. |
12563 | // |
12564 | // Notes: |
12565 | // Value typed box gets special treatment because it has associated |
12566 | // side effects that can be removed if the box result is not used. |
12567 | // |
12568 | // By default (options == BR_REMOVE_AND_NARROW) this method will |
12569 | // try and remove unnecessary trees and will try and reduce remaning |
12570 | // operations to the minimal set, possibly narrowing the width of |
12571 | // loads from the box source if it is a struct. |
12572 | // |
12573 | // To perform a trial removal, pass BR_DONT_REMOVE. This can be |
12574 | // useful to determine if this optimization should only be |
12575 | // performed if some other conditions hold true. |
12576 | // |
12577 | // To remove but not alter the access to the box source, pass |
12578 | // BR_REMOVE_BUT_NOT_NARROW. |
12579 | // |
12580 | // To remove and return the tree for the type handle used for |
12581 | // the boxed newobj, pass BR_REMOVE_BUT_NOT_NARROW_WANT_TYPE_HANDLE. |
12582 | // This can be useful when the only part of the box that is "live" |
12583 | // is its type. |
12584 | // |
12585 | // If removal fails, is is possible that a subsequent pass may be |
12586 | // able to optimize. Blocking side effects may now be minimized |
12587 | // (null or bounds checks might have been removed) or might be |
12588 | // better known (inline return placeholder updated with the actual |
12589 | // return expression). So the box is perhaps best left as is to |
12590 | // help trigger this re-examination. |
12591 | |
12592 | GenTree* Compiler::gtTryRemoveBoxUpstreamEffects(GenTree* op, BoxRemovalOptions options) |
12593 | { |
12594 | assert(op->IsBoxedValue()); |
12595 | |
12596 | // grab related parts for the optimization |
12597 | GenTreeBox* box = op->AsBox(); |
12598 | GenTree* asgStmt = box->gtAsgStmtWhenInlinedBoxValue; |
12599 | GenTree* copyStmt = box->gtCopyStmtWhenInlinedBoxValue; |
12600 | |
12601 | assert(asgStmt->gtOper == GT_STMT); |
12602 | assert(copyStmt->gtOper == GT_STMT); |
12603 | |
12604 | JITDUMP("gtTryRemoveBoxUpstreamEffects: %s to %s of BOX (valuetype)" |
12605 | " [%06u] (assign/newobj [%06u] copy [%06u])\n" , |
12606 | (options == BR_DONT_REMOVE) ? "checking if it is possible" : "attempting" , |
12607 | (options == BR_MAKE_LOCAL_COPY) ? "make local unboxed version" : "remove side effects" , dspTreeID(op), |
12608 | dspTreeID(asgStmt), dspTreeID(copyStmt)); |
12609 | |
12610 | // If we don't recognize the form of the assign, bail. |
12611 | GenTree* asg = asgStmt->gtStmt.gtStmtExpr; |
12612 | if (asg->gtOper != GT_ASG) |
12613 | { |
12614 | JITDUMP(" bailing; unexpected assignment op %s\n" , GenTree::OpName(asg->gtOper)); |
12615 | return nullptr; |
12616 | } |
12617 | |
12618 | // If we're eventually going to return the type handle, remember it now. |
12619 | GenTree* boxTypeHandle = nullptr; |
12620 | if ((options == BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE) || (options == BR_DONT_REMOVE_WANT_TYPE_HANDLE)) |
12621 | { |
12622 | GenTree* asgSrc = asg->gtOp.gtOp2; |
12623 | genTreeOps asgSrcOper = asgSrc->OperGet(); |
12624 | |
12625 | // Allocation may be via AllocObj or via helper call, depending |
12626 | // on when this is invoked and whether the jit is using AllocObj |
12627 | // for R2R allocations. |
12628 | if (asgSrcOper == GT_ALLOCOBJ) |
12629 | { |
12630 | GenTreeAllocObj* allocObj = asgSrc->AsAllocObj(); |
12631 | boxTypeHandle = allocObj->gtOp.gtOp1; |
12632 | } |
12633 | else if (asgSrcOper == GT_CALL) |
12634 | { |
12635 | GenTreeCall* newobjCall = asgSrc->AsCall(); |
12636 | GenTree* newobjArgs = newobjCall->gtCallArgs; |
12637 | |
12638 | // In R2R expansions the handle may not be an explicit operand to the helper, |
12639 | // so we can't remove the box. |
12640 | if (newobjArgs == nullptr) |
12641 | { |
12642 | assert(newobjCall->IsHelperCall(this, CORINFO_HELP_READYTORUN_NEW)); |
12643 | JITDUMP(" bailing; newobj via R2R helper\n" ); |
12644 | return nullptr; |
12645 | } |
12646 | |
12647 | boxTypeHandle = newobjArgs->AsArgList()->Current(); |
12648 | } |
12649 | else |
12650 | { |
12651 | unreached(); |
12652 | } |
12653 | |
12654 | assert(boxTypeHandle != nullptr); |
12655 | } |
12656 | |
12657 | // If we don't recognize the form of the copy, bail. |
12658 | GenTree* copy = copyStmt->gtStmt.gtStmtExpr; |
12659 | if (copy->gtOper != GT_ASG) |
12660 | { |
12661 | // GT_RET_EXPR is a tolerable temporary failure. |
12662 | // The jit will revisit this optimization after |
12663 | // inlining is done. |
12664 | if (copy->gtOper == GT_RET_EXPR) |
12665 | { |
12666 | JITDUMP(" bailing; must wait for replacement of copy %s\n" , GenTree::OpName(copy->gtOper)); |
12667 | } |
12668 | else |
12669 | { |
12670 | // Anything else is a missed case we should |
12671 | // figure out how to handle. One known case |
12672 | // is GT_COMMAs enclosing the GT_ASG we are |
12673 | // looking for. |
12674 | JITDUMP(" bailing; unexpected copy op %s\n" , GenTree::OpName(copy->gtOper)); |
12675 | } |
12676 | return nullptr; |
12677 | } |
12678 | |
12679 | // Handle case where we are optimizing the box into a local copy |
12680 | if (options == BR_MAKE_LOCAL_COPY) |
12681 | { |
12682 | // Drill into the box to get at the box temp local and the box type |
12683 | GenTree* boxTemp = box->BoxOp(); |
12684 | assert(boxTemp->IsLocal()); |
12685 | const unsigned boxTempLcl = boxTemp->AsLclVar()->GetLclNum(); |
12686 | assert(lvaTable[boxTempLcl].lvType == TYP_REF); |
12687 | CORINFO_CLASS_HANDLE boxClass = lvaTable[boxTempLcl].lvClassHnd; |
12688 | assert(boxClass != nullptr); |
12689 | |
12690 | // Verify that the copyDst has the expected shape |
12691 | // (blk|obj|ind (add (boxTempLcl, ptr-size))) |
12692 | // |
12693 | // The shape here is constrained to the patterns we produce |
12694 | // over in impImportAndPushBox for the inlined box case. |
12695 | GenTree* copyDst = copy->gtOp.gtOp1; |
12696 | |
12697 | if (!copyDst->OperIs(GT_BLK, GT_IND, GT_OBJ)) |
12698 | { |
12699 | JITDUMP("Unexpected copy dest operator %s\n" , GenTree::OpName(copyDst->gtOper)); |
12700 | return nullptr; |
12701 | } |
12702 | |
12703 | GenTree* copyDstAddr = copyDst->gtOp.gtOp1; |
12704 | if (copyDstAddr->OperGet() != GT_ADD) |
12705 | { |
12706 | JITDUMP("Unexpected copy dest address tree\n" ); |
12707 | return nullptr; |
12708 | } |
12709 | |
12710 | GenTree* copyDstAddrOp1 = copyDstAddr->gtOp.gtOp1; |
12711 | if ((copyDstAddrOp1->OperGet() != GT_LCL_VAR) || (copyDstAddrOp1->gtLclVarCommon.gtLclNum != boxTempLcl)) |
12712 | { |
12713 | JITDUMP("Unexpected copy dest address 1st addend\n" ); |
12714 | return nullptr; |
12715 | } |
12716 | |
12717 | GenTree* copyDstAddrOp2 = copyDstAddr->gtOp.gtOp2; |
12718 | if (!copyDstAddrOp2->IsIntegralConst(TARGET_POINTER_SIZE)) |
12719 | { |
12720 | JITDUMP("Unexpected copy dest address 2nd addend\n" ); |
12721 | return nullptr; |
12722 | } |
12723 | |
12724 | // Screening checks have all passed. Do the transformation. |
12725 | // |
12726 | // Retype the box temp to be a struct |
12727 | JITDUMP("Retyping box temp V%02u to struct %s\n" , boxTempLcl, eeGetClassName(boxClass)); |
12728 | lvaTable[boxTempLcl].lvType = TYP_UNDEF; |
12729 | const bool isUnsafeValueClass = false; |
12730 | lvaSetStruct(boxTempLcl, boxClass, isUnsafeValueClass); |
12731 | var_types boxTempType = lvaTable[boxTempLcl].lvType; |
12732 | |
12733 | // Remove the newobj and assigment to box temp |
12734 | JITDUMP("Bashing NEWOBJ [%06u] to NOP\n" , dspTreeID(asg)); |
12735 | asg->gtBashToNOP(); |
12736 | |
12737 | // Update the copy from the value to be boxed to the box temp |
12738 | GenTree* newDst = gtNewOperNode(GT_ADDR, TYP_BYREF, gtNewLclvNode(boxTempLcl, boxTempType)); |
12739 | copyDst->gtOp.gtOp1 = newDst; |
12740 | |
12741 | // Return the address of the now-struct typed box temp |
12742 | GenTree* retValue = gtNewOperNode(GT_ADDR, TYP_BYREF, gtNewLclvNode(boxTempLcl, boxTempType)); |
12743 | |
12744 | return retValue; |
12745 | } |
12746 | |
12747 | // If the copy is a struct copy, make sure we know how to isolate |
12748 | // any source side effects. |
12749 | GenTree* copySrc = copy->gtOp.gtOp2; |
12750 | |
12751 | // If the copy source is from a pending inline, wait for it to resolve. |
12752 | if (copySrc->gtOper == GT_RET_EXPR) |
12753 | { |
12754 | JITDUMP(" bailing; must wait for replacement of copy source %s\n" , GenTree::OpName(copySrc->gtOper)); |
12755 | return nullptr; |
12756 | } |
12757 | |
12758 | bool hasSrcSideEffect = false; |
12759 | bool isStructCopy = false; |
12760 | |
12761 | if (gtTreeHasSideEffects(copySrc, GTF_SIDE_EFFECT)) |
12762 | { |
12763 | hasSrcSideEffect = true; |
12764 | |
12765 | if (varTypeIsStruct(copySrc->gtType)) |
12766 | { |
12767 | isStructCopy = true; |
12768 | |
12769 | if ((copySrc->gtOper != GT_OBJ) && (copySrc->gtOper != GT_IND) && (copySrc->gtOper != GT_FIELD)) |
12770 | { |
12771 | // We don't know how to handle other cases, yet. |
12772 | JITDUMP(" bailing; unexpected copy source struct op with side effect %s\n" , |
12773 | GenTree::OpName(copySrc->gtOper)); |
12774 | return nullptr; |
12775 | } |
12776 | } |
12777 | } |
12778 | |
12779 | // If this was a trial removal, we're done. |
12780 | if (options == BR_DONT_REMOVE) |
12781 | { |
12782 | return copySrc; |
12783 | } |
12784 | |
12785 | if (options == BR_DONT_REMOVE_WANT_TYPE_HANDLE) |
12786 | { |
12787 | return boxTypeHandle; |
12788 | } |
12789 | |
12790 | // Otherwise, proceed with the optimization. |
12791 | // |
12792 | // Change the assignment expression to a NOP. |
12793 | JITDUMP("\nBashing NEWOBJ [%06u] to NOP\n" , dspTreeID(asg)); |
12794 | asg->gtBashToNOP(); |
12795 | |
12796 | // Change the copy expression so it preserves key |
12797 | // source side effects. |
12798 | JITDUMP("\nBashing COPY [%06u]" , dspTreeID(copy)); |
12799 | |
12800 | if (!hasSrcSideEffect) |
12801 | { |
12802 | // If there were no copy source side effects just bash |
12803 | // the copy to a NOP. |
12804 | copy->gtBashToNOP(); |
12805 | JITDUMP(" to NOP; no source side effects.\n" ); |
12806 | } |
12807 | else if (!isStructCopy) |
12808 | { |
12809 | // For scalar types, go ahead and produce the |
12810 | // value as the copy is fairly cheap and likely |
12811 | // the optimizer can trim things down to just the |
12812 | // minimal side effect parts. |
12813 | copyStmt->gtStmt.gtStmtExpr = copySrc; |
12814 | JITDUMP(" to scalar read via [%06u]\n" , dspTreeID(copySrc)); |
12815 | } |
12816 | else |
12817 | { |
12818 | // For struct types read the first byte of the |
12819 | // source struct; there's no need to read the |
12820 | // entire thing, and no place to put it. |
12821 | assert(copySrc->gtOper == GT_OBJ || copySrc->gtOper == GT_IND || copySrc->gtOper == GT_FIELD); |
12822 | copyStmt->gtStmt.gtStmtExpr = copySrc; |
12823 | |
12824 | if (options == BR_REMOVE_AND_NARROW || options == BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE) |
12825 | { |
12826 | JITDUMP(" to read first byte of struct via modified [%06u]\n" , dspTreeID(copySrc)); |
12827 | copySrc->ChangeOper(GT_IND); |
12828 | copySrc->gtType = TYP_BYTE; |
12829 | } |
12830 | else |
12831 | { |
12832 | JITDUMP(" to read entire struct via modified [%06u]\n" , dspTreeID(copySrc)); |
12833 | } |
12834 | } |
12835 | |
12836 | if (fgStmtListThreaded) |
12837 | { |
12838 | fgSetStmtSeq(asgStmt); |
12839 | fgSetStmtSeq(copyStmt); |
12840 | } |
12841 | |
12842 | // Box effects were successfully optimized. |
12843 | |
12844 | if (options == BR_REMOVE_AND_NARROW_WANT_TYPE_HANDLE) |
12845 | { |
12846 | return boxTypeHandle; |
12847 | } |
12848 | else |
12849 | { |
12850 | return copySrc; |
12851 | } |
12852 | } |
12853 | |
12854 | //------------------------------------------------------------------------ |
12855 | // gtOptimizeEnumHasFlag: given the operands for a call to Enum.HasFlag, |
12856 | // try and optimize the call to a simple and/compare tree. |
12857 | // |
12858 | // Arguments: |
12859 | // thisOp - first argument to the call |
12860 | // flagOp - second argument to the call |
12861 | // |
12862 | // Return Value: |
12863 | // A new cmp/amd tree if successful. nullptr on failure. |
12864 | // |
12865 | // Notes: |
12866 | // If successful, may allocate new temps and modify connected |
12867 | // statements. |
12868 | |
12869 | GenTree* Compiler::gtOptimizeEnumHasFlag(GenTree* thisOp, GenTree* flagOp) |
12870 | { |
12871 | JITDUMP("Considering optimizing call to Enum.HasFlag....\n" ); |
12872 | |
12873 | // Operands must be boxes |
12874 | if (!thisOp->IsBoxedValue() || !flagOp->IsBoxedValue()) |
12875 | { |
12876 | JITDUMP("bailing, need both inputs to be BOXes\n" ); |
12877 | return nullptr; |
12878 | } |
12879 | |
12880 | // Operands must have same type |
12881 | bool isExactThis = false; |
12882 | bool isNonNullThis = false; |
12883 | CORINFO_CLASS_HANDLE thisHnd = gtGetClassHandle(thisOp, &isExactThis, &isNonNullThis); |
12884 | |
12885 | if (thisHnd == nullptr) |
12886 | { |
12887 | JITDUMP("bailing, can't find type for 'this' operand\n" ); |
12888 | return nullptr; |
12889 | } |
12890 | |
12891 | // A boxed thisOp should have exact type and non-null instance |
12892 | assert(isExactThis); |
12893 | assert(isNonNullThis); |
12894 | |
12895 | bool isExactFlag = false; |
12896 | bool isNonNullFlag = false; |
12897 | CORINFO_CLASS_HANDLE flagHnd = gtGetClassHandle(flagOp, &isExactFlag, &isNonNullFlag); |
12898 | |
12899 | if (flagHnd == nullptr) |
12900 | { |
12901 | JITDUMP("bailing, can't find type for 'flag' operand\n" ); |
12902 | return nullptr; |
12903 | } |
12904 | |
12905 | // A boxed flagOp should have exact type and non-null instance |
12906 | assert(isExactFlag); |
12907 | assert(isNonNullFlag); |
12908 | |
12909 | if (flagHnd != thisHnd) |
12910 | { |
12911 | JITDUMP("bailing, operand types differ\n" ); |
12912 | return nullptr; |
12913 | } |
12914 | |
12915 | // If we have a shared type instance we can't safely check type |
12916 | // equality, so bail. |
12917 | DWORD classAttribs = info.compCompHnd->getClassAttribs(thisHnd); |
12918 | if (classAttribs & CORINFO_FLG_SHAREDINST) |
12919 | { |
12920 | JITDUMP("bailing, have shared instance type\n" ); |
12921 | return nullptr; |
12922 | } |
12923 | |
12924 | // Simulate removing the box for thisOP. We need to know that it can |
12925 | // be safely removed before we can optimize. |
12926 | GenTree* thisVal = gtTryRemoveBoxUpstreamEffects(thisOp, BR_DONT_REMOVE); |
12927 | if (thisVal == nullptr) |
12928 | { |
12929 | // Note we may fail here if the this operand comes from |
12930 | // a call. We should be able to retry this post-inlining. |
12931 | JITDUMP("bailing, can't undo box of 'this' operand\n" ); |
12932 | return nullptr; |
12933 | } |
12934 | |
12935 | GenTree* flagVal = gtTryRemoveBoxUpstreamEffects(flagOp, BR_REMOVE_BUT_NOT_NARROW); |
12936 | if (flagVal == nullptr) |
12937 | { |
12938 | // Note we may fail here if the flag operand comes from |
12939 | // a call. We should be able to retry this post-inlining. |
12940 | JITDUMP("bailing, can't undo box of 'flag' operand\n" ); |
12941 | return nullptr; |
12942 | } |
12943 | |
12944 | // Yes, both boxes can be cleaned up. Optimize. |
12945 | JITDUMP("Optimizing call to Enum.HasFlag\n" ); |
12946 | |
12947 | // Undo the boxing of thisOp and prepare to operate directly |
12948 | // on the original enum values. |
12949 | thisVal = gtTryRemoveBoxUpstreamEffects(thisOp, BR_REMOVE_BUT_NOT_NARROW); |
12950 | |
12951 | // Our trial removal above should guarantee successful removal here. |
12952 | assert(thisVal != nullptr); |
12953 | |
12954 | // We should have a consistent view of the type |
12955 | var_types type = thisVal->TypeGet(); |
12956 | assert(type == flagVal->TypeGet()); |
12957 | |
12958 | // The thisVal and flagVal trees come from earlier statements. |
12959 | // |
12960 | // Unless they are invariant values, we need to evaluate them both |
12961 | // to temps at those points to safely transmit the values here. |
12962 | // |
12963 | // Also we need to use the flag twice, so we need two trees for it. |
12964 | GenTree* thisValOpt = nullptr; |
12965 | GenTree* flagValOpt = nullptr; |
12966 | GenTree* flagValOptCopy = nullptr; |
12967 | |
12968 | if (thisVal->IsIntegralConst()) |
12969 | { |
12970 | thisValOpt = gtClone(thisVal); |
12971 | assert(thisValOpt != nullptr); |
12972 | } |
12973 | else |
12974 | { |
12975 | const unsigned thisTmp = lvaGrabTemp(true DEBUGARG("Enum:HasFlag this temp" )); |
12976 | GenTree* thisAsg = gtNewTempAssign(thisTmp, thisVal); |
12977 | GenTree* thisAsgStmt = thisOp->AsBox()->gtCopyStmtWhenInlinedBoxValue; |
12978 | thisAsgStmt->gtStmt.gtStmtExpr = thisAsg; |
12979 | thisValOpt = gtNewLclvNode(thisTmp, type); |
12980 | } |
12981 | |
12982 | if (flagVal->IsIntegralConst()) |
12983 | { |
12984 | flagValOpt = gtClone(flagVal); |
12985 | assert(flagValOpt != nullptr); |
12986 | flagValOptCopy = gtClone(flagVal); |
12987 | assert(flagValOptCopy != nullptr); |
12988 | } |
12989 | else |
12990 | { |
12991 | const unsigned flagTmp = lvaGrabTemp(true DEBUGARG("Enum:HasFlag flag temp" )); |
12992 | GenTree* flagAsg = gtNewTempAssign(flagTmp, flagVal); |
12993 | GenTree* flagAsgStmt = flagOp->AsBox()->gtCopyStmtWhenInlinedBoxValue; |
12994 | flagAsgStmt->gtStmt.gtStmtExpr = flagAsg; |
12995 | flagValOpt = gtNewLclvNode(flagTmp, type); |
12996 | flagValOptCopy = gtNewLclvNode(flagTmp, type); |
12997 | } |
12998 | |
12999 | // Turn the call into (thisValTmp & flagTmp) == flagTmp. |
13000 | GenTree* andTree = gtNewOperNode(GT_AND, type, thisValOpt, flagValOpt); |
13001 | GenTree* cmpTree = gtNewOperNode(GT_EQ, TYP_INT, andTree, flagValOptCopy); |
13002 | |
13003 | JITDUMP("Optimized call to Enum.HasFlag\n" ); |
13004 | |
13005 | return cmpTree; |
13006 | } |
13007 | |
13008 | /***************************************************************************** |
13009 | * |
13010 | * Fold the given constant tree. |
13011 | */ |
13012 | |
13013 | #ifdef _PREFAST_ |
13014 | #pragma warning(push) |
13015 | #pragma warning(disable : 21000) // Suppress PREFast warning about overly large function |
13016 | #endif |
13017 | GenTree* Compiler::gtFoldExprConst(GenTree* tree) |
13018 | { |
13019 | unsigned kind = tree->OperKind(); |
13020 | |
13021 | SSIZE_T i1, i2, itemp; |
13022 | INT64 lval1, lval2, ltemp; |
13023 | float f1, f2; |
13024 | double d1, d2; |
13025 | var_types switchType; |
13026 | FieldSeqNode* fieldSeq = FieldSeqStore::NotAField(); // default unless we override it when folding |
13027 | |
13028 | assert(kind & (GTK_UNOP | GTK_BINOP)); |
13029 | |
13030 | GenTree* op1 = tree->gtOp.gtOp1; |
13031 | GenTree* op2 = tree->gtGetOp2IfPresent(); |
13032 | |
13033 | if (!opts.OptEnabled(CLFLG_CONSTANTFOLD)) |
13034 | { |
13035 | return tree; |
13036 | } |
13037 | |
13038 | if (tree->OperGet() == GT_NOP) |
13039 | { |
13040 | return tree; |
13041 | } |
13042 | |
13043 | #ifdef FEATURE_SIMD |
13044 | if (tree->OperGet() == GT_SIMD) |
13045 | { |
13046 | return tree; |
13047 | } |
13048 | #endif // FEATURE_SIMD |
13049 | |
13050 | if (tree->gtOper == GT_ALLOCOBJ) |
13051 | { |
13052 | return tree; |
13053 | } |
13054 | |
13055 | if (tree->gtOper == GT_RUNTIMELOOKUP) |
13056 | { |
13057 | return tree; |
13058 | } |
13059 | |
13060 | if (kind & GTK_UNOP) |
13061 | { |
13062 | assert(op1->OperKind() & GTK_CONST); |
13063 | |
13064 | switch (op1->gtType) |
13065 | { |
13066 | case TYP_INT: |
13067 | |
13068 | /* Fold constant INT unary operator */ |
13069 | |
13070 | if (!op1->gtIntCon.ImmedValCanBeFolded(this, tree->OperGet())) |
13071 | { |
13072 | return tree; |
13073 | } |
13074 | |
13075 | i1 = (int)op1->gtIntCon.gtIconVal; |
13076 | |
13077 | // If we fold a unary oper, then the folded constant |
13078 | // is considered a ConstantIndexField if op1 was one |
13079 | // |
13080 | |
13081 | if ((op1->gtIntCon.gtFieldSeq != nullptr) && op1->gtIntCon.gtFieldSeq->IsConstantIndexFieldSeq()) |
13082 | { |
13083 | fieldSeq = op1->gtIntCon.gtFieldSeq; |
13084 | } |
13085 | |
13086 | switch (tree->gtOper) |
13087 | { |
13088 | case GT_NOT: |
13089 | i1 = ~i1; |
13090 | break; |
13091 | |
13092 | case GT_NEG: |
13093 | i1 = -i1; |
13094 | break; |
13095 | |
13096 | case GT_BSWAP: |
13097 | i1 = ((i1 >> 24) & 0xFF) | ((i1 >> 8) & 0xFF00) | ((i1 << 8) & 0xFF0000) | |
13098 | ((i1 << 24) & 0xFF000000); |
13099 | break; |
13100 | |
13101 | case GT_BSWAP16: |
13102 | i1 = ((i1 >> 8) & 0xFF) | ((i1 << 8) & 0xFF00); |
13103 | break; |
13104 | |
13105 | case GT_CAST: |
13106 | // assert (genActualType(tree->CastToType()) == tree->gtType); |
13107 | switch (tree->CastToType()) |
13108 | { |
13109 | case TYP_BYTE: |
13110 | itemp = INT32(INT8(i1)); |
13111 | goto CHK_OVF; |
13112 | |
13113 | case TYP_SHORT: |
13114 | itemp = INT32(INT16(i1)); |
13115 | CHK_OVF: |
13116 | if (tree->gtOverflow() && ((itemp != i1) || ((tree->gtFlags & GTF_UNSIGNED) && i1 < 0))) |
13117 | { |
13118 | goto INT_OVF; |
13119 | } |
13120 | i1 = itemp; |
13121 | goto CNS_INT; |
13122 | |
13123 | case TYP_USHORT: |
13124 | itemp = INT32(UINT16(i1)); |
13125 | if (tree->gtOverflow()) |
13126 | { |
13127 | if (itemp != i1) |
13128 | { |
13129 | goto INT_OVF; |
13130 | } |
13131 | } |
13132 | i1 = itemp; |
13133 | goto CNS_INT; |
13134 | |
13135 | case TYP_BOOL: |
13136 | case TYP_UBYTE: |
13137 | itemp = INT32(UINT8(i1)); |
13138 | if (tree->gtOverflow()) |
13139 | { |
13140 | if (itemp != i1) |
13141 | { |
13142 | goto INT_OVF; |
13143 | } |
13144 | } |
13145 | i1 = itemp; |
13146 | goto CNS_INT; |
13147 | |
13148 | case TYP_UINT: |
13149 | if (!(tree->gtFlags & GTF_UNSIGNED) && tree->gtOverflow() && i1 < 0) |
13150 | { |
13151 | goto INT_OVF; |
13152 | } |
13153 | goto CNS_INT; |
13154 | |
13155 | case TYP_INT: |
13156 | if ((tree->gtFlags & GTF_UNSIGNED) && tree->gtOverflow() && i1 < 0) |
13157 | { |
13158 | goto INT_OVF; |
13159 | } |
13160 | goto CNS_INT; |
13161 | |
13162 | case TYP_ULONG: |
13163 | if (tree->IsUnsigned()) |
13164 | { |
13165 | lval1 = UINT64(UINT32(i1)); |
13166 | } |
13167 | else |
13168 | { |
13169 | if (tree->gtOverflow() && (i1 < 0)) |
13170 | { |
13171 | goto LNG_OVF; |
13172 | } |
13173 | lval1 = UINT64(INT32(i1)); |
13174 | } |
13175 | goto CNS_LONG; |
13176 | |
13177 | case TYP_LONG: |
13178 | if (tree->IsUnsigned()) |
13179 | { |
13180 | lval1 = INT64(UINT32(i1)); |
13181 | } |
13182 | else |
13183 | { |
13184 | lval1 = INT64(INT32(i1)); |
13185 | } |
13186 | goto CNS_LONG; |
13187 | |
13188 | case TYP_FLOAT: |
13189 | if (tree->gtFlags & GTF_UNSIGNED) |
13190 | { |
13191 | f1 = forceCastToFloat(UINT32(i1)); |
13192 | } |
13193 | else |
13194 | { |
13195 | f1 = forceCastToFloat(INT32(i1)); |
13196 | } |
13197 | d1 = f1; |
13198 | goto CNS_DOUBLE; |
13199 | |
13200 | case TYP_DOUBLE: |
13201 | if (tree->gtFlags & GTF_UNSIGNED) |
13202 | { |
13203 | d1 = (double)UINT32(i1); |
13204 | } |
13205 | else |
13206 | { |
13207 | d1 = (double)INT32(i1); |
13208 | } |
13209 | goto CNS_DOUBLE; |
13210 | |
13211 | default: |
13212 | assert(!"BAD_TYP" ); |
13213 | break; |
13214 | } |
13215 | return tree; |
13216 | |
13217 | default: |
13218 | return tree; |
13219 | } |
13220 | |
13221 | goto CNS_INT; |
13222 | |
13223 | case TYP_LONG: |
13224 | |
13225 | /* Fold constant LONG unary operator */ |
13226 | |
13227 | if (!op1->gtIntConCommon.ImmedValCanBeFolded(this, tree->OperGet())) |
13228 | { |
13229 | return tree; |
13230 | } |
13231 | |
13232 | lval1 = op1->gtIntConCommon.LngValue(); |
13233 | |
13234 | switch (tree->gtOper) |
13235 | { |
13236 | case GT_NOT: |
13237 | lval1 = ~lval1; |
13238 | break; |
13239 | |
13240 | case GT_NEG: |
13241 | lval1 = -lval1; |
13242 | break; |
13243 | |
13244 | case GT_BSWAP: |
13245 | lval1 = ((lval1 >> 56) & 0xFF) | ((lval1 >> 40) & 0xFF00) | ((lval1 >> 24) & 0xFF0000) | |
13246 | ((lval1 >> 8) & 0xFF000000) | ((lval1 << 8) & 0xFF00000000) | |
13247 | ((lval1 << 24) & 0xFF0000000000) | ((lval1 << 40) & 0xFF000000000000) | |
13248 | ((lval1 << 56) & 0xFF00000000000000); |
13249 | break; |
13250 | |
13251 | case GT_CAST: |
13252 | assert(genActualType(tree->CastToType()) == tree->gtType); |
13253 | switch (tree->CastToType()) |
13254 | { |
13255 | case TYP_BYTE: |
13256 | i1 = INT32(INT8(lval1)); |
13257 | goto CHECK_INT_OVERFLOW; |
13258 | |
13259 | case TYP_SHORT: |
13260 | i1 = INT32(INT16(lval1)); |
13261 | goto CHECK_INT_OVERFLOW; |
13262 | |
13263 | case TYP_USHORT: |
13264 | i1 = INT32(UINT16(lval1)); |
13265 | goto CHECK_UINT_OVERFLOW; |
13266 | |
13267 | case TYP_UBYTE: |
13268 | i1 = INT32(UINT8(lval1)); |
13269 | goto CHECK_UINT_OVERFLOW; |
13270 | |
13271 | case TYP_INT: |
13272 | i1 = INT32(lval1); |
13273 | |
13274 | CHECK_INT_OVERFLOW: |
13275 | if (tree->gtOverflow()) |
13276 | { |
13277 | if (i1 != lval1) |
13278 | { |
13279 | goto INT_OVF; |
13280 | } |
13281 | if ((tree->gtFlags & GTF_UNSIGNED) && i1 < 0) |
13282 | { |
13283 | goto INT_OVF; |
13284 | } |
13285 | } |
13286 | goto CNS_INT; |
13287 | |
13288 | case TYP_UINT: |
13289 | i1 = UINT32(lval1); |
13290 | |
13291 | CHECK_UINT_OVERFLOW: |
13292 | if (tree->gtOverflow() && UINT32(i1) != lval1) |
13293 | { |
13294 | goto INT_OVF; |
13295 | } |
13296 | goto CNS_INT; |
13297 | |
13298 | case TYP_ULONG: |
13299 | if (!(tree->gtFlags & GTF_UNSIGNED) && tree->gtOverflow() && lval1 < 0) |
13300 | { |
13301 | goto LNG_OVF; |
13302 | } |
13303 | goto CNS_LONG; |
13304 | |
13305 | case TYP_LONG: |
13306 | if ((tree->gtFlags & GTF_UNSIGNED) && tree->gtOverflow() && lval1 < 0) |
13307 | { |
13308 | goto LNG_OVF; |
13309 | } |
13310 | goto CNS_LONG; |
13311 | |
13312 | case TYP_FLOAT: |
13313 | case TYP_DOUBLE: |
13314 | if ((tree->gtFlags & GTF_UNSIGNED) && lval1 < 0) |
13315 | { |
13316 | d1 = FloatingPointUtils::convertUInt64ToDouble((unsigned __int64)lval1); |
13317 | } |
13318 | else |
13319 | { |
13320 | d1 = (double)lval1; |
13321 | } |
13322 | |
13323 | if (tree->CastToType() == TYP_FLOAT) |
13324 | { |
13325 | f1 = forceCastToFloat(d1); // truncate precision |
13326 | d1 = f1; |
13327 | } |
13328 | goto CNS_DOUBLE; |
13329 | default: |
13330 | assert(!"BAD_TYP" ); |
13331 | break; |
13332 | } |
13333 | return tree; |
13334 | |
13335 | default: |
13336 | return tree; |
13337 | } |
13338 | |
13339 | goto CNS_LONG; |
13340 | |
13341 | case TYP_FLOAT: |
13342 | case TYP_DOUBLE: |
13343 | assert(op1->gtOper == GT_CNS_DBL); |
13344 | |
13345 | /* Fold constant DOUBLE unary operator */ |
13346 | |
13347 | d1 = op1->gtDblCon.gtDconVal; |
13348 | |
13349 | switch (tree->gtOper) |
13350 | { |
13351 | case GT_NEG: |
13352 | d1 = -d1; |
13353 | break; |
13354 | |
13355 | case GT_CAST: |
13356 | |
13357 | if (tree->gtOverflowEx()) |
13358 | { |
13359 | return tree; |
13360 | } |
13361 | |
13362 | assert(genActualType(tree->CastToType()) == tree->gtType); |
13363 | |
13364 | if ((op1->gtType == TYP_FLOAT && !_finite(forceCastToFloat(d1))) || |
13365 | (op1->gtType == TYP_DOUBLE && !_finite(d1))) |
13366 | { |
13367 | // The floating point constant is not finite. The ECMA spec says, in |
13368 | // III 3.27, that "...if overflow occurs converting a floating point type |
13369 | // to an integer, ..., the value returned is unspecified." However, it would |
13370 | // at least be desirable to have the same value returned for casting an overflowing |
13371 | // constant to an int as would obtained by passing that constant as a parameter |
13372 | // then casting that parameter to an int type. We will assume that the C compiler's |
13373 | // cast logic will yield the desired result (and trust testing to tell otherwise). |
13374 | // Cross-compilation is an issue here; if that becomes an important scenario, we should |
13375 | // capture the target-specific values of overflow casts to the various integral types as |
13376 | // constants in a target-specific function. |
13377 | CLANG_FORMAT_COMMENT_ANCHOR; |
13378 | |
13379 | // Don't fold conversions of +inf/-inf to integral value on all platforms |
13380 | // as the value returned by JIT helper doesn't match with the C compiler's cast result. |
13381 | // We want the behavior to be same with or without folding. |
13382 | return tree; |
13383 | } |
13384 | |
13385 | if (d1 <= -1.0 && varTypeIsUnsigned(tree->CastToType())) |
13386 | { |
13387 | // Don't fold conversions of these cases becasue the result is unspecified per ECMA spec |
13388 | // and the native math doing the fold doesn't match the run-time computation on all |
13389 | // platforms. |
13390 | // We want the behavior to be same with or without folding. |
13391 | return tree; |
13392 | } |
13393 | |
13394 | switch (tree->CastToType()) |
13395 | { |
13396 | case TYP_BYTE: |
13397 | i1 = INT32(INT8(d1)); |
13398 | goto CNS_INT; |
13399 | |
13400 | case TYP_SHORT: |
13401 | i1 = INT32(INT16(d1)); |
13402 | goto CNS_INT; |
13403 | |
13404 | case TYP_USHORT: |
13405 | i1 = INT32(UINT16(d1)); |
13406 | goto CNS_INT; |
13407 | |
13408 | case TYP_UBYTE: |
13409 | i1 = INT32(UINT8(d1)); |
13410 | goto CNS_INT; |
13411 | |
13412 | case TYP_INT: |
13413 | i1 = INT32(d1); |
13414 | goto CNS_INT; |
13415 | |
13416 | case TYP_UINT: |
13417 | i1 = forceCastToUInt32(d1); |
13418 | goto CNS_INT; |
13419 | |
13420 | case TYP_LONG: |
13421 | lval1 = INT64(d1); |
13422 | goto CNS_LONG; |
13423 | |
13424 | case TYP_ULONG: |
13425 | lval1 = FloatingPointUtils::convertDoubleToUInt64(d1); |
13426 | goto CNS_LONG; |
13427 | |
13428 | case TYP_FLOAT: |
13429 | d1 = forceCastToFloat(d1); |
13430 | goto CNS_DOUBLE; |
13431 | |
13432 | case TYP_DOUBLE: |
13433 | if (op1->gtType == TYP_FLOAT) |
13434 | { |
13435 | d1 = forceCastToFloat(d1); // truncate precision |
13436 | } |
13437 | goto CNS_DOUBLE; // redundant cast |
13438 | |
13439 | default: |
13440 | assert(!"BAD_TYP" ); |
13441 | break; |
13442 | } |
13443 | return tree; |
13444 | |
13445 | default: |
13446 | return tree; |
13447 | } |
13448 | goto CNS_DOUBLE; |
13449 | |
13450 | default: |
13451 | /* not a foldable typ - e.g. RET const */ |
13452 | return tree; |
13453 | } |
13454 | } |
13455 | |
13456 | /* We have a binary operator */ |
13457 | |
13458 | assert(kind & GTK_BINOP); |
13459 | assert(op2); |
13460 | assert(op1->OperKind() & GTK_CONST); |
13461 | assert(op2->OperKind() & GTK_CONST); |
13462 | |
13463 | if (tree->gtOper == GT_COMMA) |
13464 | { |
13465 | return op2; |
13466 | } |
13467 | |
13468 | if (tree->OperIsAnyList()) |
13469 | { |
13470 | return tree; |
13471 | } |
13472 | |
13473 | switchType = op1->gtType; |
13474 | |
13475 | // Normally we will just switch on op1 types, but for the case where |
13476 | // only op2 is a GC type and op1 is not a GC type, we use the op2 type. |
13477 | // This makes us handle this as a case of folding for GC type. |
13478 | // |
13479 | if (varTypeIsGC(op2->gtType) && !varTypeIsGC(op1->gtType)) |
13480 | { |
13481 | switchType = op2->gtType; |
13482 | } |
13483 | |
13484 | switch (switchType) |
13485 | { |
13486 | |
13487 | /*------------------------------------------------------------------------- |
13488 | * Fold constant REF of BYREF binary operator |
13489 | * These can only be comparisons or null pointers |
13490 | */ |
13491 | |
13492 | case TYP_REF: |
13493 | |
13494 | /* String nodes are an RVA at this point */ |
13495 | |
13496 | if (op1->gtOper == GT_CNS_STR || op2->gtOper == GT_CNS_STR) |
13497 | { |
13498 | return tree; |
13499 | } |
13500 | |
13501 | __fallthrough; |
13502 | |
13503 | case TYP_BYREF: |
13504 | |
13505 | i1 = op1->gtIntConCommon.IconValue(); |
13506 | i2 = op2->gtIntConCommon.IconValue(); |
13507 | |
13508 | switch (tree->gtOper) |
13509 | { |
13510 | case GT_EQ: |
13511 | i1 = (i1 == i2); |
13512 | goto FOLD_COND; |
13513 | |
13514 | case GT_NE: |
13515 | i1 = (i1 != i2); |
13516 | goto FOLD_COND; |
13517 | |
13518 | case GT_ADD: |
13519 | noway_assert(tree->gtType != TYP_REF); |
13520 | // We only fold a GT_ADD that involves a null reference. |
13521 | if (((op1->TypeGet() == TYP_REF) && (i1 == 0)) || ((op2->TypeGet() == TYP_REF) && (i2 == 0))) |
13522 | { |
13523 | #ifdef DEBUG |
13524 | if (verbose) |
13525 | { |
13526 | printf("\nFolding operator with constant nodes into a constant:\n" ); |
13527 | gtDispTree(tree); |
13528 | } |
13529 | #endif |
13530 | // Fold into GT_IND of null byref |
13531 | tree->ChangeOperConst(GT_CNS_INT); |
13532 | tree->gtType = TYP_BYREF; |
13533 | tree->gtIntCon.gtIconVal = 0; |
13534 | tree->gtIntCon.gtFieldSeq = FieldSeqStore::NotAField(); |
13535 | if (vnStore != nullptr) |
13536 | { |
13537 | fgValueNumberTreeConst(tree); |
13538 | } |
13539 | #ifdef DEBUG |
13540 | if (verbose) |
13541 | { |
13542 | printf("\nFolded to null byref:\n" ); |
13543 | gtDispTree(tree); |
13544 | } |
13545 | #endif |
13546 | goto DONE; |
13547 | } |
13548 | |
13549 | default: |
13550 | break; |
13551 | } |
13552 | |
13553 | return tree; |
13554 | |
13555 | /*------------------------------------------------------------------------- |
13556 | * Fold constant INT binary operator |
13557 | */ |
13558 | |
13559 | case TYP_INT: |
13560 | |
13561 | if (tree->OperIsCompare() && (tree->gtType == TYP_BYTE)) |
13562 | { |
13563 | tree->gtType = TYP_INT; |
13564 | } |
13565 | |
13566 | assert(tree->gtType == TYP_INT || varTypeIsGC(tree->TypeGet()) || tree->gtOper == GT_MKREFANY); |
13567 | |
13568 | // No GC pointer types should be folded here... |
13569 | // |
13570 | assert(!varTypeIsGC(op1->gtType) && !varTypeIsGC(op2->gtType)); |
13571 | |
13572 | if (!op1->gtIntConCommon.ImmedValCanBeFolded(this, tree->OperGet())) |
13573 | { |
13574 | return tree; |
13575 | } |
13576 | |
13577 | if (!op2->gtIntConCommon.ImmedValCanBeFolded(this, tree->OperGet())) |
13578 | { |
13579 | return tree; |
13580 | } |
13581 | |
13582 | i1 = op1->gtIntConCommon.IconValue(); |
13583 | i2 = op2->gtIntConCommon.IconValue(); |
13584 | |
13585 | switch (tree->gtOper) |
13586 | { |
13587 | case GT_EQ: |
13588 | i1 = (INT32(i1) == INT32(i2)); |
13589 | break; |
13590 | case GT_NE: |
13591 | i1 = (INT32(i1) != INT32(i2)); |
13592 | break; |
13593 | |
13594 | case GT_LT: |
13595 | if (tree->gtFlags & GTF_UNSIGNED) |
13596 | { |
13597 | i1 = (UINT32(i1) < UINT32(i2)); |
13598 | } |
13599 | else |
13600 | { |
13601 | i1 = (INT32(i1) < INT32(i2)); |
13602 | } |
13603 | break; |
13604 | |
13605 | case GT_LE: |
13606 | if (tree->gtFlags & GTF_UNSIGNED) |
13607 | { |
13608 | i1 = (UINT32(i1) <= UINT32(i2)); |
13609 | } |
13610 | else |
13611 | { |
13612 | i1 = (INT32(i1) <= INT32(i2)); |
13613 | } |
13614 | break; |
13615 | |
13616 | case GT_GE: |
13617 | if (tree->gtFlags & GTF_UNSIGNED) |
13618 | { |
13619 | i1 = (UINT32(i1) >= UINT32(i2)); |
13620 | } |
13621 | else |
13622 | { |
13623 | i1 = (INT32(i1) >= INT32(i2)); |
13624 | } |
13625 | break; |
13626 | |
13627 | case GT_GT: |
13628 | if (tree->gtFlags & GTF_UNSIGNED) |
13629 | { |
13630 | i1 = (UINT32(i1) > UINT32(i2)); |
13631 | } |
13632 | else |
13633 | { |
13634 | i1 = (INT32(i1) > INT32(i2)); |
13635 | } |
13636 | break; |
13637 | |
13638 | case GT_ADD: |
13639 | itemp = i1 + i2; |
13640 | if (tree->gtOverflow()) |
13641 | { |
13642 | if (tree->gtFlags & GTF_UNSIGNED) |
13643 | { |
13644 | if (INT64(UINT32(itemp)) != INT64(UINT32(i1)) + INT64(UINT32(i2))) |
13645 | { |
13646 | goto INT_OVF; |
13647 | } |
13648 | } |
13649 | else |
13650 | { |
13651 | if (INT64(INT32(itemp)) != INT64(INT32(i1)) + INT64(INT32(i2))) |
13652 | { |
13653 | goto INT_OVF; |
13654 | } |
13655 | } |
13656 | } |
13657 | i1 = itemp; |
13658 | fieldSeq = GetFieldSeqStore()->Append(op1->gtIntCon.gtFieldSeq, op2->gtIntCon.gtFieldSeq); |
13659 | break; |
13660 | case GT_SUB: |
13661 | itemp = i1 - i2; |
13662 | if (tree->gtOverflow()) |
13663 | { |
13664 | if (tree->gtFlags & GTF_UNSIGNED) |
13665 | { |
13666 | if (INT64(UINT32(itemp)) != ((INT64)((UINT32)i1) - (INT64)((UINT32)i2))) |
13667 | { |
13668 | goto INT_OVF; |
13669 | } |
13670 | } |
13671 | else |
13672 | { |
13673 | if (INT64(INT32(itemp)) != INT64(INT32(i1)) - INT64(INT32(i2))) |
13674 | { |
13675 | goto INT_OVF; |
13676 | } |
13677 | } |
13678 | } |
13679 | i1 = itemp; |
13680 | break; |
13681 | case GT_MUL: |
13682 | itemp = i1 * i2; |
13683 | if (tree->gtOverflow()) |
13684 | { |
13685 | if (tree->gtFlags & GTF_UNSIGNED) |
13686 | { |
13687 | if (INT64(UINT32(itemp)) != ((INT64)((UINT32)i1) * (INT64)((UINT32)i2))) |
13688 | { |
13689 | goto INT_OVF; |
13690 | } |
13691 | } |
13692 | else |
13693 | { |
13694 | if (INT64(INT32(itemp)) != INT64(INT32(i1)) * INT64(INT32(i2))) |
13695 | { |
13696 | goto INT_OVF; |
13697 | } |
13698 | } |
13699 | } |
13700 | // For the very particular case of the "constant array index" pseudo-field, we |
13701 | // assume that multiplication is by the field width, and preserves that field. |
13702 | // This could obviously be made more robust by a more complicated set of annotations... |
13703 | if ((op1->gtIntCon.gtFieldSeq != nullptr) && op1->gtIntCon.gtFieldSeq->IsConstantIndexFieldSeq()) |
13704 | { |
13705 | assert(op2->gtIntCon.gtFieldSeq == FieldSeqStore::NotAField()); |
13706 | fieldSeq = op1->gtIntCon.gtFieldSeq; |
13707 | } |
13708 | else if ((op2->gtIntCon.gtFieldSeq != nullptr) && |
13709 | op2->gtIntCon.gtFieldSeq->IsConstantIndexFieldSeq()) |
13710 | { |
13711 | assert(op1->gtIntCon.gtFieldSeq == FieldSeqStore::NotAField()); |
13712 | fieldSeq = op2->gtIntCon.gtFieldSeq; |
13713 | } |
13714 | i1 = itemp; |
13715 | break; |
13716 | |
13717 | case GT_OR: |
13718 | i1 |= i2; |
13719 | break; |
13720 | case GT_XOR: |
13721 | i1 ^= i2; |
13722 | break; |
13723 | case GT_AND: |
13724 | i1 &= i2; |
13725 | break; |
13726 | |
13727 | case GT_LSH: |
13728 | i1 <<= (i2 & 0x1f); |
13729 | break; |
13730 | case GT_RSH: |
13731 | i1 >>= (i2 & 0x1f); |
13732 | break; |
13733 | case GT_RSZ: |
13734 | /* logical shift -> make it unsigned to not propagate the sign bit */ |
13735 | i1 = UINT32(i1) >> (i2 & 0x1f); |
13736 | break; |
13737 | case GT_ROL: |
13738 | i1 = (i1 << (i2 & 0x1f)) | (UINT32(i1) >> ((32 - i2) & 0x1f)); |
13739 | break; |
13740 | case GT_ROR: |
13741 | i1 = (i1 << ((32 - i2) & 0x1f)) | (UINT32(i1) >> (i2 & 0x1f)); |
13742 | break; |
13743 | |
13744 | /* DIV and MOD can generate an INT 0 - if division by 0 |
13745 | * or overflow - when dividing MIN by -1 */ |
13746 | |
13747 | case GT_DIV: |
13748 | case GT_MOD: |
13749 | case GT_UDIV: |
13750 | case GT_UMOD: |
13751 | if (INT32(i2) == 0) |
13752 | { |
13753 | // Division by zero: |
13754 | // We have to evaluate this expression and throw an exception |
13755 | return tree; |
13756 | } |
13757 | else if ((INT32(i2) == -1) && (UINT32(i1) == 0x80000000)) |
13758 | { |
13759 | // Overflow Division: |
13760 | // We have to evaluate this expression and throw an exception |
13761 | return tree; |
13762 | } |
13763 | |
13764 | if (tree->gtOper == GT_DIV) |
13765 | { |
13766 | i1 = INT32(i1) / INT32(i2); |
13767 | } |
13768 | else if (tree->gtOper == GT_MOD) |
13769 | { |
13770 | i1 = INT32(i1) % INT32(i2); |
13771 | } |
13772 | else if (tree->gtOper == GT_UDIV) |
13773 | { |
13774 | i1 = UINT32(i1) / UINT32(i2); |
13775 | } |
13776 | else |
13777 | { |
13778 | assert(tree->gtOper == GT_UMOD); |
13779 | i1 = UINT32(i1) % UINT32(i2); |
13780 | } |
13781 | break; |
13782 | |
13783 | default: |
13784 | return tree; |
13785 | } |
13786 | |
13787 | /* We get here after folding to a GT_CNS_INT type |
13788 | * change the node to the new type / value and make sure the node sizes are OK */ |
13789 | CNS_INT: |
13790 | FOLD_COND: |
13791 | |
13792 | #ifdef DEBUG |
13793 | if (verbose) |
13794 | { |
13795 | printf("\nFolding operator with constant nodes into a constant:\n" ); |
13796 | gtDispTree(tree); |
13797 | } |
13798 | #endif |
13799 | |
13800 | #ifdef _TARGET_64BIT_ |
13801 | // Some operations are performed as 64 bit instead of 32 bit so the upper 32 bits |
13802 | // need to be discarded. Since constant values are stored as ssize_t and the node |
13803 | // has TYP_INT the result needs to be sign extended rather than zero extended. |
13804 | i1 = INT32(i1); |
13805 | #endif // _TARGET_64BIT_ |
13806 | |
13807 | /* Also all conditional folding jumps here since the node hanging from |
13808 | * GT_JTRUE has to be a GT_CNS_INT - value 0 or 1 */ |
13809 | |
13810 | tree->ChangeOperConst(GT_CNS_INT); |
13811 | tree->gtType = TYP_INT; |
13812 | tree->gtIntCon.gtIconVal = i1; |
13813 | tree->gtIntCon.gtFieldSeq = fieldSeq; |
13814 | if (vnStore != nullptr) |
13815 | { |
13816 | fgValueNumberTreeConst(tree); |
13817 | } |
13818 | #ifdef DEBUG |
13819 | if (verbose) |
13820 | { |
13821 | printf("Bashed to int constant:\n" ); |
13822 | gtDispTree(tree); |
13823 | } |
13824 | #endif |
13825 | goto DONE; |
13826 | |
13827 | /* This operation is going to cause an overflow exception. Morph into |
13828 | an overflow helper. Put a dummy constant value for code generation. |
13829 | |
13830 | We could remove all subsequent trees in the current basic block, |
13831 | unless this node is a child of GT_COLON |
13832 | |
13833 | NOTE: Since the folded value is not constant we should not change the |
13834 | "tree" node - otherwise we confuse the logic that checks if the folding |
13835 | was successful - instead use one of the operands, e.g. op1 |
13836 | */ |
13837 | |
13838 | LNG_OVF: |
13839 | // Don't fold overflow operations if not global morph phase. |
13840 | // The reason for this is that this optimization is replacing a gentree node |
13841 | // with another new gentree node. Say a GT_CALL(arglist) has one 'arg' |
13842 | // involving overflow arithmetic. During assertion prop, it is possible |
13843 | // that the 'arg' could be constant folded and the result could lead to an |
13844 | // overflow. In such a case 'arg' will get replaced with GT_COMMA node |
13845 | // but fgMorphArgs() - see the logic around "if(lateArgsComputed)" - doesn't |
13846 | // update args table. For this reason this optimization is enabled only |
13847 | // for global morphing phase. |
13848 | // |
13849 | // TODO-CQ: Once fgMorphArgs() is fixed this restriction could be removed. |
13850 | CLANG_FORMAT_COMMENT_ANCHOR; |
13851 | |
13852 | if (!fgGlobalMorph) |
13853 | { |
13854 | assert(tree->gtOverflow()); |
13855 | return tree; |
13856 | } |
13857 | |
13858 | op1 = gtNewLconNode(0); |
13859 | if (vnStore != nullptr) |
13860 | { |
13861 | op1->gtVNPair.SetBoth(vnStore->VNZeroForType(TYP_LONG)); |
13862 | } |
13863 | goto OVF; |
13864 | |
13865 | INT_OVF: |
13866 | // Don't fold overflow operations if not global morph phase. |
13867 | // The reason for this is that this optimization is replacing a gentree node |
13868 | // with another new gentree node. Say a GT_CALL(arglist) has one 'arg' |
13869 | // involving overflow arithmetic. During assertion prop, it is possible |
13870 | // that the 'arg' could be constant folded and the result could lead to an |
13871 | // overflow. In such a case 'arg' will get replaced with GT_COMMA node |
13872 | // but fgMorphArgs() - see the logic around "if(lateArgsComputed)" - doesn't |
13873 | // update args table. For this reason this optimization is enabled only |
13874 | // for global morphing phase. |
13875 | // |
13876 | // TODO-CQ: Once fgMorphArgs() is fixed this restriction could be removed. |
13877 | |
13878 | if (!fgGlobalMorph) |
13879 | { |
13880 | assert(tree->gtOverflow()); |
13881 | return tree; |
13882 | } |
13883 | |
13884 | op1 = gtNewIconNode(0); |
13885 | if (vnStore != nullptr) |
13886 | { |
13887 | op1->gtVNPair.SetBoth(vnStore->VNZeroForType(TYP_INT)); |
13888 | } |
13889 | goto OVF; |
13890 | |
13891 | OVF: |
13892 | #ifdef DEBUG |
13893 | if (verbose) |
13894 | { |
13895 | printf("\nFolding binary operator with constant nodes into a comma throw:\n" ); |
13896 | gtDispTree(tree); |
13897 | } |
13898 | #endif |
13899 | /* We will change the cast to a GT_COMMA and attach the exception helper as gtOp.gtOp1. |
13900 | * The constant expression zero becomes op2. */ |
13901 | |
13902 | assert(tree->gtOverflow()); |
13903 | assert(tree->gtOper == GT_ADD || tree->gtOper == GT_SUB || tree->gtOper == GT_CAST || |
13904 | tree->gtOper == GT_MUL); |
13905 | assert(op1); |
13906 | |
13907 | op2 = op1; |
13908 | op1 = gtNewHelperCallNode(CORINFO_HELP_OVERFLOW, TYP_VOID, |
13909 | gtNewArgList(gtNewIconNode(compCurBB->bbTryIndex))); |
13910 | |
13911 | // op1 is a call to the JIT helper that throws an Overflow exception |
13912 | // attach the ExcSet for VNF_OverflowExc(Void) to this call |
13913 | |
13914 | if (vnStore != nullptr) |
13915 | { |
13916 | op1->gtVNPair = |
13917 | vnStore->VNPWithExc(ValueNumPair(ValueNumStore::VNForVoid(), ValueNumStore::VNForVoid()), |
13918 | vnStore->VNPExcSetSingleton( |
13919 | vnStore->VNPairForFunc(TYP_REF, VNF_OverflowExc, vnStore->VNPForVoid()))); |
13920 | } |
13921 | |
13922 | tree = gtNewOperNode(GT_COMMA, tree->gtType, op1, op2); |
13923 | |
13924 | return tree; |
13925 | |
13926 | /*------------------------------------------------------------------------- |
13927 | * Fold constant LONG binary operator |
13928 | */ |
13929 | |
13930 | case TYP_LONG: |
13931 | |
13932 | // No GC pointer types should be folded here... |
13933 | // |
13934 | assert(!varTypeIsGC(op1->gtType) && !varTypeIsGC(op2->gtType)); |
13935 | |
13936 | // op1 is known to be a TYP_LONG, op2 is normally a TYP_LONG, unless we have a shift operator in which case |
13937 | // it is a TYP_INT |
13938 | // |
13939 | assert((op2->gtType == TYP_LONG) || (op2->gtType == TYP_INT)); |
13940 | |
13941 | if (!op1->gtIntConCommon.ImmedValCanBeFolded(this, tree->OperGet())) |
13942 | { |
13943 | return tree; |
13944 | } |
13945 | |
13946 | if (!op2->gtIntConCommon.ImmedValCanBeFolded(this, tree->OperGet())) |
13947 | { |
13948 | return tree; |
13949 | } |
13950 | |
13951 | lval1 = op1->gtIntConCommon.LngValue(); |
13952 | |
13953 | // For the shift operators we can have a op2 that is a TYP_INT and thus will be GT_CNS_INT |
13954 | if (op2->OperGet() == GT_CNS_INT) |
13955 | { |
13956 | lval2 = op2->gtIntConCommon.IconValue(); |
13957 | } |
13958 | else |
13959 | { |
13960 | lval2 = op2->gtIntConCommon.LngValue(); |
13961 | } |
13962 | |
13963 | switch (tree->gtOper) |
13964 | { |
13965 | case GT_EQ: |
13966 | i1 = (lval1 == lval2); |
13967 | goto FOLD_COND; |
13968 | case GT_NE: |
13969 | i1 = (lval1 != lval2); |
13970 | goto FOLD_COND; |
13971 | |
13972 | case GT_LT: |
13973 | if (tree->gtFlags & GTF_UNSIGNED) |
13974 | { |
13975 | i1 = (UINT64(lval1) < UINT64(lval2)); |
13976 | } |
13977 | else |
13978 | { |
13979 | i1 = (lval1 < lval2); |
13980 | } |
13981 | goto FOLD_COND; |
13982 | |
13983 | case GT_LE: |
13984 | if (tree->gtFlags & GTF_UNSIGNED) |
13985 | { |
13986 | i1 = (UINT64(lval1) <= UINT64(lval2)); |
13987 | } |
13988 | else |
13989 | { |
13990 | i1 = (lval1 <= lval2); |
13991 | } |
13992 | goto FOLD_COND; |
13993 | |
13994 | case GT_GE: |
13995 | if (tree->gtFlags & GTF_UNSIGNED) |
13996 | { |
13997 | i1 = (UINT64(lval1) >= UINT64(lval2)); |
13998 | } |
13999 | else |
14000 | { |
14001 | i1 = (lval1 >= lval2); |
14002 | } |
14003 | goto FOLD_COND; |
14004 | |
14005 | case GT_GT: |
14006 | if (tree->gtFlags & GTF_UNSIGNED) |
14007 | { |
14008 | i1 = (UINT64(lval1) > UINT64(lval2)); |
14009 | } |
14010 | else |
14011 | { |
14012 | i1 = (lval1 > lval2); |
14013 | } |
14014 | goto FOLD_COND; |
14015 | |
14016 | case GT_ADD: |
14017 | ltemp = lval1 + lval2; |
14018 | |
14019 | LNG_ADD_CHKOVF: |
14020 | /* For the SIGNED case - If there is one positive and one negative operand, there can be no overflow |
14021 | * If both are positive, the result has to be positive, and similary for negatives. |
14022 | * |
14023 | * For the UNSIGNED case - If a UINT32 operand is bigger than the result then OVF */ |
14024 | |
14025 | if (tree->gtOverflow()) |
14026 | { |
14027 | if (tree->gtFlags & GTF_UNSIGNED) |
14028 | { |
14029 | if ((UINT64(lval1) > UINT64(ltemp)) || (UINT64(lval2) > UINT64(ltemp))) |
14030 | { |
14031 | goto LNG_OVF; |
14032 | } |
14033 | } |
14034 | else if (((lval1 < 0) == (lval2 < 0)) && ((lval1 < 0) != (ltemp < 0))) |
14035 | { |
14036 | goto LNG_OVF; |
14037 | } |
14038 | } |
14039 | lval1 = ltemp; |
14040 | break; |
14041 | |
14042 | case GT_SUB: |
14043 | ltemp = lval1 - lval2; |
14044 | if (tree->gtOverflow()) |
14045 | { |
14046 | if (tree->gtFlags & GTF_UNSIGNED) |
14047 | { |
14048 | if (UINT64(lval2) > UINT64(lval1)) |
14049 | { |
14050 | goto LNG_OVF; |
14051 | } |
14052 | } |
14053 | else |
14054 | { |
14055 | /* If both operands are +ve or both are -ve, there can be no |
14056 | overflow. Else use the logic for : lval1 + (-lval2) */ |
14057 | |
14058 | if ((lval1 < 0) != (lval2 < 0)) |
14059 | { |
14060 | if (lval2 == INT64_MIN) |
14061 | { |
14062 | goto LNG_OVF; |
14063 | } |
14064 | lval2 = -lval2; |
14065 | goto LNG_ADD_CHKOVF; |
14066 | } |
14067 | } |
14068 | } |
14069 | lval1 = ltemp; |
14070 | break; |
14071 | |
14072 | case GT_MUL: |
14073 | ltemp = lval1 * lval2; |
14074 | |
14075 | if (tree->gtOverflow() && lval2 != 0) |
14076 | { |
14077 | |
14078 | if (tree->gtFlags & GTF_UNSIGNED) |
14079 | { |
14080 | UINT64 ultemp = ltemp; |
14081 | UINT64 ulval1 = lval1; |
14082 | UINT64 ulval2 = lval2; |
14083 | if ((ultemp / ulval2) != ulval1) |
14084 | { |
14085 | goto LNG_OVF; |
14086 | } |
14087 | } |
14088 | else |
14089 | { |
14090 | // This does a multiply and then reverses it. This test works great except for MIN_INT * |
14091 | //-1. In that case we mess up the sign on ltmp. Make sure to double check the sign. |
14092 | // if either is 0, then no overflow |
14093 | if (lval1 != 0) // lval2 checked above. |
14094 | { |
14095 | if (((lval1 < 0) == (lval2 < 0)) && (ltemp < 0)) |
14096 | { |
14097 | goto LNG_OVF; |
14098 | } |
14099 | if (((lval1 < 0) != (lval2 < 0)) && (ltemp > 0)) |
14100 | { |
14101 | goto LNG_OVF; |
14102 | } |
14103 | |
14104 | // TODO-Amd64-Unix: Remove the code that disables optimizations for this method when the |
14105 | // clang |
14106 | // optimizer is fixed and/or the method implementation is refactored in a simpler code. |
14107 | // There is a bug in the clang-3.5 optimizer. The issue is that in release build the |
14108 | // optimizer is mistyping (or just wrongly decides to use 32 bit operation for a corner |
14109 | // case of MIN_LONG) the args of the (ltemp / lval2) to int (it does a 32 bit div |
14110 | // operation instead of 64 bit.). For the case of lval1 and lval2 equal to MIN_LONG |
14111 | // (0x8000000000000000) this results in raising a SIGFPE. |
14112 | // Optimizations disabled for now. See compiler.h. |
14113 | if ((ltemp / lval2) != lval1) |
14114 | { |
14115 | goto LNG_OVF; |
14116 | } |
14117 | } |
14118 | } |
14119 | } |
14120 | |
14121 | lval1 = ltemp; |
14122 | break; |
14123 | |
14124 | case GT_OR: |
14125 | lval1 |= lval2; |
14126 | break; |
14127 | case GT_XOR: |
14128 | lval1 ^= lval2; |
14129 | break; |
14130 | case GT_AND: |
14131 | lval1 &= lval2; |
14132 | break; |
14133 | |
14134 | case GT_LSH: |
14135 | lval1 <<= (lval2 & 0x3f); |
14136 | break; |
14137 | case GT_RSH: |
14138 | lval1 >>= (lval2 & 0x3f); |
14139 | break; |
14140 | case GT_RSZ: |
14141 | /* logical shift -> make it unsigned to not propagate the sign bit */ |
14142 | lval1 = UINT64(lval1) >> (lval2 & 0x3f); |
14143 | break; |
14144 | case GT_ROL: |
14145 | lval1 = (lval1 << (lval2 & 0x3f)) | (UINT64(lval1) >> ((64 - lval2) & 0x3f)); |
14146 | break; |
14147 | case GT_ROR: |
14148 | lval1 = (lval1 << ((64 - lval2) & 0x3f)) | (UINT64(lval1) >> (lval2 & 0x3f)); |
14149 | break; |
14150 | |
14151 | // Both DIV and IDIV on x86 raise an exception for min_int (and min_long) / -1. So we preserve |
14152 | // that behavior here. |
14153 | case GT_DIV: |
14154 | if (!lval2) |
14155 | { |
14156 | return tree; |
14157 | } |
14158 | |
14159 | if (UINT64(lval1) == UI64(0x8000000000000000) && lval2 == INT64(-1)) |
14160 | { |
14161 | return tree; |
14162 | } |
14163 | lval1 /= lval2; |
14164 | break; |
14165 | |
14166 | case GT_MOD: |
14167 | if (!lval2) |
14168 | { |
14169 | return tree; |
14170 | } |
14171 | if (UINT64(lval1) == UI64(0x8000000000000000) && lval2 == INT64(-1)) |
14172 | { |
14173 | return tree; |
14174 | } |
14175 | lval1 %= lval2; |
14176 | break; |
14177 | |
14178 | case GT_UDIV: |
14179 | if (!lval2) |
14180 | { |
14181 | return tree; |
14182 | } |
14183 | if (UINT64(lval1) == UI64(0x8000000000000000) && lval2 == INT64(-1)) |
14184 | { |
14185 | return tree; |
14186 | } |
14187 | lval1 = UINT64(lval1) / UINT64(lval2); |
14188 | break; |
14189 | |
14190 | case GT_UMOD: |
14191 | if (!lval2) |
14192 | { |
14193 | return tree; |
14194 | } |
14195 | if (UINT64(lval1) == UI64(0x8000000000000000) && lval2 == INT64(-1)) |
14196 | { |
14197 | return tree; |
14198 | } |
14199 | lval1 = UINT64(lval1) % UINT64(lval2); |
14200 | break; |
14201 | default: |
14202 | return tree; |
14203 | } |
14204 | |
14205 | CNS_LONG: |
14206 | |
14207 | if (fieldSeq != FieldSeqStore::NotAField()) |
14208 | { |
14209 | return tree; |
14210 | } |
14211 | |
14212 | #ifdef DEBUG |
14213 | if (verbose) |
14214 | { |
14215 | printf("\nFolding long operator with constant nodes into a constant:\n" ); |
14216 | gtDispTree(tree); |
14217 | } |
14218 | #endif |
14219 | assert((GenTree::s_gtNodeSizes[GT_CNS_NATIVELONG] == TREE_NODE_SZ_SMALL) || |
14220 | (tree->gtDebugFlags & GTF_DEBUG_NODE_LARGE)); |
14221 | |
14222 | tree->ChangeOperConst(GT_CNS_NATIVELONG); |
14223 | tree->gtIntConCommon.SetLngValue(lval1); |
14224 | if (vnStore != nullptr) |
14225 | { |
14226 | fgValueNumberTreeConst(tree); |
14227 | } |
14228 | |
14229 | #ifdef DEBUG |
14230 | if (verbose) |
14231 | { |
14232 | printf("Bashed to long constant:\n" ); |
14233 | gtDispTree(tree); |
14234 | } |
14235 | #endif |
14236 | goto DONE; |
14237 | |
14238 | /*------------------------------------------------------------------------- |
14239 | * Fold constant FLOAT or DOUBLE binary operator |
14240 | */ |
14241 | |
14242 | case TYP_FLOAT: |
14243 | case TYP_DOUBLE: |
14244 | |
14245 | if (tree->gtOverflowEx()) |
14246 | { |
14247 | return tree; |
14248 | } |
14249 | |
14250 | assert(op1->gtOper == GT_CNS_DBL); |
14251 | d1 = op1->gtDblCon.gtDconVal; |
14252 | |
14253 | assert(varTypeIsFloating(op2->gtType)); |
14254 | assert(op2->gtOper == GT_CNS_DBL); |
14255 | d2 = op2->gtDblCon.gtDconVal; |
14256 | |
14257 | /* Special case - check if we have NaN operands. |
14258 | * For comparisons if not an unordered operation always return 0. |
14259 | * For unordered operations (i.e. the GTF_RELOP_NAN_UN flag is set) |
14260 | * the result is always true - return 1. */ |
14261 | |
14262 | if (_isnan(d1) || _isnan(d2)) |
14263 | { |
14264 | #ifdef DEBUG |
14265 | if (verbose) |
14266 | { |
14267 | printf("Double operator(s) is NaN\n" ); |
14268 | } |
14269 | #endif |
14270 | if (tree->OperKind() & GTK_RELOP) |
14271 | { |
14272 | if (tree->gtFlags & GTF_RELOP_NAN_UN) |
14273 | { |
14274 | /* Unordered comparison with NaN always succeeds */ |
14275 | i1 = 1; |
14276 | goto FOLD_COND; |
14277 | } |
14278 | else |
14279 | { |
14280 | /* Normal comparison with NaN always fails */ |
14281 | i1 = 0; |
14282 | goto FOLD_COND; |
14283 | } |
14284 | } |
14285 | } |
14286 | |
14287 | switch (tree->gtOper) |
14288 | { |
14289 | case GT_EQ: |
14290 | i1 = (d1 == d2); |
14291 | goto FOLD_COND; |
14292 | case GT_NE: |
14293 | i1 = (d1 != d2); |
14294 | goto FOLD_COND; |
14295 | |
14296 | case GT_LT: |
14297 | i1 = (d1 < d2); |
14298 | goto FOLD_COND; |
14299 | case GT_LE: |
14300 | i1 = (d1 <= d2); |
14301 | goto FOLD_COND; |
14302 | case GT_GE: |
14303 | i1 = (d1 >= d2); |
14304 | goto FOLD_COND; |
14305 | case GT_GT: |
14306 | i1 = (d1 > d2); |
14307 | goto FOLD_COND; |
14308 | |
14309 | // Floating point arithmetic should be done in declared |
14310 | // precision while doing constant folding. For this reason though TYP_FLOAT |
14311 | // constants are stored as double constants, while performing float arithmetic, |
14312 | // double constants should be converted to float. Here is an example case |
14313 | // where performing arithmetic in double precision would lead to incorrect |
14314 | // results. |
14315 | // |
14316 | // Example: |
14317 | // float a = float.MaxValue; |
14318 | // float b = a*a; This will produce +inf in single precision and 1.1579207543382391e+077 in double |
14319 | // precision. |
14320 | // flaot c = b/b; This will produce NaN in single precision and 1 in double precision. |
14321 | case GT_ADD: |
14322 | if (op1->TypeGet() == TYP_FLOAT) |
14323 | { |
14324 | f1 = forceCastToFloat(d1); |
14325 | f2 = forceCastToFloat(d2); |
14326 | d1 = forceCastToFloat(f1 + f2); |
14327 | } |
14328 | else |
14329 | { |
14330 | d1 += d2; |
14331 | } |
14332 | break; |
14333 | |
14334 | case GT_SUB: |
14335 | if (op1->TypeGet() == TYP_FLOAT) |
14336 | { |
14337 | f1 = forceCastToFloat(d1); |
14338 | f2 = forceCastToFloat(d2); |
14339 | d1 = forceCastToFloat(f1 - f2); |
14340 | } |
14341 | else |
14342 | { |
14343 | d1 -= d2; |
14344 | } |
14345 | break; |
14346 | |
14347 | case GT_MUL: |
14348 | if (op1->TypeGet() == TYP_FLOAT) |
14349 | { |
14350 | f1 = forceCastToFloat(d1); |
14351 | f2 = forceCastToFloat(d2); |
14352 | d1 = forceCastToFloat(f1 * f2); |
14353 | } |
14354 | else |
14355 | { |
14356 | d1 *= d2; |
14357 | } |
14358 | break; |
14359 | |
14360 | case GT_DIV: |
14361 | if (!d2) |
14362 | { |
14363 | return tree; |
14364 | } |
14365 | if (op1->TypeGet() == TYP_FLOAT) |
14366 | { |
14367 | f1 = forceCastToFloat(d1); |
14368 | f2 = forceCastToFloat(d2); |
14369 | d1 = forceCastToFloat(f1 / f2); |
14370 | } |
14371 | else |
14372 | { |
14373 | d1 /= d2; |
14374 | } |
14375 | break; |
14376 | |
14377 | default: |
14378 | return tree; |
14379 | } |
14380 | |
14381 | CNS_DOUBLE: |
14382 | |
14383 | #ifdef DEBUG |
14384 | if (verbose) |
14385 | { |
14386 | printf("\nFolding fp operator with constant nodes into a fp constant:\n" ); |
14387 | gtDispTree(tree); |
14388 | } |
14389 | #endif |
14390 | |
14391 | assert((GenTree::s_gtNodeSizes[GT_CNS_DBL] == TREE_NODE_SZ_SMALL) || |
14392 | (tree->gtDebugFlags & GTF_DEBUG_NODE_LARGE)); |
14393 | |
14394 | tree->ChangeOperConst(GT_CNS_DBL); |
14395 | tree->gtDblCon.gtDconVal = d1; |
14396 | if (vnStore != nullptr) |
14397 | { |
14398 | fgValueNumberTreeConst(tree); |
14399 | } |
14400 | #ifdef DEBUG |
14401 | if (verbose) |
14402 | { |
14403 | printf("Bashed to fp constant:\n" ); |
14404 | gtDispTree(tree); |
14405 | } |
14406 | #endif |
14407 | goto DONE; |
14408 | |
14409 | default: |
14410 | /* not a foldable typ */ |
14411 | return tree; |
14412 | } |
14413 | |
14414 | //------------------------------------------------------------------------- |
14415 | |
14416 | DONE: |
14417 | |
14418 | /* Make sure no side effect flags are set on this constant node */ |
14419 | |
14420 | tree->gtFlags &= ~GTF_ALL_EFFECT; |
14421 | |
14422 | return tree; |
14423 | } |
14424 | #ifdef _PREFAST_ |
14425 | #pragma warning(pop) |
14426 | #endif |
14427 | |
14428 | //------------------------------------------------------------------------ |
14429 | // gtNewTempAssign: Create an assignment of the given value to a temp. |
14430 | // |
14431 | // Arguments: |
14432 | // tmp - local number for a compiler temp |
14433 | // val - value to assign to the temp |
14434 | // pAfterStmt - statement to insert any additional statements after |
14435 | // ilOffset - il offset for new statements |
14436 | // block - block to insert any additional statements in |
14437 | // |
14438 | // Return Value: |
14439 | // Normally a new assignment node. |
14440 | // However may return a nop node if val is simply a reference to the temp. |
14441 | // |
14442 | // Notes: |
14443 | // Self-assignments may be represented via NOPs. |
14444 | // |
14445 | // May update the type of the temp, if it was previously unknown. |
14446 | // |
14447 | // May set compFloatingPointUsed. |
14448 | |
14449 | GenTree* Compiler::gtNewTempAssign( |
14450 | unsigned tmp, GenTree* val, GenTree** pAfterStmt, IL_OFFSETX ilOffset, BasicBlock* block) |
14451 | { |
14452 | // Self-assignment is a nop. |
14453 | if (val->OperGet() == GT_LCL_VAR && val->gtLclVarCommon.gtLclNum == tmp) |
14454 | { |
14455 | return gtNewNothingNode(); |
14456 | } |
14457 | |
14458 | LclVarDsc* varDsc = lvaTable + tmp; |
14459 | |
14460 | if (varDsc->TypeGet() == TYP_I_IMPL && val->TypeGet() == TYP_BYREF) |
14461 | { |
14462 | impBashVarAddrsToI(val); |
14463 | } |
14464 | |
14465 | var_types valTyp = val->TypeGet(); |
14466 | if (val->OperGet() == GT_LCL_VAR && lvaTable[val->gtLclVar.gtLclNum].lvNormalizeOnLoad()) |
14467 | { |
14468 | valTyp = lvaGetRealType(val->gtLclVar.gtLclNum); |
14469 | val->gtType = valTyp; |
14470 | } |
14471 | var_types dstTyp = varDsc->TypeGet(); |
14472 | |
14473 | /* If the variable's lvType is not yet set then set it here */ |
14474 | if (dstTyp == TYP_UNDEF) |
14475 | { |
14476 | varDsc->lvType = dstTyp = genActualType(valTyp); |
14477 | if (varTypeIsGC(dstTyp)) |
14478 | { |
14479 | varDsc->lvStructGcCount = 1; |
14480 | } |
14481 | #if FEATURE_SIMD |
14482 | else if (varTypeIsSIMD(dstTyp)) |
14483 | { |
14484 | varDsc->lvSIMDType = 1; |
14485 | } |
14486 | #endif |
14487 | } |
14488 | |
14489 | #ifdef DEBUG |
14490 | /* Make sure the actual types match */ |
14491 | if (genActualType(valTyp) != genActualType(dstTyp)) |
14492 | { |
14493 | // Plus some other exceptions that are apparently legal: |
14494 | // 1) TYP_REF or BYREF = TYP_I_IMPL |
14495 | bool ok = false; |
14496 | if (varTypeIsGC(dstTyp) && (valTyp == TYP_I_IMPL)) |
14497 | { |
14498 | ok = true; |
14499 | } |
14500 | // 2) TYP_DOUBLE = TYP_FLOAT or TYP_FLOAT = TYP_DOUBLE |
14501 | else if (varTypeIsFloating(dstTyp) && varTypeIsFloating(valTyp)) |
14502 | { |
14503 | ok = true; |
14504 | } |
14505 | |
14506 | if (!ok) |
14507 | { |
14508 | gtDispTree(val); |
14509 | assert(!"Incompatible types for gtNewTempAssign" ); |
14510 | } |
14511 | } |
14512 | #endif |
14513 | |
14514 | // Floating Point assignments can be created during inlining |
14515 | // see "Zero init inlinee locals:" in fgInlinePrependStatements |
14516 | // thus we may need to set compFloatingPointUsed to true here. |
14517 | // |
14518 | if (varTypeIsFloating(dstTyp) && (compFloatingPointUsed == false)) |
14519 | { |
14520 | compFloatingPointUsed = true; |
14521 | } |
14522 | |
14523 | /* Create the assignment node */ |
14524 | |
14525 | GenTree* asg; |
14526 | GenTree* dest = gtNewLclvNode(tmp, dstTyp); |
14527 | dest->gtFlags |= GTF_VAR_DEF; |
14528 | |
14529 | // With first-class structs, we should be propagating the class handle on all non-primitive |
14530 | // struct types. We don't have a convenient way to do that for all SIMD temps, since some |
14531 | // internal trees use SIMD types that are not used by the input IL. In this case, we allow |
14532 | // a null type handle and derive the necessary information about the type from its varType. |
14533 | CORINFO_CLASS_HANDLE structHnd = gtGetStructHandleIfPresent(val); |
14534 | if (varTypeIsStruct(valTyp) && ((structHnd != NO_CLASS_HANDLE) || (varTypeIsSIMD(valTyp)))) |
14535 | { |
14536 | // The struct value may be be a child of a GT_COMMA. |
14537 | GenTree* valx = val->gtEffectiveVal(/*commaOnly*/ true); |
14538 | |
14539 | if (structHnd != NO_CLASS_HANDLE) |
14540 | { |
14541 | lvaSetStruct(tmp, structHnd, false); |
14542 | } |
14543 | else |
14544 | { |
14545 | assert(valx->gtOper != GT_OBJ); |
14546 | } |
14547 | dest->gtFlags |= GTF_DONT_CSE; |
14548 | valx->gtFlags |= GTF_DONT_CSE; |
14549 | asg = impAssignStruct(dest, val, structHnd, (unsigned)CHECK_SPILL_NONE, pAfterStmt, ilOffset, block); |
14550 | } |
14551 | else |
14552 | { |
14553 | asg = gtNewAssignNode(dest, val); |
14554 | } |
14555 | |
14556 | if (compRationalIRForm) |
14557 | { |
14558 | Rationalizer::RewriteAssignmentIntoStoreLcl(asg->AsOp()); |
14559 | } |
14560 | |
14561 | return asg; |
14562 | } |
14563 | |
14564 | /***************************************************************************** |
14565 | * |
14566 | * Create a helper call to access a COM field (iff 'assg' is non-zero this is |
14567 | * an assignment and 'assg' is the new value). |
14568 | */ |
14569 | |
14570 | GenTree* Compiler::gtNewRefCOMfield(GenTree* objPtr, |
14571 | CORINFO_RESOLVED_TOKEN* pResolvedToken, |
14572 | CORINFO_ACCESS_FLAGS access, |
14573 | CORINFO_FIELD_INFO* pFieldInfo, |
14574 | var_types lclTyp, |
14575 | CORINFO_CLASS_HANDLE structType, |
14576 | GenTree* assg) |
14577 | { |
14578 | assert(pFieldInfo->fieldAccessor == CORINFO_FIELD_INSTANCE_HELPER || |
14579 | pFieldInfo->fieldAccessor == CORINFO_FIELD_INSTANCE_ADDR_HELPER || |
14580 | pFieldInfo->fieldAccessor == CORINFO_FIELD_STATIC_ADDR_HELPER); |
14581 | |
14582 | /* If we can't access it directly, we need to call a helper function */ |
14583 | GenTreeArgList* args = nullptr; |
14584 | var_types helperType = TYP_BYREF; |
14585 | |
14586 | if (pFieldInfo->fieldAccessor == CORINFO_FIELD_INSTANCE_HELPER) |
14587 | { |
14588 | if (access & CORINFO_ACCESS_SET) |
14589 | { |
14590 | assert(assg != nullptr); |
14591 | // helper needs pointer to struct, not struct itself |
14592 | if (pFieldInfo->helper == CORINFO_HELP_SETFIELDSTRUCT) |
14593 | { |
14594 | assert(structType != nullptr); |
14595 | assg = impGetStructAddr(assg, structType, (unsigned)CHECK_SPILL_ALL, true); |
14596 | } |
14597 | else if (lclTyp == TYP_DOUBLE && assg->TypeGet() == TYP_FLOAT) |
14598 | { |
14599 | assg = gtNewCastNode(TYP_DOUBLE, assg, false, TYP_DOUBLE); |
14600 | } |
14601 | else if (lclTyp == TYP_FLOAT && assg->TypeGet() == TYP_DOUBLE) |
14602 | { |
14603 | assg = gtNewCastNode(TYP_FLOAT, assg, false, TYP_FLOAT); |
14604 | } |
14605 | |
14606 | args = gtNewArgList(assg); |
14607 | helperType = TYP_VOID; |
14608 | } |
14609 | else if (access & CORINFO_ACCESS_GET) |
14610 | { |
14611 | helperType = lclTyp; |
14612 | |
14613 | // The calling convention for the helper does not take into |
14614 | // account optimization of primitive structs. |
14615 | if ((pFieldInfo->helper == CORINFO_HELP_GETFIELDSTRUCT) && !varTypeIsStruct(lclTyp)) |
14616 | { |
14617 | helperType = TYP_STRUCT; |
14618 | } |
14619 | } |
14620 | } |
14621 | |
14622 | if (pFieldInfo->helper == CORINFO_HELP_GETFIELDSTRUCT || pFieldInfo->helper == CORINFO_HELP_SETFIELDSTRUCT) |
14623 | { |
14624 | assert(pFieldInfo->structType != nullptr); |
14625 | args = gtNewListNode(gtNewIconEmbClsHndNode(pFieldInfo->structType), args); |
14626 | } |
14627 | |
14628 | GenTree* fieldHnd = impTokenToHandle(pResolvedToken); |
14629 | if (fieldHnd == nullptr) |
14630 | { // compDonotInline() |
14631 | return nullptr; |
14632 | } |
14633 | |
14634 | args = gtNewListNode(fieldHnd, args); |
14635 | |
14636 | // If it's a static field, we shouldn't have an object node |
14637 | // If it's an instance field, we have an object node |
14638 | assert((pFieldInfo->fieldAccessor != CORINFO_FIELD_STATIC_ADDR_HELPER) ^ (objPtr == nullptr)); |
14639 | |
14640 | if (objPtr != nullptr) |
14641 | { |
14642 | args = gtNewListNode(objPtr, args); |
14643 | } |
14644 | |
14645 | GenTreeCall* call = gtNewHelperCallNode(pFieldInfo->helper, genActualType(helperType), args); |
14646 | |
14647 | #if FEATURE_MULTIREG_RET |
14648 | if (varTypeIsStruct(call)) |
14649 | { |
14650 | // Initialize Return type descriptor of call node. |
14651 | ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); |
14652 | retTypeDesc->InitializeStructReturnType(this, structType); |
14653 | } |
14654 | #endif // FEATURE_MULTIREG_RET |
14655 | |
14656 | GenTree* result = call; |
14657 | |
14658 | if (pFieldInfo->fieldAccessor == CORINFO_FIELD_INSTANCE_HELPER) |
14659 | { |
14660 | if (access & CORINFO_ACCESS_GET) |
14661 | { |
14662 | if (pFieldInfo->helper == CORINFO_HELP_GETFIELDSTRUCT) |
14663 | { |
14664 | if (!varTypeIsStruct(lclTyp)) |
14665 | { |
14666 | // get the result as primitive type |
14667 | result = impGetStructAddr(result, structType, (unsigned)CHECK_SPILL_ALL, true); |
14668 | result = gtNewOperNode(GT_IND, lclTyp, result); |
14669 | } |
14670 | } |
14671 | else if (varTypeIsIntegral(lclTyp) && genTypeSize(lclTyp) < genTypeSize(TYP_INT)) |
14672 | { |
14673 | // The helper does not extend the small return types. |
14674 | result = gtNewCastNode(genActualType(lclTyp), result, false, lclTyp); |
14675 | } |
14676 | } |
14677 | } |
14678 | else |
14679 | { |
14680 | // OK, now do the indirection |
14681 | if (access & CORINFO_ACCESS_GET) |
14682 | { |
14683 | if (varTypeIsStruct(lclTyp)) |
14684 | { |
14685 | result = gtNewObjNode(structType, result); |
14686 | } |
14687 | else |
14688 | { |
14689 | result = gtNewOperNode(GT_IND, lclTyp, result); |
14690 | } |
14691 | result->gtFlags |= (GTF_EXCEPT | GTF_GLOB_REF); |
14692 | } |
14693 | else if (access & CORINFO_ACCESS_SET) |
14694 | { |
14695 | if (varTypeIsStruct(lclTyp)) |
14696 | { |
14697 | result = impAssignStructPtr(result, assg, structType, (unsigned)CHECK_SPILL_ALL); |
14698 | } |
14699 | else |
14700 | { |
14701 | result = gtNewOperNode(GT_IND, lclTyp, result); |
14702 | result->gtFlags |= (GTF_EXCEPT | GTF_GLOB_REF | GTF_IND_TGTANYWHERE); |
14703 | result = gtNewAssignNode(result, assg); |
14704 | } |
14705 | } |
14706 | } |
14707 | |
14708 | return result; |
14709 | } |
14710 | |
14711 | /***************************************************************************** |
14712 | * |
14713 | * Return true if the given node (excluding children trees) contains side effects. |
14714 | * Note that it does not recurse, and children need to be handled separately. |
14715 | * It may return false even if the node has GTF_SIDE_EFFECT (because of its children). |
14716 | * |
14717 | * Similar to OperMayThrow() (but handles GT_CALLs specially), but considers |
14718 | * assignments too. |
14719 | */ |
14720 | |
14721 | bool Compiler::gtNodeHasSideEffects(GenTree* tree, unsigned flags) |
14722 | { |
14723 | if (flags & GTF_ASG) |
14724 | { |
14725 | // TODO-Cleanup: This only checks for GT_ASG but according to OperRequiresAsgFlag there |
14726 | // are many more opers that are considered to have an assignment side effect: atomic ops |
14727 | // (GT_CMPXCHG & co.), GT_MEMORYBARRIER (not classified as an atomic op) and HW intrinsic |
14728 | // memory stores. Atomic ops have special handling in gtExtractSideEffList but the others |
14729 | // will simply be dropped is they are ever subject to an "extract side effects" operation. |
14730 | // It is possible that the reason no bugs have yet been observed in this area is that the |
14731 | // other nodes are likely to always be tree roots. |
14732 | if (tree->OperIs(GT_ASG)) |
14733 | { |
14734 | return true; |
14735 | } |
14736 | } |
14737 | |
14738 | // Are there only GTF_CALL side effects remaining? (and no other side effect kinds) |
14739 | if (flags & GTF_CALL) |
14740 | { |
14741 | if (tree->OperGet() == GT_CALL) |
14742 | { |
14743 | GenTreeCall* const call = tree->AsCall(); |
14744 | const bool ignoreExceptions = (flags & GTF_EXCEPT) == 0; |
14745 | const bool ignoreCctors = (flags & GTF_IS_IN_CSE) != 0; // We can CSE helpers that run cctors. |
14746 | if (!call->HasSideEffects(this, ignoreExceptions, ignoreCctors)) |
14747 | { |
14748 | // If this call is otherwise side effect free, check its arguments. |
14749 | for (GenTreeArgList* args = call->gtCallArgs; args != nullptr; args = args->Rest()) |
14750 | { |
14751 | if (gtTreeHasSideEffects(args->Current(), flags)) |
14752 | { |
14753 | return true; |
14754 | } |
14755 | } |
14756 | // I'm a little worried that args that assign to temps that are late args will look like |
14757 | // side effects...but better to be conservative for now. |
14758 | for (GenTreeArgList* args = call->gtCallLateArgs; args != nullptr; args = args->Rest()) |
14759 | { |
14760 | if (gtTreeHasSideEffects(args->Current(), flags)) |
14761 | { |
14762 | return true; |
14763 | } |
14764 | } |
14765 | |
14766 | // Otherwise: |
14767 | return false; |
14768 | } |
14769 | |
14770 | // Otherwise the GT_CALL is considered to have side-effects. |
14771 | return true; |
14772 | } |
14773 | } |
14774 | |
14775 | if (flags & GTF_EXCEPT) |
14776 | { |
14777 | if (tree->OperMayThrow(this)) |
14778 | { |
14779 | return true; |
14780 | } |
14781 | } |
14782 | |
14783 | // Expressions declared as CSE by (e.g.) hoisting code are considered to have relevant side |
14784 | // effects (if we care about GTF_MAKE_CSE). |
14785 | if ((flags & GTF_MAKE_CSE) && (tree->gtFlags & GTF_MAKE_CSE)) |
14786 | { |
14787 | return true; |
14788 | } |
14789 | |
14790 | return false; |
14791 | } |
14792 | |
14793 | /***************************************************************************** |
14794 | * Returns true if the expr tree has any side effects. |
14795 | */ |
14796 | |
14797 | bool Compiler::gtTreeHasSideEffects(GenTree* tree, unsigned flags /* = GTF_SIDE_EFFECT*/) |
14798 | { |
14799 | // These are the side effect flags that we care about for this tree |
14800 | unsigned sideEffectFlags = tree->gtFlags & flags; |
14801 | |
14802 | // Does this tree have any Side-effect flags set that we care about? |
14803 | if (sideEffectFlags == 0) |
14804 | { |
14805 | // no it doesn't.. |
14806 | return false; |
14807 | } |
14808 | |
14809 | if (sideEffectFlags == GTF_CALL) |
14810 | { |
14811 | if (tree->OperGet() == GT_CALL) |
14812 | { |
14813 | // Generally all trees that contain GT_CALL nodes are considered to have side-effects. |
14814 | // |
14815 | if (tree->gtCall.gtCallType == CT_HELPER) |
14816 | { |
14817 | // If this node is a helper call we may not care about the side-effects. |
14818 | // Note that gtNodeHasSideEffects checks the side effects of the helper itself |
14819 | // as well as the side effects of its arguments. |
14820 | return gtNodeHasSideEffects(tree, flags); |
14821 | } |
14822 | } |
14823 | else if (tree->OperGet() == GT_INTRINSIC) |
14824 | { |
14825 | if (gtNodeHasSideEffects(tree, flags)) |
14826 | { |
14827 | return true; |
14828 | } |
14829 | |
14830 | if (gtNodeHasSideEffects(tree->gtOp.gtOp1, flags)) |
14831 | { |
14832 | return true; |
14833 | } |
14834 | |
14835 | if ((tree->gtOp.gtOp2 != nullptr) && gtNodeHasSideEffects(tree->gtOp.gtOp2, flags)) |
14836 | { |
14837 | return true; |
14838 | } |
14839 | |
14840 | return false; |
14841 | } |
14842 | } |
14843 | |
14844 | return true; |
14845 | } |
14846 | |
14847 | GenTree* Compiler::gtBuildCommaList(GenTree* list, GenTree* expr) |
14848 | { |
14849 | // 'list' starts off as null, |
14850 | // and when it is null we haven't started the list yet. |
14851 | // |
14852 | if (list != nullptr) |
14853 | { |
14854 | // Create a GT_COMMA that appends 'expr' in front of the remaining set of expressions in (*list) |
14855 | GenTree* result = gtNewOperNode(GT_COMMA, TYP_VOID, expr, list); |
14856 | |
14857 | // Set the flags in the comma node |
14858 | result->gtFlags |= (list->gtFlags & GTF_ALL_EFFECT); |
14859 | result->gtFlags |= (expr->gtFlags & GTF_ALL_EFFECT); |
14860 | |
14861 | // 'list' and 'expr' should have valuenumbers defined for both or for neither one (unless we are remorphing, |
14862 | // in which case a prior transform involving either node may have discarded or otherwise invalidated the value |
14863 | // numbers). |
14864 | assert((list->gtVNPair.BothDefined() == expr->gtVNPair.BothDefined()) || !fgGlobalMorph); |
14865 | |
14866 | // Set the ValueNumber 'gtVNPair' for the new GT_COMMA node |
14867 | // |
14868 | if (list->gtVNPair.BothDefined() && expr->gtVNPair.BothDefined()) |
14869 | { |
14870 | // The result of a GT_COMMA node is op2, the normal value number is op2vnp |
14871 | // But we also need to include the union of side effects from op1 and op2. |
14872 | // we compute this value into exceptions_vnp. |
14873 | ValueNumPair op1vnp; |
14874 | ValueNumPair op1Xvnp = ValueNumStore::VNPForEmptyExcSet(); |
14875 | ValueNumPair op2vnp; |
14876 | ValueNumPair op2Xvnp = ValueNumStore::VNPForEmptyExcSet(); |
14877 | |
14878 | vnStore->VNPUnpackExc(expr->gtVNPair, &op1vnp, &op1Xvnp); |
14879 | vnStore->VNPUnpackExc(list->gtVNPair, &op2vnp, &op2Xvnp); |
14880 | |
14881 | ValueNumPair exceptions_vnp = ValueNumStore::VNPForEmptyExcSet(); |
14882 | |
14883 | exceptions_vnp = vnStore->VNPExcSetUnion(exceptions_vnp, op1Xvnp); |
14884 | exceptions_vnp = vnStore->VNPExcSetUnion(exceptions_vnp, op2Xvnp); |
14885 | |
14886 | result->gtVNPair = vnStore->VNPWithExc(op2vnp, exceptions_vnp); |
14887 | } |
14888 | |
14889 | return result; |
14890 | } |
14891 | else |
14892 | { |
14893 | // The 'expr' will start the list of expressions |
14894 | return expr; |
14895 | } |
14896 | } |
14897 | |
14898 | //------------------------------------------------------------------------ |
14899 | // gtExtractSideEffList: Extracts side effects from the given expression. |
14900 | // |
14901 | // Arguments: |
14902 | // expr - the expression tree to extract side effects from |
14903 | // pList - pointer to a (possibly null) GT_COMMA list that |
14904 | // will contain the extracted side effects |
14905 | // flags - side effect flags to be considered |
14906 | // ignoreRoot - ignore side effects on the expression root node |
14907 | // |
14908 | // Notes: |
14909 | // Side effects are prepended to the GT_COMMA list such that op1 of |
14910 | // each comma node holds the side effect tree and op2 points to the |
14911 | // next comma node. The original side effect execution order is preserved. |
14912 | // |
14913 | void Compiler::(GenTree* expr, |
14914 | GenTree** pList, |
14915 | unsigned flags /* = GTF_SIDE_EFFECT*/, |
14916 | bool ignoreRoot /* = false */) |
14917 | { |
14918 | class final : public GenTreeVisitor<SideEffectExtractor> |
14919 | { |
14920 | public: |
14921 | const unsigned m_flags; |
14922 | ArrayStack<GenTree*> m_sideEffects; |
14923 | |
14924 | enum |
14925 | { |
14926 | DoPreOrder = true, |
14927 | UseExecutionOrder = true |
14928 | }; |
14929 | |
14930 | SideEffectExtractor(Compiler* compiler, unsigned flags) |
14931 | : GenTreeVisitor(compiler), m_flags(flags), m_sideEffects(compiler->getAllocator(CMK_SideEffects)) |
14932 | { |
14933 | } |
14934 | |
14935 | fgWalkResult PreOrderVisit(GenTree** use, GenTree* user) |
14936 | { |
14937 | GenTree* node = *use; |
14938 | |
14939 | bool treeHasSideEffects = m_compiler->gtTreeHasSideEffects(node, m_flags); |
14940 | |
14941 | if (treeHasSideEffects) |
14942 | { |
14943 | if (m_compiler->gtNodeHasSideEffects(node, m_flags)) |
14944 | { |
14945 | m_sideEffects.Push(node); |
14946 | return Compiler::WALK_SKIP_SUBTREES; |
14947 | } |
14948 | |
14949 | // TODO-Cleanup: These have GTF_ASG set but for some reason gtNodeHasSideEffects ignores |
14950 | // them. See the related gtNodeHasSideEffects comment as well. |
14951 | // Also, these nodes must always be preserved, no matter what side effect flags are passed |
14952 | // in. But then it should never be the case that gtExtractSideEffList gets called without |
14953 | // specifying GTF_ASG so there doesn't seem to be any reason to be inconsistent with |
14954 | // gtNodeHasSideEffects and make this check unconditionally. |
14955 | if (node->OperIsAtomicOp()) |
14956 | { |
14957 | m_sideEffects.Push(node); |
14958 | return Compiler::WALK_SKIP_SUBTREES; |
14959 | } |
14960 | |
14961 | if ((m_flags & GTF_EXCEPT) != 0) |
14962 | { |
14963 | // Special case - GT_ADDR of GT_IND nodes of TYP_STRUCT have to be kept together. |
14964 | if (node->OperIs(GT_ADDR) && node->gtGetOp1()->OperIsIndir() && |
14965 | (node->gtGetOp1()->TypeGet() == TYP_STRUCT)) |
14966 | { |
14967 | #ifdef DEBUG |
14968 | if (m_compiler->verbose) |
14969 | { |
14970 | printf("Keep the GT_ADDR and GT_IND together:\n" ); |
14971 | } |
14972 | #endif |
14973 | m_sideEffects.Push(node); |
14974 | return Compiler::WALK_SKIP_SUBTREES; |
14975 | } |
14976 | } |
14977 | |
14978 | // Generally all GT_CALL nodes are considered to have side-effects. |
14979 | // So if we get here it must be a helper call that we decided it does |
14980 | // not have side effects that we needed to keep. |
14981 | assert(!node->OperIs(GT_CALL) || (node->AsCall()->gtCallType == CT_HELPER)); |
14982 | } |
14983 | |
14984 | if ((m_flags & GTF_IS_IN_CSE) != 0) |
14985 | { |
14986 | // If we're doing CSE then we also need to unmark CSE nodes. This will fail for CSE defs, |
14987 | // those need to be extracted as if they're side effects. |
14988 | if (!UnmarkCSE(node)) |
14989 | { |
14990 | m_sideEffects.Push(node); |
14991 | return Compiler::WALK_SKIP_SUBTREES; |
14992 | } |
14993 | |
14994 | // The existence of CSE defs and uses is not propagated up the tree like side |
14995 | // effects are. We need to continue visiting the tree as if it has side effects. |
14996 | treeHasSideEffects = true; |
14997 | } |
14998 | |
14999 | return treeHasSideEffects ? Compiler::WALK_CONTINUE : Compiler::WALK_SKIP_SUBTREES; |
15000 | } |
15001 | |
15002 | private: |
15003 | bool UnmarkCSE(GenTree* node) |
15004 | { |
15005 | assert(m_compiler->optValnumCSE_phase); |
15006 | |
15007 | if (m_compiler->optUnmarkCSE(node)) |
15008 | { |
15009 | // The call to optUnmarkCSE(node) should have cleared any CSE info. |
15010 | assert(!IS_CSE_INDEX(node->gtCSEnum)); |
15011 | return true; |
15012 | } |
15013 | else |
15014 | { |
15015 | assert(IS_CSE_DEF(node->gtCSEnum)); |
15016 | #ifdef DEBUG |
15017 | if (m_compiler->verbose) |
15018 | { |
15019 | printf("Preserving the CSE def #%02d at " , GET_CSE_INDEX(node->gtCSEnum)); |
15020 | m_compiler->printTreeID(node); |
15021 | } |
15022 | #endif |
15023 | return false; |
15024 | } |
15025 | } |
15026 | }; |
15027 | |
15028 | assert(!expr->OperIs(GT_STMT)); |
15029 | |
15030 | SideEffectExtractor (this, flags); |
15031 | |
15032 | if (ignoreRoot) |
15033 | { |
15034 | for (GenTree* op : expr->Operands()) |
15035 | { |
15036 | extractor.WalkTree(&op, nullptr); |
15037 | } |
15038 | } |
15039 | else |
15040 | { |
15041 | extractor.WalkTree(&expr, nullptr); |
15042 | } |
15043 | |
15044 | GenTree* list = *pList; |
15045 | |
15046 | // The extractor returns side effects in execution order but gtBuildCommaList prepends |
15047 | // to the comma-based side effect list so we have to build the list in reverse order. |
15048 | // This is also why the list cannot be built while traversing the tree. |
15049 | // The number of side effects is usually small (<= 4), less than the ArrayStack's |
15050 | // built-in size, so memory allocation is avoided. |
15051 | while (!extractor.m_sideEffects.Empty()) |
15052 | { |
15053 | list = gtBuildCommaList(list, extractor.m_sideEffects.Pop()); |
15054 | } |
15055 | |
15056 | *pList = list; |
15057 | } |
15058 | |
15059 | /***************************************************************************** |
15060 | * |
15061 | * For debugging only - displays a tree node list and makes sure all the |
15062 | * links are correctly set. |
15063 | */ |
15064 | |
15065 | #ifdef DEBUG |
15066 | |
15067 | void dispNodeList(GenTree* list, bool verbose) |
15068 | { |
15069 | GenTree* last = nullptr; |
15070 | GenTree* next; |
15071 | |
15072 | if (!list) |
15073 | { |
15074 | return; |
15075 | } |
15076 | |
15077 | for (;;) |
15078 | { |
15079 | next = list->gtNext; |
15080 | |
15081 | if (verbose) |
15082 | { |
15083 | printf("%08X -> %08X -> %08X\n" , last, list, next); |
15084 | } |
15085 | |
15086 | assert(!last || last->gtNext == list); |
15087 | |
15088 | assert(next == nullptr || next->gtPrev == list); |
15089 | |
15090 | if (!next) |
15091 | { |
15092 | break; |
15093 | } |
15094 | |
15095 | last = list; |
15096 | list = next; |
15097 | } |
15098 | printf("" ); // null string means flush |
15099 | } |
15100 | |
15101 | /***************************************************************************** |
15102 | * Callback to assert that the nodes of a qmark-colon subtree are marked |
15103 | */ |
15104 | |
15105 | /* static */ |
15106 | Compiler::fgWalkResult Compiler::gtAssertColonCond(GenTree** pTree, fgWalkData* data) |
15107 | { |
15108 | assert(data->pCallbackData == nullptr); |
15109 | |
15110 | assert((*pTree)->gtFlags & GTF_COLON_COND); |
15111 | |
15112 | return WALK_CONTINUE; |
15113 | } |
15114 | #endif // DEBUG |
15115 | |
15116 | /***************************************************************************** |
15117 | * Callback to mark the nodes of a qmark-colon subtree that are conditionally |
15118 | * executed. |
15119 | */ |
15120 | |
15121 | /* static */ |
15122 | Compiler::fgWalkResult Compiler::gtMarkColonCond(GenTree** pTree, fgWalkData* data) |
15123 | { |
15124 | assert(data->pCallbackData == nullptr); |
15125 | |
15126 | (*pTree)->gtFlags |= GTF_COLON_COND; |
15127 | |
15128 | return WALK_CONTINUE; |
15129 | } |
15130 | |
15131 | /***************************************************************************** |
15132 | * Callback to clear the conditionally executed flags of nodes that no longer |
15133 | will be conditionally executed. Note that when we find another colon we must |
15134 | stop, as the nodes below this one WILL be conditionally executed. This callback |
15135 | is called when folding a qmark condition (ie the condition is constant). |
15136 | */ |
15137 | |
15138 | /* static */ |
15139 | Compiler::fgWalkResult Compiler::gtClearColonCond(GenTree** pTree, fgWalkData* data) |
15140 | { |
15141 | GenTree* tree = *pTree; |
15142 | |
15143 | assert(data->pCallbackData == nullptr); |
15144 | |
15145 | if (tree->OperGet() == GT_COLON) |
15146 | { |
15147 | // Nodes below this will be conditionally executed. |
15148 | return WALK_SKIP_SUBTREES; |
15149 | } |
15150 | |
15151 | tree->gtFlags &= ~GTF_COLON_COND; |
15152 | return WALK_CONTINUE; |
15153 | } |
15154 | |
15155 | struct FindLinkData |
15156 | { |
15157 | GenTree* nodeToFind; |
15158 | GenTree** result; |
15159 | }; |
15160 | |
15161 | /***************************************************************************** |
15162 | * |
15163 | * Callback used by the tree walker to implement fgFindLink() |
15164 | */ |
15165 | static Compiler::fgWalkResult gtFindLinkCB(GenTree** pTree, Compiler::fgWalkData* cbData) |
15166 | { |
15167 | FindLinkData* data = (FindLinkData*)cbData->pCallbackData; |
15168 | if (*pTree == data->nodeToFind) |
15169 | { |
15170 | data->result = pTree; |
15171 | return Compiler::WALK_ABORT; |
15172 | } |
15173 | |
15174 | return Compiler::WALK_CONTINUE; |
15175 | } |
15176 | |
15177 | GenTree** Compiler::gtFindLink(GenTree* stmt, GenTree* node) |
15178 | { |
15179 | assert(stmt->gtOper == GT_STMT); |
15180 | |
15181 | FindLinkData data = {node, nullptr}; |
15182 | |
15183 | fgWalkResult result = fgWalkTreePre(&stmt->gtStmt.gtStmtExpr, gtFindLinkCB, &data); |
15184 | |
15185 | if (result == WALK_ABORT) |
15186 | { |
15187 | assert(data.nodeToFind == *data.result); |
15188 | return data.result; |
15189 | } |
15190 | else |
15191 | { |
15192 | return nullptr; |
15193 | } |
15194 | } |
15195 | |
15196 | /***************************************************************************** |
15197 | * |
15198 | * Callback that checks if a tree node has oper type GT_CATCH_ARG |
15199 | */ |
15200 | |
15201 | static Compiler::fgWalkResult gtFindCatchArg(GenTree** pTree, Compiler::fgWalkData* /* data */) |
15202 | { |
15203 | return ((*pTree)->OperGet() == GT_CATCH_ARG) ? Compiler::WALK_ABORT : Compiler::WALK_CONTINUE; |
15204 | } |
15205 | |
15206 | /*****************************************************************************/ |
15207 | bool Compiler::gtHasCatchArg(GenTree* tree) |
15208 | { |
15209 | if (((tree->gtFlags & GTF_ORDER_SIDEEFF) != 0) && (fgWalkTreePre(&tree, gtFindCatchArg) == WALK_ABORT)) |
15210 | { |
15211 | return true; |
15212 | } |
15213 | return false; |
15214 | } |
15215 | |
15216 | //------------------------------------------------------------------------ |
15217 | // gtHasCallOnStack: |
15218 | // |
15219 | // Arguments: |
15220 | // parentStack: a context (stack of parent nodes) |
15221 | // |
15222 | // Return Value: |
15223 | // returns true if any of the parent nodes are a GT_CALL |
15224 | // |
15225 | // Assumptions: |
15226 | // We have a stack of parent nodes. This generally requires that |
15227 | // we are performing a recursive tree walk using struct fgWalkData |
15228 | // |
15229 | //------------------------------------------------------------------------ |
15230 | /* static */ bool Compiler::gtHasCallOnStack(GenTreeStack* parentStack) |
15231 | { |
15232 | for (int i = 0; i < parentStack->Height(); i++) |
15233 | { |
15234 | GenTree* node = parentStack->Index(i); |
15235 | if (node->OperGet() == GT_CALL) |
15236 | { |
15237 | return true; |
15238 | } |
15239 | } |
15240 | return false; |
15241 | } |
15242 | |
15243 | //------------------------------------------------------------------------ |
15244 | // gtGetTypeProducerKind: determine if a tree produces a runtime type, and |
15245 | // if so, how. |
15246 | // |
15247 | // Arguments: |
15248 | // tree - tree to examine |
15249 | // |
15250 | // Return Value: |
15251 | // TypeProducerKind for the tree. |
15252 | // |
15253 | // Notes: |
15254 | // Checks to see if this tree returns a RuntimeType value, and if so, |
15255 | // how that value is determined. |
15256 | // |
15257 | // Currently handles these cases |
15258 | // 1) The result of Object::GetType |
15259 | // 2) The result of typeof(...) |
15260 | // 3) A null reference |
15261 | // 4) Tree is otherwise known to have type RuntimeType |
15262 | // |
15263 | // The null reference case is surprisingly common because operator |
15264 | // overloading turns the otherwise innocuous |
15265 | // |
15266 | // Type t = ....; |
15267 | // if (t == null) |
15268 | // |
15269 | // into a method call. |
15270 | |
15271 | Compiler::TypeProducerKind Compiler::gtGetTypeProducerKind(GenTree* tree) |
15272 | { |
15273 | if (tree->gtOper == GT_CALL) |
15274 | { |
15275 | if (tree->gtCall.gtCallType == CT_HELPER) |
15276 | { |
15277 | if (gtIsTypeHandleToRuntimeTypeHelper(tree->AsCall())) |
15278 | { |
15279 | return TPK_Handle; |
15280 | } |
15281 | } |
15282 | else if (tree->gtCall.gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) |
15283 | { |
15284 | if (info.compCompHnd->getIntrinsicID(tree->gtCall.gtCallMethHnd) == CORINFO_INTRINSIC_Object_GetType) |
15285 | { |
15286 | return TPK_GetType; |
15287 | } |
15288 | } |
15289 | } |
15290 | else if ((tree->gtOper == GT_INTRINSIC) && (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Object_GetType)) |
15291 | { |
15292 | return TPK_GetType; |
15293 | } |
15294 | else if ((tree->gtOper == GT_CNS_INT) && (tree->gtIntCon.gtIconVal == 0)) |
15295 | { |
15296 | return TPK_Null; |
15297 | } |
15298 | else |
15299 | { |
15300 | bool isExact = false; |
15301 | bool isNonNull = false; |
15302 | CORINFO_CLASS_HANDLE clsHnd = gtGetClassHandle(tree, &isExact, &isNonNull); |
15303 | |
15304 | if (clsHnd != NO_CLASS_HANDLE && clsHnd == info.compCompHnd->getBuiltinClass(CLASSID_RUNTIME_TYPE)) |
15305 | { |
15306 | return TPK_Other; |
15307 | } |
15308 | } |
15309 | return TPK_Unknown; |
15310 | } |
15311 | |
15312 | //------------------------------------------------------------------------ |
15313 | // gtIsTypeHandleToRuntimeTypeHelperCall -- see if tree is constructing |
15314 | // a RuntimeType from a handle |
15315 | // |
15316 | // Arguments: |
15317 | // tree - tree to examine |
15318 | // |
15319 | // Return Value: |
15320 | // True if so |
15321 | |
15322 | bool Compiler::gtIsTypeHandleToRuntimeTypeHelper(GenTreeCall* call) |
15323 | { |
15324 | return call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE) || |
15325 | call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL); |
15326 | } |
15327 | |
15328 | //------------------------------------------------------------------------ |
15329 | // gtIsTypeHandleToRuntimeTypeHandleHelperCall -- see if tree is constructing |
15330 | // a RuntimeTypeHandle from a handle |
15331 | // |
15332 | // Arguments: |
15333 | // tree - tree to examine |
15334 | // pHelper - optional pointer to a variable that receives the type of the helper |
15335 | // |
15336 | // Return Value: |
15337 | // True if so |
15338 | |
15339 | bool Compiler::gtIsTypeHandleToRuntimeTypeHandleHelper(GenTreeCall* call, CorInfoHelpFunc* pHelper) |
15340 | { |
15341 | CorInfoHelpFunc helper = CORINFO_HELP_UNDEF; |
15342 | |
15343 | if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE)) |
15344 | { |
15345 | helper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE; |
15346 | } |
15347 | else if (call->gtCallMethHnd == eeFindHelper(CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL)) |
15348 | { |
15349 | helper = CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPEHANDLE_MAYBENULL; |
15350 | } |
15351 | |
15352 | if (pHelper != nullptr) |
15353 | { |
15354 | *pHelper = helper; |
15355 | } |
15356 | |
15357 | return helper != CORINFO_HELP_UNDEF; |
15358 | } |
15359 | |
15360 | bool Compiler::gtIsActiveCSE_Candidate(GenTree* tree) |
15361 | { |
15362 | return (optValnumCSE_phase && IS_CSE_INDEX(tree->gtCSEnum)); |
15363 | } |
15364 | |
15365 | /*****************************************************************************/ |
15366 | |
15367 | struct ComplexityStruct |
15368 | { |
15369 | unsigned m_numNodes; |
15370 | unsigned m_nodeLimit; |
15371 | ComplexityStruct(unsigned nodeLimit) : m_numNodes(0), m_nodeLimit(nodeLimit) |
15372 | { |
15373 | } |
15374 | }; |
15375 | |
15376 | static Compiler::fgWalkResult ComplexityExceedsWalker(GenTree** pTree, Compiler::fgWalkData* data) |
15377 | { |
15378 | ComplexityStruct* pComplexity = (ComplexityStruct*)data->pCallbackData; |
15379 | if (++pComplexity->m_numNodes > pComplexity->m_nodeLimit) |
15380 | { |
15381 | return Compiler::WALK_ABORT; |
15382 | } |
15383 | else |
15384 | { |
15385 | return Compiler::WALK_CONTINUE; |
15386 | } |
15387 | } |
15388 | |
15389 | bool Compiler::gtComplexityExceeds(GenTree** tree, unsigned limit) |
15390 | { |
15391 | ComplexityStruct complexity(limit); |
15392 | if (fgWalkTreePre(tree, &ComplexityExceedsWalker, &complexity) == WALK_ABORT) |
15393 | { |
15394 | return true; |
15395 | } |
15396 | else |
15397 | { |
15398 | return false; |
15399 | } |
15400 | } |
15401 | |
15402 | bool GenTree::IsPhiNode() |
15403 | { |
15404 | return (OperGet() == GT_PHI_ARG) || (OperGet() == GT_PHI) || IsPhiDefn(); |
15405 | } |
15406 | |
15407 | bool GenTree::IsPhiDefn() |
15408 | { |
15409 | bool res = ((OperGet() == GT_ASG) && (gtOp.gtOp2 != nullptr) && (gtOp.gtOp2->OperGet() == GT_PHI)) || |
15410 | ((OperGet() == GT_STORE_LCL_VAR) && (gtOp.gtOp1 != nullptr) && (gtOp.gtOp1->OperGet() == GT_PHI)); |
15411 | assert(!res || OperGet() == GT_STORE_LCL_VAR || gtOp.gtOp1->OperGet() == GT_LCL_VAR); |
15412 | return res; |
15413 | } |
15414 | |
15415 | bool GenTree::IsPhiDefnStmt() |
15416 | { |
15417 | if (OperGet() != GT_STMT) |
15418 | { |
15419 | return false; |
15420 | } |
15421 | GenTree* asg = gtStmt.gtStmtExpr; |
15422 | return asg->IsPhiDefn(); |
15423 | } |
15424 | |
15425 | // IsPartialLclFld: Check for a GT_LCL_FLD whose type is a different size than the lclVar. |
15426 | // |
15427 | // Arguments: |
15428 | // comp - the Compiler object. |
15429 | // |
15430 | // Return Value: |
15431 | // Returns "true" iff 'this' is a GT_LCL_FLD or GT_STORE_LCL_FLD on which the type |
15432 | // is not the same size as the type of the GT_LCL_VAR |
15433 | |
15434 | bool GenTree::IsPartialLclFld(Compiler* comp) |
15435 | { |
15436 | return ((gtOper == GT_LCL_FLD) && |
15437 | (comp->lvaTable[this->gtLclVarCommon.gtLclNum].lvExactSize != genTypeSize(gtType))); |
15438 | } |
15439 | |
15440 | bool GenTree::DefinesLocal(Compiler* comp, GenTreeLclVarCommon** pLclVarTree, bool* pIsEntire) |
15441 | { |
15442 | GenTreeBlk* blkNode = nullptr; |
15443 | if (OperIs(GT_ASG)) |
15444 | { |
15445 | if (gtOp.gtOp1->IsLocal()) |
15446 | { |
15447 | GenTreeLclVarCommon* lclVarTree = gtOp.gtOp1->AsLclVarCommon(); |
15448 | *pLclVarTree = lclVarTree; |
15449 | if (pIsEntire != nullptr) |
15450 | { |
15451 | if (lclVarTree->IsPartialLclFld(comp)) |
15452 | { |
15453 | *pIsEntire = false; |
15454 | } |
15455 | else |
15456 | { |
15457 | *pIsEntire = true; |
15458 | } |
15459 | } |
15460 | return true; |
15461 | } |
15462 | else if (gtOp.gtOp1->OperGet() == GT_IND) |
15463 | { |
15464 | GenTree* indArg = gtOp.gtOp1->gtOp.gtOp1; |
15465 | return indArg->DefinesLocalAddr(comp, genTypeSize(gtOp.gtOp1->TypeGet()), pLclVarTree, pIsEntire); |
15466 | } |
15467 | else if (gtOp.gtOp1->OperIsBlk()) |
15468 | { |
15469 | blkNode = gtOp.gtOp1->AsBlk(); |
15470 | } |
15471 | } |
15472 | else if (OperIsBlk()) |
15473 | { |
15474 | blkNode = this->AsBlk(); |
15475 | } |
15476 | if (blkNode != nullptr) |
15477 | { |
15478 | GenTree* destAddr = blkNode->Addr(); |
15479 | unsigned width = blkNode->gtBlkSize; |
15480 | // Do we care about whether this assigns the entire variable? |
15481 | if (pIsEntire != nullptr && width == 0) |
15482 | { |
15483 | assert(blkNode->gtOper == GT_DYN_BLK); |
15484 | GenTree* blockWidth = blkNode->AsDynBlk()->gtDynamicSize; |
15485 | if (blockWidth->IsCnsIntOrI()) |
15486 | { |
15487 | if (blockWidth->IsIconHandle()) |
15488 | { |
15489 | // If it's a handle, it must be a class handle. We only create such block operations |
15490 | // for initialization of struct types, so the type of the argument(s) will match this |
15491 | // type, by construction, and be "entire". |
15492 | assert(blockWidth->IsIconHandle(GTF_ICON_CLASS_HDL)); |
15493 | width = comp->info.compCompHnd->getClassSize( |
15494 | CORINFO_CLASS_HANDLE(blockWidth->gtIntConCommon.IconValue())); |
15495 | } |
15496 | else |
15497 | { |
15498 | ssize_t swidth = blockWidth->AsIntConCommon()->IconValue(); |
15499 | assert(swidth >= 0); |
15500 | // cpblk of size zero exists in the wild (in yacc-generated code in SQL) and is valid IL. |
15501 | if (swidth == 0) |
15502 | { |
15503 | return false; |
15504 | } |
15505 | width = unsigned(swidth); |
15506 | } |
15507 | } |
15508 | } |
15509 | return destAddr->DefinesLocalAddr(comp, width, pLclVarTree, pIsEntire); |
15510 | } |
15511 | // Otherwise... |
15512 | return false; |
15513 | } |
15514 | |
15515 | // Returns true if this GenTree defines a result which is based on the address of a local. |
15516 | bool GenTree::DefinesLocalAddr(Compiler* comp, unsigned width, GenTreeLclVarCommon** pLclVarTree, bool* pIsEntire) |
15517 | { |
15518 | if (OperGet() == GT_ADDR || OperGet() == GT_LCL_VAR_ADDR) |
15519 | { |
15520 | GenTree* addrArg = this; |
15521 | if (OperGet() == GT_ADDR) |
15522 | { |
15523 | addrArg = gtOp.gtOp1; |
15524 | } |
15525 | |
15526 | if (addrArg->IsLocal() || addrArg->OperIsLocalAddr()) |
15527 | { |
15528 | GenTreeLclVarCommon* addrArgLcl = addrArg->AsLclVarCommon(); |
15529 | *pLclVarTree = addrArgLcl; |
15530 | if (pIsEntire != nullptr) |
15531 | { |
15532 | unsigned lclOffset = 0; |
15533 | if (addrArg->OperIsLocalField()) |
15534 | { |
15535 | lclOffset = addrArg->gtLclFld.gtLclOffs; |
15536 | } |
15537 | |
15538 | if (lclOffset != 0) |
15539 | { |
15540 | // We aren't updating the bytes at [0..lclOffset-1] so *pIsEntire should be set to false |
15541 | *pIsEntire = false; |
15542 | } |
15543 | else |
15544 | { |
15545 | unsigned lclNum = addrArgLcl->GetLclNum(); |
15546 | unsigned varWidth = comp->lvaLclExactSize(lclNum); |
15547 | if (comp->lvaTable[lclNum].lvNormalizeOnStore()) |
15548 | { |
15549 | // It's normalize on store, so use the full storage width -- writing to low bytes won't |
15550 | // necessarily yield a normalized value. |
15551 | varWidth = genTypeStSz(var_types(comp->lvaTable[lclNum].lvType)) * sizeof(int); |
15552 | } |
15553 | *pIsEntire = (varWidth == width); |
15554 | } |
15555 | } |
15556 | return true; |
15557 | } |
15558 | else if (addrArg->OperGet() == GT_IND) |
15559 | { |
15560 | // A GT_ADDR of a GT_IND can both be optimized away, recurse using the child of the GT_IND |
15561 | return addrArg->gtOp.gtOp1->DefinesLocalAddr(comp, width, pLclVarTree, pIsEntire); |
15562 | } |
15563 | } |
15564 | else if (OperGet() == GT_ADD) |
15565 | { |
15566 | if (gtOp.gtOp1->IsCnsIntOrI()) |
15567 | { |
15568 | // If we just adding a zero then we allow an IsEntire match against width |
15569 | // otherwise we change width to zero to disallow an IsEntire Match |
15570 | return gtOp.gtOp2->DefinesLocalAddr(comp, gtOp.gtOp1->IsIntegralConst(0) ? width : 0, pLclVarTree, |
15571 | pIsEntire); |
15572 | } |
15573 | else if (gtOp.gtOp2->IsCnsIntOrI()) |
15574 | { |
15575 | // If we just adding a zero then we allow an IsEntire match against width |
15576 | // otherwise we change width to zero to disallow an IsEntire Match |
15577 | return gtOp.gtOp1->DefinesLocalAddr(comp, gtOp.gtOp2->IsIntegralConst(0) ? width : 0, pLclVarTree, |
15578 | pIsEntire); |
15579 | } |
15580 | } |
15581 | // Post rationalization we could have GT_IND(GT_LEA(..)) trees. |
15582 | else if (OperGet() == GT_LEA) |
15583 | { |
15584 | // This method gets invoked during liveness computation and therefore it is critical |
15585 | // that we don't miss 'use' of any local. The below logic is making the assumption |
15586 | // that in case of LEA(base, index, offset) - only base can be a GT_LCL_VAR_ADDR |
15587 | // and index is not. |
15588 | CLANG_FORMAT_COMMENT_ANCHOR; |
15589 | |
15590 | #ifdef DEBUG |
15591 | GenTree* index = gtOp.gtOp2; |
15592 | if (index != nullptr) |
15593 | { |
15594 | assert(!index->DefinesLocalAddr(comp, width, pLclVarTree, pIsEntire)); |
15595 | } |
15596 | #endif // DEBUG |
15597 | |
15598 | // base |
15599 | GenTree* base = gtOp.gtOp1; |
15600 | if (base != nullptr) |
15601 | { |
15602 | // Lea could have an Indir as its base. |
15603 | if (base->OperGet() == GT_IND) |
15604 | { |
15605 | base = base->gtOp.gtOp1->gtEffectiveVal(/*commas only*/ true); |
15606 | } |
15607 | return base->DefinesLocalAddr(comp, width, pLclVarTree, pIsEntire); |
15608 | } |
15609 | } |
15610 | // Otherwise... |
15611 | return false; |
15612 | } |
15613 | |
15614 | //------------------------------------------------------------------------ |
15615 | // IsLocalExpr: Determine if this is a LclVarCommon node and return some |
15616 | // additional info about it in the two out parameters. |
15617 | // |
15618 | // Arguments: |
15619 | // comp - The Compiler instance |
15620 | // pLclVarTree - An "out" argument that returns the local tree as a |
15621 | // LclVarCommon, if it is indeed local. |
15622 | // pFldSeq - An "out" argument that returns the value numbering field |
15623 | // sequence for the node, if any. |
15624 | // |
15625 | // Return Value: |
15626 | // Returns true, and sets the out arguments accordingly, if this is |
15627 | // a LclVarCommon node. |
15628 | |
15629 | bool GenTree::IsLocalExpr(Compiler* comp, GenTreeLclVarCommon** pLclVarTree, FieldSeqNode** pFldSeq) |
15630 | { |
15631 | if (IsLocal()) // Note that this covers "GT_LCL_FLD." |
15632 | { |
15633 | *pLclVarTree = AsLclVarCommon(); |
15634 | if (OperGet() == GT_LCL_FLD) |
15635 | { |
15636 | // Otherwise, prepend this field to whatever we've already accumulated outside in. |
15637 | *pFldSeq = comp->GetFieldSeqStore()->Append(AsLclFld()->gtFieldSeq, *pFldSeq); |
15638 | } |
15639 | return true; |
15640 | } |
15641 | else |
15642 | { |
15643 | return false; |
15644 | } |
15645 | } |
15646 | |
15647 | // If this tree evaluates some sum of a local address and some constants, |
15648 | // return the node for the local being addressed |
15649 | |
15650 | GenTreeLclVarCommon* GenTree::IsLocalAddrExpr() |
15651 | { |
15652 | if (OperGet() == GT_ADDR) |
15653 | { |
15654 | return gtOp.gtOp1->IsLocal() ? gtOp.gtOp1->AsLclVarCommon() : nullptr; |
15655 | } |
15656 | else if (OperIsLocalAddr()) |
15657 | { |
15658 | return this->AsLclVarCommon(); |
15659 | } |
15660 | else if (OperGet() == GT_ADD) |
15661 | { |
15662 | if (gtOp.gtOp1->OperGet() == GT_CNS_INT) |
15663 | { |
15664 | return gtOp.gtOp2->IsLocalAddrExpr(); |
15665 | } |
15666 | else if (gtOp.gtOp2->OperGet() == GT_CNS_INT) |
15667 | { |
15668 | return gtOp.gtOp1->IsLocalAddrExpr(); |
15669 | } |
15670 | } |
15671 | // Otherwise... |
15672 | return nullptr; |
15673 | } |
15674 | |
15675 | bool GenTree::IsLocalAddrExpr(Compiler* comp, GenTreeLclVarCommon** pLclVarTree, FieldSeqNode** pFldSeq) |
15676 | { |
15677 | if (OperGet() == GT_ADDR) |
15678 | { |
15679 | assert(!comp->compRationalIRForm); |
15680 | GenTree* addrArg = gtOp.gtOp1; |
15681 | if (addrArg->IsLocal()) // Note that this covers "GT_LCL_FLD." |
15682 | { |
15683 | *pLclVarTree = addrArg->AsLclVarCommon(); |
15684 | if (addrArg->OperGet() == GT_LCL_FLD) |
15685 | { |
15686 | // Otherwise, prepend this field to whatever we've already accumulated outside in. |
15687 | *pFldSeq = comp->GetFieldSeqStore()->Append(addrArg->AsLclFld()->gtFieldSeq, *pFldSeq); |
15688 | } |
15689 | return true; |
15690 | } |
15691 | else |
15692 | { |
15693 | return false; |
15694 | } |
15695 | } |
15696 | else if (OperIsLocalAddr()) |
15697 | { |
15698 | *pLclVarTree = this->AsLclVarCommon(); |
15699 | if (this->OperGet() == GT_LCL_FLD_ADDR) |
15700 | { |
15701 | *pFldSeq = comp->GetFieldSeqStore()->Append(this->AsLclFld()->gtFieldSeq, *pFldSeq); |
15702 | } |
15703 | return true; |
15704 | } |
15705 | else if (OperGet() == GT_ADD) |
15706 | { |
15707 | if (gtOp.gtOp1->OperGet() == GT_CNS_INT) |
15708 | { |
15709 | if (gtOp.gtOp1->AsIntCon()->gtFieldSeq == nullptr) |
15710 | { |
15711 | return false; |
15712 | } |
15713 | // Otherwise, prepend this field to whatever we've already accumulated outside in. |
15714 | *pFldSeq = comp->GetFieldSeqStore()->Append(gtOp.gtOp1->AsIntCon()->gtFieldSeq, *pFldSeq); |
15715 | return gtOp.gtOp2->IsLocalAddrExpr(comp, pLclVarTree, pFldSeq); |
15716 | } |
15717 | else if (gtOp.gtOp2->OperGet() == GT_CNS_INT) |
15718 | { |
15719 | if (gtOp.gtOp2->AsIntCon()->gtFieldSeq == nullptr) |
15720 | { |
15721 | return false; |
15722 | } |
15723 | // Otherwise, prepend this field to whatever we've already accumulated outside in. |
15724 | *pFldSeq = comp->GetFieldSeqStore()->Append(gtOp.gtOp2->AsIntCon()->gtFieldSeq, *pFldSeq); |
15725 | return gtOp.gtOp1->IsLocalAddrExpr(comp, pLclVarTree, pFldSeq); |
15726 | } |
15727 | } |
15728 | // Otherwise... |
15729 | return false; |
15730 | } |
15731 | |
15732 | //------------------------------------------------------------------------ |
15733 | // IsLclVarUpdateTree: Determine whether this is an assignment tree of the |
15734 | // form Vn = Vn 'oper' 'otherTree' where Vn is a lclVar |
15735 | // |
15736 | // Arguments: |
15737 | // pOtherTree - An "out" argument in which 'otherTree' will be returned. |
15738 | // pOper - An "out" argument in which 'oper' will be returned. |
15739 | // |
15740 | // Return Value: |
15741 | // If the tree is of the above form, the lclNum of the variable being |
15742 | // updated is returned, and 'pOtherTree' and 'pOper' are set. |
15743 | // Otherwise, returns BAD_VAR_NUM. |
15744 | // |
15745 | // Notes: |
15746 | // 'otherTree' can have any shape. |
15747 | // We avoid worrying about whether the op is commutative by only considering the |
15748 | // first operand of the rhs. It is expected that most trees of this form will |
15749 | // already have the lclVar on the lhs. |
15750 | // TODO-CQ: Evaluate whether there are missed opportunities due to this, or |
15751 | // whether gtSetEvalOrder will already have put the lclVar on the lhs in |
15752 | // the cases of interest. |
15753 | |
15754 | unsigned GenTree::IsLclVarUpdateTree(GenTree** pOtherTree, genTreeOps* pOper) |
15755 | { |
15756 | unsigned lclNum = BAD_VAR_NUM; |
15757 | if (OperIs(GT_ASG)) |
15758 | { |
15759 | GenTree* lhs = gtOp.gtOp1; |
15760 | if (lhs->OperGet() == GT_LCL_VAR) |
15761 | { |
15762 | unsigned lhsLclNum = lhs->AsLclVarCommon()->gtLclNum; |
15763 | GenTree* rhs = gtOp.gtOp2; |
15764 | if (rhs->OperIsBinary() && (rhs->gtOp.gtOp1->gtOper == GT_LCL_VAR) && |
15765 | (rhs->gtOp.gtOp1->AsLclVarCommon()->gtLclNum == lhsLclNum)) |
15766 | { |
15767 | lclNum = lhsLclNum; |
15768 | *pOtherTree = rhs->gtOp.gtOp2; |
15769 | *pOper = rhs->gtOper; |
15770 | } |
15771 | } |
15772 | } |
15773 | return lclNum; |
15774 | } |
15775 | |
15776 | //------------------------------------------------------------------------ |
15777 | // canBeContained: check whether this tree node may be a subcomponent of its parent for purposes |
15778 | // of code generation. |
15779 | // |
15780 | // Return value: returns true if it is possible to contain this node and false otherwise. |
15781 | bool GenTree::canBeContained() const |
15782 | { |
15783 | assert(IsLIR()); |
15784 | |
15785 | if (gtHasReg()) |
15786 | { |
15787 | return false; |
15788 | } |
15789 | |
15790 | // It is not possible for nodes that do not produce values or that are not containable values |
15791 | // to be contained. |
15792 | if (((OperKind() & (GTK_NOVALUE | GTK_NOCONTAIN)) != 0) || (OperIsHWIntrinsic() && !isContainableHWIntrinsic())) |
15793 | { |
15794 | return false; |
15795 | } |
15796 | |
15797 | return true; |
15798 | } |
15799 | |
15800 | //------------------------------------------------------------------------ |
15801 | // isContained: check whether this tree node is a subcomponent of its parent for codegen purposes |
15802 | // |
15803 | // Return Value: |
15804 | // Returns true if there is no code generated explicitly for this node. |
15805 | // Essentially, it will be rolled into the code generation for the parent. |
15806 | // |
15807 | // Assumptions: |
15808 | // This method relies upon the value of the GTF_CONTAINED flag. |
15809 | // Therefore this method is only valid after Lowering. |
15810 | // Also note that register allocation or other subsequent phases may cause |
15811 | // nodes to become contained (or not) and therefore this property may change. |
15812 | // |
15813 | bool GenTree::isContained() const |
15814 | { |
15815 | assert(IsLIR()); |
15816 | const bool isMarkedContained = ((gtFlags & GTF_CONTAINED) != 0); |
15817 | |
15818 | #ifdef DEBUG |
15819 | if (!canBeContained()) |
15820 | { |
15821 | assert(!isMarkedContained); |
15822 | } |
15823 | |
15824 | // these actually produce a register (the flags reg, we just don't model it) |
15825 | // and are a separate instruction from the branch that consumes the result. |
15826 | // They can only produce a result if the child is a SIMD equality comparison. |
15827 | else if (OperKind() & GTK_RELOP) |
15828 | { |
15829 | // We have to cast away const-ness since AsOp() method is non-const. |
15830 | GenTree* childNode = const_cast<GenTree*>(this)->AsOp()->gtOp1; |
15831 | assert((isMarkedContained == false) || childNode->IsSIMDEqualityOrInequality()); |
15832 | } |
15833 | |
15834 | // these either produce a result in register or set flags reg. |
15835 | else if (IsSIMDEqualityOrInequality()) |
15836 | { |
15837 | assert(!isMarkedContained); |
15838 | } |
15839 | |
15840 | // if it's contained it can't be unused. |
15841 | if (isMarkedContained) |
15842 | { |
15843 | assert(!IsUnusedValue()); |
15844 | } |
15845 | #endif // DEBUG |
15846 | return isMarkedContained; |
15847 | } |
15848 | |
15849 | // return true if node is contained and an indir |
15850 | bool GenTree::isContainedIndir() const |
15851 | { |
15852 | return isIndir() && isContained(); |
15853 | } |
15854 | |
15855 | bool GenTree::isIndirAddrMode() |
15856 | { |
15857 | return isIndir() && AsIndir()->Addr()->OperIsAddrMode() && AsIndir()->Addr()->isContained(); |
15858 | } |
15859 | |
15860 | bool GenTree::isIndir() const |
15861 | { |
15862 | return OperGet() == GT_IND || OperGet() == GT_STOREIND; |
15863 | } |
15864 | |
15865 | bool GenTreeIndir::HasBase() |
15866 | { |
15867 | return Base() != nullptr; |
15868 | } |
15869 | |
15870 | bool GenTreeIndir::HasIndex() |
15871 | { |
15872 | return Index() != nullptr; |
15873 | } |
15874 | |
15875 | GenTree* GenTreeIndir::Base() |
15876 | { |
15877 | GenTree* addr = Addr(); |
15878 | |
15879 | if (isIndirAddrMode()) |
15880 | { |
15881 | GenTree* result = addr->AsAddrMode()->Base(); |
15882 | if (result != nullptr) |
15883 | { |
15884 | result = result->gtEffectiveVal(); |
15885 | } |
15886 | return result; |
15887 | } |
15888 | else |
15889 | { |
15890 | return addr; // TODO: why do we return 'addr' here, but we return 'nullptr' in the equivalent Index() case? |
15891 | } |
15892 | } |
15893 | |
15894 | GenTree* GenTreeIndir::Index() |
15895 | { |
15896 | if (isIndirAddrMode()) |
15897 | { |
15898 | GenTree* result = Addr()->AsAddrMode()->Index(); |
15899 | if (result != nullptr) |
15900 | { |
15901 | result = result->gtEffectiveVal(); |
15902 | } |
15903 | return result; |
15904 | } |
15905 | else |
15906 | { |
15907 | return nullptr; |
15908 | } |
15909 | } |
15910 | |
15911 | unsigned GenTreeIndir::Scale() |
15912 | { |
15913 | if (HasIndex()) |
15914 | { |
15915 | return Addr()->AsAddrMode()->gtScale; |
15916 | } |
15917 | else |
15918 | { |
15919 | return 1; |
15920 | } |
15921 | } |
15922 | |
15923 | ssize_t GenTreeIndir::Offset() |
15924 | { |
15925 | if (isIndirAddrMode()) |
15926 | { |
15927 | return Addr()->AsAddrMode()->Offset(); |
15928 | } |
15929 | else if (Addr()->gtOper == GT_CLS_VAR_ADDR) |
15930 | { |
15931 | return static_cast<ssize_t>(reinterpret_cast<intptr_t>(Addr()->gtClsVar.gtClsVarHnd)); |
15932 | } |
15933 | else if (Addr()->IsCnsIntOrI() && Addr()->isContained()) |
15934 | { |
15935 | return Addr()->AsIntConCommon()->IconValue(); |
15936 | } |
15937 | else |
15938 | { |
15939 | return 0; |
15940 | } |
15941 | } |
15942 | |
15943 | //------------------------------------------------------------------------ |
15944 | // GenTreeIntConCommon::ImmedValNeedsReloc: does this immediate value needs recording a relocation with the VM? |
15945 | // |
15946 | // Arguments: |
15947 | // comp - Compiler instance |
15948 | // |
15949 | // Return Value: |
15950 | // True if this immediate value requires us to record a relocation for it; false otherwise. |
15951 | |
15952 | bool GenTreeIntConCommon::ImmedValNeedsReloc(Compiler* comp) |
15953 | { |
15954 | return comp->opts.compReloc && (gtOper == GT_CNS_INT) && IsIconHandle(); |
15955 | } |
15956 | |
15957 | //------------------------------------------------------------------------ |
15958 | // ImmedValCanBeFolded: can this immediate value be folded for op? |
15959 | // |
15960 | // Arguments: |
15961 | // comp - Compiler instance |
15962 | // op - Tree operator |
15963 | // |
15964 | // Return Value: |
15965 | // True if this immediate value can be folded for op; false otherwise. |
15966 | |
15967 | bool GenTreeIntConCommon::ImmedValCanBeFolded(Compiler* comp, genTreeOps op) |
15968 | { |
15969 | // In general, immediate values that need relocations can't be folded. |
15970 | // There are cases where we do want to allow folding of handle comparisons |
15971 | // (e.g., typeof(T) == typeof(int)). |
15972 | return !ImmedValNeedsReloc(comp) || (op == GT_EQ) || (op == GT_NE); |
15973 | } |
15974 | |
15975 | #ifdef _TARGET_AMD64_ |
15976 | // Returns true if this absolute address fits within the base of an addr mode. |
15977 | // On Amd64 this effectively means, whether an absolute indirect address can |
15978 | // be encoded as 32-bit offset relative to IP or zero. |
15979 | bool GenTreeIntConCommon::FitsInAddrBase(Compiler* comp) |
15980 | { |
15981 | #ifdef DEBUG |
15982 | // Early out if PC-rel encoding of absolute addr is disabled. |
15983 | if (!comp->opts.compEnablePCRelAddr) |
15984 | { |
15985 | return false; |
15986 | } |
15987 | #endif |
15988 | |
15989 | if (comp->opts.compReloc) |
15990 | { |
15991 | // During Ngen JIT is always asked to generate relocatable code. |
15992 | // Hence JIT will try to encode only icon handles as pc-relative offsets. |
15993 | return IsIconHandle() && (IMAGE_REL_BASED_REL32 == comp->eeGetRelocTypeHint((void*)IconValue())); |
15994 | } |
15995 | else |
15996 | { |
15997 | // During Jitting, we are allowed to generate non-relocatable code. |
15998 | // On Amd64 we can encode an absolute indirect addr as an offset relative to zero or RIP. |
15999 | // An absolute indir addr that can fit within 32-bits can ben encoded as an offset relative |
16000 | // to zero. All other absolute indir addr could be attempted to be encoded as RIP relative |
16001 | // based on reloc hint provided by VM. RIP relative encoding is preferred over relative |
16002 | // to zero, because the former is one byte smaller than the latter. For this reason |
16003 | // we check for reloc hint first and then whether addr fits in 32-bits next. |
16004 | // |
16005 | // VM starts off with an initial state to allow both data and code address to be encoded as |
16006 | // pc-relative offsets. Hence JIT will attempt to encode all absolute addresses as pc-relative |
16007 | // offsets. It is possible while jitting a method, an address could not be encoded as a |
16008 | // pc-relative offset. In that case VM will note the overflow and will trigger re-jitting |
16009 | // of the method with reloc hints turned off for all future methods. Second time around |
16010 | // jitting will succeed since JIT will not attempt to encode data addresses as pc-relative |
16011 | // offsets. Note that JIT will always attempt to relocate code addresses (.e.g call addr). |
16012 | // After an overflow, VM will assume any relocation recorded is for a code address and will |
16013 | // emit jump thunk if it cannot be encoded as pc-relative offset. |
16014 | return (IMAGE_REL_BASED_REL32 == comp->eeGetRelocTypeHint((void*)IconValue())) || FitsInI32(); |
16015 | } |
16016 | } |
16017 | |
16018 | // Returns true if this icon value is encoded as addr needs recording a relocation with VM |
16019 | bool GenTreeIntConCommon::AddrNeedsReloc(Compiler* comp) |
16020 | { |
16021 | if (comp->opts.compReloc) |
16022 | { |
16023 | // During Ngen JIT is always asked to generate relocatable code. |
16024 | // Hence JIT will try to encode only icon handles as pc-relative offsets. |
16025 | return IsIconHandle() && (IMAGE_REL_BASED_REL32 == comp->eeGetRelocTypeHint((void*)IconValue())); |
16026 | } |
16027 | else |
16028 | { |
16029 | return IMAGE_REL_BASED_REL32 == comp->eeGetRelocTypeHint((void*)IconValue()); |
16030 | } |
16031 | } |
16032 | |
16033 | #elif defined(_TARGET_X86_) |
16034 | // Returns true if this absolute address fits within the base of an addr mode. |
16035 | // On x86 all addresses are 4-bytes and can be directly encoded in an addr mode. |
16036 | bool GenTreeIntConCommon::FitsInAddrBase(Compiler* comp) |
16037 | { |
16038 | #ifdef DEBUG |
16039 | // Early out if PC-rel encoding of absolute addr is disabled. |
16040 | if (!comp->opts.compEnablePCRelAddr) |
16041 | { |
16042 | return false; |
16043 | } |
16044 | #endif |
16045 | |
16046 | return IsCnsIntOrI(); |
16047 | } |
16048 | |
16049 | // Returns true if this icon value is encoded as addr needs recording a relocation with VM |
16050 | bool GenTreeIntConCommon::AddrNeedsReloc(Compiler* comp) |
16051 | { |
16052 | // If generating relocatable code, icons should be reported for recording relocatons. |
16053 | return comp->opts.compReloc && IsIconHandle(); |
16054 | } |
16055 | #endif //_TARGET_X86_ |
16056 | |
16057 | bool GenTree::IsFieldAddr(Compiler* comp, GenTree** pObj, GenTree** pStatic, FieldSeqNode** pFldSeq) |
16058 | { |
16059 | FieldSeqNode* newFldSeq = nullptr; |
16060 | GenTree* baseAddr = nullptr; |
16061 | bool mustBeStatic = false; |
16062 | |
16063 | FieldSeqNode* statStructFldSeq = nullptr; |
16064 | if (TypeGet() == TYP_REF) |
16065 | { |
16066 | // Recognize struct static field patterns... |
16067 | if (OperGet() == GT_IND) |
16068 | { |
16069 | GenTree* addr = gtOp.gtOp1; |
16070 | GenTreeIntCon* icon = nullptr; |
16071 | if (addr->OperGet() == GT_CNS_INT) |
16072 | { |
16073 | icon = addr->AsIntCon(); |
16074 | } |
16075 | else if (addr->OperGet() == GT_ADD) |
16076 | { |
16077 | // op1 should never be a field sequence (or any other kind of handle) |
16078 | assert((addr->gtOp.gtOp1->gtOper != GT_CNS_INT) || !addr->gtOp.gtOp1->IsIconHandle()); |
16079 | if (addr->gtOp.gtOp2->OperGet() == GT_CNS_INT) |
16080 | { |
16081 | icon = addr->gtOp.gtOp2->AsIntCon(); |
16082 | } |
16083 | } |
16084 | if (icon != nullptr && !icon->IsIconHandle(GTF_ICON_STR_HDL) // String handles are a source of TYP_REFs. |
16085 | && icon->gtFieldSeq != nullptr && |
16086 | icon->gtFieldSeq->m_next == nullptr // A static field should be a singleton |
16087 | // TODO-Review: A pseudoField here indicates an issue - this requires investigation |
16088 | // See test case src\ddsuites\src\clr\x86\CoreMangLib\Dev\Globalization\CalendarRegressions.exe |
16089 | && !(FieldSeqStore::IsPseudoField(icon->gtFieldSeq->m_fieldHnd)) && |
16090 | icon->gtFieldSeq != FieldSeqStore::NotAField()) // Ignore non-fields. |
16091 | { |
16092 | statStructFldSeq = icon->gtFieldSeq; |
16093 | } |
16094 | else |
16095 | { |
16096 | addr = addr->gtEffectiveVal(); |
16097 | |
16098 | // Perhaps it's a direct indirection of a helper call or a cse with a zero offset annotation. |
16099 | if ((addr->OperGet() == GT_CALL) || (addr->OperGet() == GT_LCL_VAR)) |
16100 | { |
16101 | FieldSeqNode* zeroFieldSeq = nullptr; |
16102 | if (comp->GetZeroOffsetFieldMap()->Lookup(addr, &zeroFieldSeq)) |
16103 | { |
16104 | if (zeroFieldSeq->m_next == nullptr) |
16105 | { |
16106 | statStructFldSeq = zeroFieldSeq; |
16107 | } |
16108 | } |
16109 | } |
16110 | } |
16111 | } |
16112 | else if (OperGet() == GT_CLS_VAR) |
16113 | { |
16114 | GenTreeClsVar* clsVar = AsClsVar(); |
16115 | if (clsVar->gtFieldSeq != nullptr && clsVar->gtFieldSeq->m_next == nullptr) |
16116 | { |
16117 | statStructFldSeq = clsVar->gtFieldSeq; |
16118 | } |
16119 | } |
16120 | else if (OperIsLocal()) |
16121 | { |
16122 | // If we have a GT_LCL_VAR, it can be result of a CSE substitution |
16123 | // If it is then the CSE assignment will have a ValueNum that |
16124 | // describes the RHS of the CSE assignment. |
16125 | // |
16126 | // The CSE could be a pointer to a boxed struct |
16127 | // |
16128 | GenTreeLclVarCommon* lclVar = AsLclVarCommon(); |
16129 | ValueNum vn = gtVNPair.GetLiberal(); |
16130 | if (vn != ValueNumStore::NoVN) |
16131 | { |
16132 | // Is the ValueNum a MapSelect involving a SharedStatic helper? |
16133 | VNFuncApp funcApp1; |
16134 | if (comp->vnStore->GetVNFunc(vn, &funcApp1) && (funcApp1.m_func == VNF_MapSelect) && |
16135 | (comp->vnStore->IsSharedStatic(funcApp1.m_args[1]))) |
16136 | { |
16137 | ValueNum mapVN = funcApp1.m_args[0]; |
16138 | // Is this new 'mapVN' ValueNum, a MapSelect involving a handle? |
16139 | VNFuncApp funcApp2; |
16140 | if (comp->vnStore->GetVNFunc(mapVN, &funcApp2) && (funcApp2.m_func == VNF_MapSelect) && |
16141 | (comp->vnStore->IsVNHandle(funcApp2.m_args[1]))) |
16142 | { |
16143 | ValueNum fldHndVN = funcApp2.m_args[1]; |
16144 | // Is this new 'fldHndVN' VNhandle a FieldHandle? |
16145 | unsigned flags = comp->vnStore->GetHandleFlags(fldHndVN); |
16146 | if (flags == GTF_ICON_FIELD_HDL) |
16147 | { |
16148 | CORINFO_FIELD_HANDLE fieldHnd = |
16149 | CORINFO_FIELD_HANDLE(comp->vnStore->ConstantValue<ssize_t>(fldHndVN)); |
16150 | |
16151 | // Record this field sequence in 'statStructFldSeq' as it is likely to be a Boxed Struct |
16152 | // field access. |
16153 | statStructFldSeq = comp->GetFieldSeqStore()->CreateSingleton(fieldHnd); |
16154 | } |
16155 | } |
16156 | } |
16157 | } |
16158 | } |
16159 | |
16160 | if (statStructFldSeq != nullptr) |
16161 | { |
16162 | assert(statStructFldSeq->m_next == nullptr); |
16163 | // Is this a pointer to a boxed struct? |
16164 | if (comp->gtIsStaticFieldPtrToBoxedStruct(TYP_REF, statStructFldSeq->m_fieldHnd)) |
16165 | { |
16166 | *pFldSeq = comp->GetFieldSeqStore()->Append(statStructFldSeq, *pFldSeq); |
16167 | *pObj = nullptr; |
16168 | *pStatic = this; |
16169 | return true; |
16170 | } |
16171 | } |
16172 | |
16173 | // Otherwise... |
16174 | *pObj = this; |
16175 | *pStatic = nullptr; |
16176 | return true; |
16177 | } |
16178 | else if (OperGet() == GT_ADD) |
16179 | { |
16180 | // If one operator is a field sequence/handle, the other operator must not also be a field sequence/handle. |
16181 | if ((gtOp.gtOp1->OperGet() == GT_CNS_INT) && gtOp.gtOp1->IsIconHandle()) |
16182 | { |
16183 | assert((gtOp.gtOp2->gtOper != GT_CNS_INT) || !gtOp.gtOp2->IsIconHandle()); |
16184 | newFldSeq = gtOp.gtOp1->AsIntCon()->gtFieldSeq; |
16185 | baseAddr = gtOp.gtOp2; |
16186 | } |
16187 | else if (gtOp.gtOp2->OperGet() == GT_CNS_INT) |
16188 | { |
16189 | assert((gtOp.gtOp1->gtOper != GT_CNS_INT) || !gtOp.gtOp1->IsIconHandle()); |
16190 | newFldSeq = gtOp.gtOp2->AsIntCon()->gtFieldSeq; |
16191 | baseAddr = gtOp.gtOp1; |
16192 | } |
16193 | } |
16194 | else |
16195 | { |
16196 | // Check if "this" has a zero-offset annotation. |
16197 | if (!comp->GetZeroOffsetFieldMap()->Lookup(this, &newFldSeq)) |
16198 | { |
16199 | // If not, this is not a field address. |
16200 | return false; |
16201 | } |
16202 | else |
16203 | { |
16204 | baseAddr = this; |
16205 | mustBeStatic = true; |
16206 | } |
16207 | } |
16208 | |
16209 | // If not we don't have a field seq, it's not a field address. |
16210 | if (newFldSeq == nullptr || newFldSeq == FieldSeqStore::NotAField()) |
16211 | { |
16212 | return false; |
16213 | } |
16214 | |
16215 | // Prepend this field to whatever we've already accumulated (outside-in). |
16216 | *pFldSeq = comp->GetFieldSeqStore()->Append(newFldSeq, *pFldSeq); |
16217 | |
16218 | // Is it a static or instance field? |
16219 | if (!FieldSeqStore::IsPseudoField(newFldSeq->m_fieldHnd) && |
16220 | comp->info.compCompHnd->isFieldStatic(newFldSeq->m_fieldHnd)) |
16221 | { |
16222 | // It is a static field. We're done. |
16223 | *pObj = nullptr; |
16224 | *pStatic = baseAddr; |
16225 | return true; |
16226 | } |
16227 | else if ((baseAddr != nullptr) && !mustBeStatic) |
16228 | { |
16229 | // It's an instance field...but it must be for a struct field, since we've not yet encountered |
16230 | // a "TYP_REF" address. Analyze the reset of the address. |
16231 | return baseAddr->gtEffectiveVal()->IsFieldAddr(comp, pObj, pStatic, pFldSeq); |
16232 | } |
16233 | |
16234 | // Otherwise... |
16235 | return false; |
16236 | } |
16237 | |
16238 | bool Compiler::gtIsStaticFieldPtrToBoxedStruct(var_types fieldNodeType, CORINFO_FIELD_HANDLE fldHnd) |
16239 | { |
16240 | if (fieldNodeType != TYP_REF) |
16241 | { |
16242 | return false; |
16243 | } |
16244 | noway_assert(fldHnd != nullptr); |
16245 | CorInfoType cit = info.compCompHnd->getFieldType(fldHnd); |
16246 | var_types fieldTyp = JITtype2varType(cit); |
16247 | return fieldTyp != TYP_REF; |
16248 | } |
16249 | |
16250 | #ifdef FEATURE_SIMD |
16251 | //------------------------------------------------------------------------ |
16252 | // gtGetSIMDZero: Get a zero value of the appropriate SIMD type. |
16253 | // |
16254 | // Arguments: |
16255 | // var_types - The simdType |
16256 | // baseType - The base type we need |
16257 | // simdHandle - The handle for the SIMD type |
16258 | // |
16259 | // Return Value: |
16260 | // A node generating the appropriate Zero, if we are able to discern it, |
16261 | // otherwise null (note that this shouldn't happen, but callers should |
16262 | // be tolerant of this case). |
16263 | |
16264 | GenTree* Compiler::gtGetSIMDZero(var_types simdType, var_types baseType, CORINFO_CLASS_HANDLE simdHandle) |
16265 | { |
16266 | bool found = false; |
16267 | bool isHWSIMD = true; |
16268 | noway_assert(m_simdHandleCache != nullptr); |
16269 | |
16270 | // First, determine whether this is Vector<T>. |
16271 | if (simdType == getSIMDVectorType()) |
16272 | { |
16273 | switch (baseType) |
16274 | { |
16275 | case TYP_FLOAT: |
16276 | found = (simdHandle == m_simdHandleCache->SIMDFloatHandle); |
16277 | break; |
16278 | case TYP_DOUBLE: |
16279 | found = (simdHandle == m_simdHandleCache->SIMDDoubleHandle); |
16280 | break; |
16281 | case TYP_INT: |
16282 | found = (simdHandle == m_simdHandleCache->SIMDIntHandle); |
16283 | break; |
16284 | case TYP_USHORT: |
16285 | found = (simdHandle == m_simdHandleCache->SIMDUShortHandle); |
16286 | break; |
16287 | case TYP_UBYTE: |
16288 | found = (simdHandle == m_simdHandleCache->SIMDUByteHandle); |
16289 | break; |
16290 | case TYP_SHORT: |
16291 | found = (simdHandle == m_simdHandleCache->SIMDShortHandle); |
16292 | break; |
16293 | case TYP_BYTE: |
16294 | found = (simdHandle == m_simdHandleCache->SIMDByteHandle); |
16295 | break; |
16296 | case TYP_LONG: |
16297 | found = (simdHandle == m_simdHandleCache->SIMDLongHandle); |
16298 | break; |
16299 | case TYP_UINT: |
16300 | found = (simdHandle == m_simdHandleCache->SIMDUIntHandle); |
16301 | break; |
16302 | case TYP_ULONG: |
16303 | found = (simdHandle == m_simdHandleCache->SIMDULongHandle); |
16304 | break; |
16305 | default: |
16306 | break; |
16307 | } |
16308 | if (found) |
16309 | { |
16310 | isHWSIMD = false; |
16311 | } |
16312 | } |
16313 | |
16314 | if (!found) |
16315 | { |
16316 | // We must still have isHWSIMD set to true, and the only non-HW types left are the fixed types. |
16317 | switch (simdType) |
16318 | { |
16319 | case TYP_SIMD8: |
16320 | switch (baseType) |
16321 | { |
16322 | case TYP_FLOAT: |
16323 | if (simdHandle == m_simdHandleCache->SIMDVector2Handle) |
16324 | { |
16325 | isHWSIMD = false; |
16326 | } |
16327 | #if defined(_TARGET_ARM64_) && defined(FEATURE_HW_INTRINSICS) |
16328 | else |
16329 | { |
16330 | assert(simdHandle == m_simdHandleCache->Vector64FloatHandle); |
16331 | } |
16332 | break; |
16333 | case TYP_INT: |
16334 | assert(simdHandle == m_simdHandleCache->Vector64IntHandle); |
16335 | break; |
16336 | case TYP_USHORT: |
16337 | assert(simdHandle == m_simdHandleCache->Vector64UShortHandle); |
16338 | break; |
16339 | case TYP_UBYTE: |
16340 | assert(simdHandle == m_simdHandleCache->Vector64UByteHandle); |
16341 | break; |
16342 | case TYP_SHORT: |
16343 | assert(simdHandle == m_simdHandleCache->Vector64ShortHandle); |
16344 | break; |
16345 | case TYP_BYTE: |
16346 | assert(simdHandle == m_simdHandleCache->Vector64ByteHandle); |
16347 | break; |
16348 | case TYP_UINT: |
16349 | assert(simdHandle == m_simdHandleCache->Vector64UIntHandle); |
16350 | break; |
16351 | #endif // defined(_TARGET_ARM64_) && defined(FEATURE_HW_INTRINSICS) |
16352 | default: |
16353 | break; |
16354 | } |
16355 | break; |
16356 | |
16357 | case TYP_SIMD12: |
16358 | assert((baseType == TYP_FLOAT) && (simdHandle == m_simdHandleCache->SIMDVector3Handle)); |
16359 | isHWSIMD = false; |
16360 | break; |
16361 | |
16362 | case TYP_SIMD16: |
16363 | switch (baseType) |
16364 | { |
16365 | case TYP_FLOAT: |
16366 | if (simdHandle == m_simdHandleCache->SIMDVector4Handle) |
16367 | { |
16368 | isHWSIMD = false; |
16369 | } |
16370 | #if defined(FEATURE_HW_INTRINSICS) |
16371 | else |
16372 | { |
16373 | assert(simdHandle == m_simdHandleCache->Vector128FloatHandle); |
16374 | } |
16375 | break; |
16376 | case TYP_DOUBLE: |
16377 | assert(simdHandle == m_simdHandleCache->Vector128DoubleHandle); |
16378 | break; |
16379 | case TYP_INT: |
16380 | assert(simdHandle == m_simdHandleCache->Vector128IntHandle); |
16381 | break; |
16382 | case TYP_USHORT: |
16383 | assert(simdHandle == m_simdHandleCache->Vector128UShortHandle); |
16384 | break; |
16385 | case TYP_UBYTE: |
16386 | assert(simdHandle == m_simdHandleCache->Vector128UByteHandle); |
16387 | break; |
16388 | case TYP_SHORT: |
16389 | assert(simdHandle == m_simdHandleCache->Vector128ShortHandle); |
16390 | break; |
16391 | case TYP_BYTE: |
16392 | assert(simdHandle == m_simdHandleCache->Vector128ByteHandle); |
16393 | break; |
16394 | case TYP_LONG: |
16395 | assert(simdHandle == m_simdHandleCache->Vector128LongHandle); |
16396 | break; |
16397 | case TYP_UINT: |
16398 | assert(simdHandle == m_simdHandleCache->Vector128UIntHandle); |
16399 | break; |
16400 | case TYP_ULONG: |
16401 | assert(simdHandle == m_simdHandleCache->Vector128ULongHandle); |
16402 | break; |
16403 | #endif // defined(FEATURE_HW_INTRINSICS) |
16404 | |
16405 | default: |
16406 | break; |
16407 | } |
16408 | break; |
16409 | |
16410 | #if defined(_TARGET_XARCH4_) && defined(FEATURE_HW_INTRINSICS) |
16411 | case TYP_SIMD32: |
16412 | switch (baseType) |
16413 | { |
16414 | case TYP_FLOAT: |
16415 | assert(simdHandle == m_simdHandleCache->Vector256FloatHandle); |
16416 | break; |
16417 | case TYP_DOUBLE: |
16418 | assert(simdHandle == m_simdHandleCache->Vector256DoubleHandle); |
16419 | break; |
16420 | case TYP_INT: |
16421 | assert(simdHandle == m_simdHandleCache->Vector256IntHandle); |
16422 | break; |
16423 | case TYP_USHORT: |
16424 | assert(simdHandle == m_simdHandleCache->Vector256UShortHandle); |
16425 | break; |
16426 | case TYP_UBYTE: |
16427 | assert(simdHandle == m_simdHandleCache->Vector256UByteHandle); |
16428 | break; |
16429 | case TYP_SHORT: |
16430 | assert(simdHandle == m_simdHandleCache->Vector256ShortHandle); |
16431 | break; |
16432 | case TYP_BYTE: |
16433 | assert(simdHandle == m_simdHandleCache->Vector256ByteHandle); |
16434 | break; |
16435 | case TYP_LONG: |
16436 | assert(simdHandle == m_simdHandleCache->Vector256LongHandle); |
16437 | break; |
16438 | case TYP_UINT: |
16439 | assert(simdHandle == m_simdHandleCache->Vector256UIntHandle); |
16440 | break; |
16441 | case TYP_ULONG: |
16442 | assert(simdHandle == m_simdHandleCache->Vector256ULongHandle); |
16443 | break; |
16444 | default: |
16445 | break; |
16446 | } |
16447 | break; |
16448 | #endif // _TARGET_XARCH_ && FEATURE_HW_INTRINSICS |
16449 | default: |
16450 | break; |
16451 | } |
16452 | } |
16453 | |
16454 | unsigned size = genTypeSize(simdType); |
16455 | if (isHWSIMD) |
16456 | { |
16457 | #if defined(_TARGET_XARCH_) && defined(FEATURE_HW_INTRINSICS) |
16458 | switch (simdType) |
16459 | { |
16460 | case TYP_SIMD16: |
16461 | if (compSupports(InstructionSet_SSE)) |
16462 | { |
16463 | // We only return the HWIntrinsicNode if SSE is supported, since it is possible for |
16464 | // the user to disable the SSE HWIntrinsic support via the COMPlus configuration knobs |
16465 | // even though the hardware vector types are still available. |
16466 | return gtNewSimdHWIntrinsicNode(simdType, NI_Base_Vector128_Zero, baseType, size); |
16467 | } |
16468 | return nullptr; |
16469 | case TYP_SIMD32: |
16470 | if (compSupports(InstructionSet_AVX)) |
16471 | { |
16472 | // We only return the HWIntrinsicNode if AVX is supported, since it is possible for |
16473 | // the user to disable the AVX HWIntrinsic support via the COMPlus configuration knobs |
16474 | // even though the hardware vector types are still available. |
16475 | return gtNewSimdHWIntrinsicNode(simdType, NI_Base_Vector256_Zero, baseType, size); |
16476 | } |
16477 | return nullptr; |
16478 | default: |
16479 | break; |
16480 | } |
16481 | #endif // _TARGET_XARCH_ && FEATURE_HW_INTRINSICS |
16482 | JITDUMP("Coudn't find the matching SIMD type for %s<%s> in gtGetSIMDZero\n" , varTypeName(simdType), |
16483 | varTypeName(baseType)); |
16484 | } |
16485 | else |
16486 | { |
16487 | return gtNewSIMDVectorZero(simdType, baseType, size); |
16488 | } |
16489 | return nullptr; |
16490 | } |
16491 | #endif // FEATURE_SIMD |
16492 | |
16493 | CORINFO_CLASS_HANDLE Compiler::gtGetStructHandleIfPresent(GenTree* tree) |
16494 | { |
16495 | CORINFO_CLASS_HANDLE structHnd = NO_CLASS_HANDLE; |
16496 | tree = tree->gtEffectiveVal(); |
16497 | if (varTypeIsStruct(tree->gtType)) |
16498 | { |
16499 | switch (tree->gtOper) |
16500 | { |
16501 | default: |
16502 | break; |
16503 | case GT_MKREFANY: |
16504 | structHnd = impGetRefAnyClass(); |
16505 | break; |
16506 | case GT_OBJ: |
16507 | structHnd = tree->gtObj.gtClass; |
16508 | break; |
16509 | case GT_CALL: |
16510 | structHnd = tree->gtCall.gtRetClsHnd; |
16511 | break; |
16512 | case GT_RET_EXPR: |
16513 | structHnd = tree->gtRetExpr.gtRetClsHnd; |
16514 | break; |
16515 | case GT_ARGPLACE: |
16516 | structHnd = tree->gtArgPlace.gtArgPlaceClsHnd; |
16517 | break; |
16518 | case GT_INDEX: |
16519 | structHnd = tree->gtIndex.gtStructElemClass; |
16520 | break; |
16521 | case GT_INDEX_ADDR: |
16522 | structHnd = tree->AsIndexAddr()->gtStructElemClass; |
16523 | break; |
16524 | case GT_FIELD: |
16525 | info.compCompHnd->getFieldType(tree->gtField.gtFldHnd, &structHnd); |
16526 | break; |
16527 | case GT_ASG: |
16528 | structHnd = gtGetStructHandleIfPresent(tree->gtGetOp1()); |
16529 | break; |
16530 | case GT_LCL_FLD: |
16531 | #ifdef FEATURE_SIMD |
16532 | if (varTypeIsSIMD(tree)) |
16533 | { |
16534 | structHnd = gtGetStructHandleForSIMD(tree->gtType, TYP_FLOAT); |
16535 | } |
16536 | #endif |
16537 | break; |
16538 | case GT_LCL_VAR: |
16539 | structHnd = lvaTable[tree->AsLclVarCommon()->gtLclNum].lvVerTypeInfo.GetClassHandle(); |
16540 | break; |
16541 | case GT_RETURN: |
16542 | structHnd = gtGetStructHandleIfPresent(tree->gtOp.gtOp1); |
16543 | break; |
16544 | case GT_IND: |
16545 | #ifdef FEATURE_SIMD |
16546 | if (varTypeIsSIMD(tree)) |
16547 | { |
16548 | structHnd = gtGetStructHandleForSIMD(tree->gtType, TYP_FLOAT); |
16549 | } |
16550 | else |
16551 | #endif |
16552 | { |
16553 | ArrayInfo arrInfo; |
16554 | if (TryGetArrayInfo(tree->AsIndir(), &arrInfo)) |
16555 | { |
16556 | structHnd = EncodeElemType(arrInfo.m_elemType, arrInfo.m_elemStructType); |
16557 | } |
16558 | } |
16559 | break; |
16560 | #ifdef FEATURE_SIMD |
16561 | case GT_SIMD: |
16562 | structHnd = gtGetStructHandleForSIMD(tree->gtType, tree->AsSIMD()->gtSIMDBaseType); |
16563 | break; |
16564 | #endif // FEATURE_SIMD |
16565 | #ifdef FEATURE_HW_INTRINSICS |
16566 | case GT_HWIntrinsic: |
16567 | structHnd = gtGetStructHandleForHWSIMD(tree->gtType, tree->AsHWIntrinsic()->gtSIMDBaseType); |
16568 | break; |
16569 | #endif |
16570 | break; |
16571 | } |
16572 | } |
16573 | return structHnd; |
16574 | } |
16575 | |
16576 | CORINFO_CLASS_HANDLE Compiler::gtGetStructHandle(GenTree* tree) |
16577 | { |
16578 | CORINFO_CLASS_HANDLE structHnd = gtGetStructHandleIfPresent(tree); |
16579 | assert(structHnd != NO_CLASS_HANDLE); |
16580 | return structHnd; |
16581 | } |
16582 | |
16583 | //------------------------------------------------------------------------ |
16584 | // gtGetClassHandle: find class handle for a ref type |
16585 | // |
16586 | // Arguments: |
16587 | // tree -- tree to find handle for |
16588 | // pIsExact [out] -- whether handle is exact type |
16589 | // pIsNonNull [out] -- whether tree value is known not to be null |
16590 | // |
16591 | // Return Value: |
16592 | // nullptr if class handle is unknown, |
16593 | // otherwise the class handle. |
16594 | // *pIsExact set true if tree type is known to be exactly the handle type, |
16595 | // otherwise actual type may be a subtype. |
16596 | // *pIsNonNull set true if tree value is known not to be null, |
16597 | // otherwise a null value is possible. |
16598 | |
16599 | CORINFO_CLASS_HANDLE Compiler::gtGetClassHandle(GenTree* tree, bool* pIsExact, bool* pIsNonNull) |
16600 | { |
16601 | // Set default values for our out params. |
16602 | *pIsNonNull = false; |
16603 | *pIsExact = false; |
16604 | CORINFO_CLASS_HANDLE objClass = nullptr; |
16605 | |
16606 | // Bail out if we're just importing and not generating code, since |
16607 | // the jit uses TYP_REF for CORINFO_TYPE_VAR locals and args, but |
16608 | // these may not be ref types. |
16609 | if (compIsForImportOnly()) |
16610 | { |
16611 | return objClass; |
16612 | } |
16613 | |
16614 | // Bail out if the tree is not a ref type. |
16615 | var_types treeType = tree->TypeGet(); |
16616 | if (treeType != TYP_REF) |
16617 | { |
16618 | return objClass; |
16619 | } |
16620 | |
16621 | // Tunnel through commas. |
16622 | GenTree* obj = tree->gtEffectiveVal(false); |
16623 | const genTreeOps objOp = obj->OperGet(); |
16624 | |
16625 | switch (objOp) |
16626 | { |
16627 | case GT_COMMA: |
16628 | { |
16629 | // gtEffectiveVal above means we shouldn't see commas here. |
16630 | assert(!"unexpected GT_COMMA" ); |
16631 | break; |
16632 | } |
16633 | |
16634 | case GT_LCL_VAR: |
16635 | { |
16636 | // For locals, pick up type info from the local table. |
16637 | const unsigned objLcl = obj->AsLclVar()->GetLclNum(); |
16638 | |
16639 | objClass = lvaTable[objLcl].lvClassHnd; |
16640 | *pIsExact = lvaTable[objLcl].lvClassIsExact; |
16641 | break; |
16642 | } |
16643 | |
16644 | case GT_FIELD: |
16645 | { |
16646 | // For fields, get the type from the field handle. |
16647 | CORINFO_FIELD_HANDLE fieldHnd = obj->gtField.gtFldHnd; |
16648 | |
16649 | if (fieldHnd != nullptr) |
16650 | { |
16651 | objClass = gtGetFieldClassHandle(fieldHnd, pIsExact, pIsNonNull); |
16652 | } |
16653 | |
16654 | break; |
16655 | } |
16656 | |
16657 | case GT_RET_EXPR: |
16658 | { |
16659 | // If we see a RET_EXPR, recurse through to examine the |
16660 | // return value expression. |
16661 | GenTree* retExpr = tree->gtRetExpr.gtInlineCandidate; |
16662 | objClass = gtGetClassHandle(retExpr, pIsExact, pIsNonNull); |
16663 | break; |
16664 | } |
16665 | |
16666 | case GT_CALL: |
16667 | { |
16668 | GenTreeCall* call = tree->AsCall(); |
16669 | if (call->IsInlineCandidate()) |
16670 | { |
16671 | // For inline candidates, we've already cached the return |
16672 | // type class handle in the inline info. |
16673 | InlineCandidateInfo* inlInfo = call->gtInlineCandidateInfo; |
16674 | assert(inlInfo != nullptr); |
16675 | |
16676 | // Grab it as our first cut at a return type. |
16677 | assert(inlInfo->methInfo.args.retType == CORINFO_TYPE_CLASS); |
16678 | objClass = inlInfo->methInfo.args.retTypeClass; |
16679 | |
16680 | // If the method is shared, the above may not capture |
16681 | // the most precise return type information (that is, |
16682 | // it may represent a shared return type and as such, |
16683 | // have instances of __Canon). See if we can use the |
16684 | // context to get at something more definite. |
16685 | // |
16686 | // For now, we do this here on demand rather than when |
16687 | // processing the call, but we could/should apply |
16688 | // similar sharpening to the argument and local types |
16689 | // of the inlinee. |
16690 | const unsigned retClassFlags = info.compCompHnd->getClassAttribs(objClass); |
16691 | if (retClassFlags & CORINFO_FLG_SHAREDINST) |
16692 | { |
16693 | CORINFO_CONTEXT_HANDLE context = inlInfo->exactContextHnd; |
16694 | |
16695 | if (context != nullptr) |
16696 | { |
16697 | CORINFO_CLASS_HANDLE exactClass = nullptr; |
16698 | |
16699 | if (((size_t)context & CORINFO_CONTEXTFLAGS_MASK) == CORINFO_CONTEXTFLAGS_CLASS) |
16700 | { |
16701 | exactClass = (CORINFO_CLASS_HANDLE)((size_t)context & ~CORINFO_CONTEXTFLAGS_MASK); |
16702 | } |
16703 | else |
16704 | { |
16705 | CORINFO_METHOD_HANDLE exactMethod = |
16706 | (CORINFO_METHOD_HANDLE)((size_t)context & ~CORINFO_CONTEXTFLAGS_MASK); |
16707 | exactClass = info.compCompHnd->getMethodClass(exactMethod); |
16708 | } |
16709 | |
16710 | // Grab the signature in this context. |
16711 | CORINFO_SIG_INFO sig; |
16712 | eeGetMethodSig(call->gtCallMethHnd, &sig, exactClass); |
16713 | assert(sig.retType == CORINFO_TYPE_CLASS); |
16714 | objClass = sig.retTypeClass; |
16715 | } |
16716 | } |
16717 | } |
16718 | else if (call->gtCallType == CT_USER_FUNC) |
16719 | { |
16720 | // For user calls, we can fetch the approximate return |
16721 | // type info from the method handle. Unfortunately |
16722 | // we've lost the exact context, so this is the best |
16723 | // we can do for now. |
16724 | CORINFO_METHOD_HANDLE method = call->gtCallMethHnd; |
16725 | CORINFO_CLASS_HANDLE exactClass = nullptr; |
16726 | CORINFO_SIG_INFO sig; |
16727 | eeGetMethodSig(method, &sig, exactClass); |
16728 | if (sig.retType == CORINFO_TYPE_VOID) |
16729 | { |
16730 | // This is a constructor call. |
16731 | const unsigned methodFlags = info.compCompHnd->getMethodAttribs(method); |
16732 | assert((methodFlags & CORINFO_FLG_CONSTRUCTOR) != 0); |
16733 | objClass = info.compCompHnd->getMethodClass(method); |
16734 | *pIsExact = true; |
16735 | *pIsNonNull = true; |
16736 | } |
16737 | else |
16738 | { |
16739 | assert(sig.retType == CORINFO_TYPE_CLASS); |
16740 | objClass = sig.retTypeClass; |
16741 | } |
16742 | } |
16743 | else if (call->gtCallType == CT_HELPER) |
16744 | { |
16745 | objClass = gtGetHelperCallClassHandle(call, pIsExact, pIsNonNull); |
16746 | } |
16747 | |
16748 | break; |
16749 | } |
16750 | |
16751 | case GT_INTRINSIC: |
16752 | { |
16753 | GenTreeIntrinsic* intrinsic = obj->AsIntrinsic(); |
16754 | |
16755 | if (intrinsic->gtIntrinsicId == CORINFO_INTRINSIC_Object_GetType) |
16756 | { |
16757 | CORINFO_CLASS_HANDLE runtimeType = info.compCompHnd->getBuiltinClass(CLASSID_RUNTIME_TYPE); |
16758 | assert(runtimeType != NO_CLASS_HANDLE); |
16759 | |
16760 | objClass = runtimeType; |
16761 | *pIsExact = false; |
16762 | *pIsNonNull = true; |
16763 | } |
16764 | |
16765 | break; |
16766 | } |
16767 | |
16768 | case GT_CNS_STR: |
16769 | { |
16770 | // For literal strings, we know the class and that the |
16771 | // value is not null. |
16772 | objClass = impGetStringClass(); |
16773 | *pIsExact = true; |
16774 | *pIsNonNull = true; |
16775 | break; |
16776 | } |
16777 | |
16778 | case GT_IND: |
16779 | { |
16780 | GenTreeIndir* indir = obj->AsIndir(); |
16781 | |
16782 | if (indir->HasBase() && !indir->HasIndex()) |
16783 | { |
16784 | // indir(addr(lcl)) --> lcl |
16785 | // |
16786 | // This comes up during constrained callvirt on ref types. |
16787 | |
16788 | GenTree* base = indir->Base(); |
16789 | GenTreeLclVarCommon* lcl = base->IsLocalAddrExpr(); |
16790 | |
16791 | if ((lcl != nullptr) && (base->OperGet() != GT_ADD)) |
16792 | { |
16793 | const unsigned objLcl = lcl->GetLclNum(); |
16794 | objClass = lvaTable[objLcl].lvClassHnd; |
16795 | *pIsExact = lvaTable[objLcl].lvClassIsExact; |
16796 | } |
16797 | else if (base->OperGet() == GT_ARR_ELEM) |
16798 | { |
16799 | // indir(arr_elem(...)) -> array element type |
16800 | |
16801 | GenTree* array = base->AsArrElem()->gtArrObj; |
16802 | |
16803 | objClass = gtGetArrayElementClassHandle(array); |
16804 | *pIsExact = false; |
16805 | *pIsNonNull = false; |
16806 | } |
16807 | else if (base->OperGet() == GT_ADD) |
16808 | { |
16809 | // This could be a static field access. |
16810 | // |
16811 | // See if op1 is a static field base helper call |
16812 | // and if so, op2 will have the field info. |
16813 | GenTree* op1 = base->gtOp.gtOp1; |
16814 | GenTree* op2 = base->gtOp.gtOp2; |
16815 | |
16816 | const bool op1IsStaticFieldBase = gtIsStaticGCBaseHelperCall(op1); |
16817 | |
16818 | if (op1IsStaticFieldBase && (op2->OperGet() == GT_CNS_INT)) |
16819 | { |
16820 | FieldSeqNode* fieldSeq = op2->AsIntCon()->gtFieldSeq; |
16821 | |
16822 | if (fieldSeq != nullptr) |
16823 | { |
16824 | while (fieldSeq->m_next != nullptr) |
16825 | { |
16826 | fieldSeq = fieldSeq->m_next; |
16827 | } |
16828 | |
16829 | assert(!fieldSeq->IsPseudoField()); |
16830 | |
16831 | // No benefit to calling gtGetFieldClassHandle here, as |
16832 | // the exact field being accessed can vary. |
16833 | CORINFO_FIELD_HANDLE fieldHnd = fieldSeq->m_fieldHnd; |
16834 | CORINFO_CLASS_HANDLE fieldClass = nullptr; |
16835 | CorInfoType fieldCorType = info.compCompHnd->getFieldType(fieldHnd, &fieldClass); |
16836 | |
16837 | assert(fieldCorType == CORINFO_TYPE_CLASS); |
16838 | objClass = fieldClass; |
16839 | } |
16840 | } |
16841 | } |
16842 | } |
16843 | |
16844 | break; |
16845 | } |
16846 | |
16847 | case GT_BOX: |
16848 | { |
16849 | // Box should just wrap a local var reference which has |
16850 | // the type we're looking for. Also box only represents a |
16851 | // non-nullable value type so result cannot be null. |
16852 | GenTreeBox* box = obj->AsBox(); |
16853 | GenTree* boxTemp = box->BoxOp(); |
16854 | assert(boxTemp->IsLocal()); |
16855 | const unsigned boxTempLcl = boxTemp->AsLclVar()->GetLclNum(); |
16856 | objClass = lvaTable[boxTempLcl].lvClassHnd; |
16857 | *pIsExact = lvaTable[boxTempLcl].lvClassIsExact; |
16858 | *pIsNonNull = true; |
16859 | break; |
16860 | } |
16861 | |
16862 | case GT_INDEX: |
16863 | { |
16864 | GenTree* array = obj->AsIndex()->Arr(); |
16865 | |
16866 | objClass = gtGetArrayElementClassHandle(array); |
16867 | *pIsExact = false; |
16868 | *pIsNonNull = false; |
16869 | break; |
16870 | } |
16871 | |
16872 | default: |
16873 | { |
16874 | break; |
16875 | } |
16876 | } |
16877 | |
16878 | return objClass; |
16879 | } |
16880 | |
16881 | //------------------------------------------------------------------------ |
16882 | // gtGetHelperCallClassHandle: find class handle for return value of a |
16883 | // helper call |
16884 | // |
16885 | // Arguments: |
16886 | // call - helper call to examine |
16887 | // pIsExact - [OUT] true if type is known exactly |
16888 | // pIsNonNull - [OUT] true if return value is not null |
16889 | // |
16890 | // Return Value: |
16891 | // nullptr if helper call result is not a ref class, or the class handle |
16892 | // is unknown, otherwise the class handle. |
16893 | |
16894 | CORINFO_CLASS_HANDLE Compiler::gtGetHelperCallClassHandle(GenTreeCall* call, bool* pIsExact, bool* pIsNonNull) |
16895 | { |
16896 | assert(call->gtCallType == CT_HELPER); |
16897 | |
16898 | *pIsNonNull = false; |
16899 | *pIsExact = false; |
16900 | CORINFO_CLASS_HANDLE objClass = nullptr; |
16901 | const CorInfoHelpFunc helper = eeGetHelperNum(call->gtCallMethHnd); |
16902 | |
16903 | switch (helper) |
16904 | { |
16905 | case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE: |
16906 | case CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE_MAYBENULL: |
16907 | { |
16908 | // Note for some runtimes these helpers return exact types. |
16909 | // |
16910 | // But in those cases the types are also sealed, so there's no |
16911 | // need to claim exactness here. |
16912 | const bool helperResultNonNull = (helper == CORINFO_HELP_TYPEHANDLE_TO_RUNTIMETYPE); |
16913 | CORINFO_CLASS_HANDLE runtimeType = info.compCompHnd->getBuiltinClass(CLASSID_RUNTIME_TYPE); |
16914 | |
16915 | assert(runtimeType != NO_CLASS_HANDLE); |
16916 | |
16917 | objClass = runtimeType; |
16918 | *pIsNonNull = helperResultNonNull; |
16919 | break; |
16920 | } |
16921 | |
16922 | case CORINFO_HELP_CHKCASTCLASS: |
16923 | case CORINFO_HELP_CHKCASTANY: |
16924 | case CORINFO_HELP_CHKCASTARRAY: |
16925 | case CORINFO_HELP_CHKCASTINTERFACE: |
16926 | case CORINFO_HELP_CHKCASTCLASS_SPECIAL: |
16927 | case CORINFO_HELP_ISINSTANCEOFINTERFACE: |
16928 | case CORINFO_HELP_ISINSTANCEOFARRAY: |
16929 | case CORINFO_HELP_ISINSTANCEOFCLASS: |
16930 | case CORINFO_HELP_ISINSTANCEOFANY: |
16931 | { |
16932 | // Fetch the class handle from the helper call arglist |
16933 | GenTreeArgList* args = call->gtCallArgs; |
16934 | GenTree* typeArg = args->Current(); |
16935 | CORINFO_CLASS_HANDLE castHnd = gtGetHelperArgClassHandle(typeArg); |
16936 | |
16937 | // We generally assume the type being cast to is the best type |
16938 | // for the result, unless it is an interface type. |
16939 | // |
16940 | // TODO-CQ: when we have default interface methods then |
16941 | // this might not be the best assumption. We could also |
16942 | // explore calling something like mergeClasses to identify |
16943 | // the more specific class. A similar issue arises when |
16944 | // typing the temp in impCastClassOrIsInstToTree, when we |
16945 | // expand the cast inline. |
16946 | if (castHnd != nullptr) |
16947 | { |
16948 | DWORD attrs = info.compCompHnd->getClassAttribs(castHnd); |
16949 | |
16950 | if ((attrs & CORINFO_FLG_INTERFACE) != 0) |
16951 | { |
16952 | castHnd = nullptr; |
16953 | } |
16954 | } |
16955 | |
16956 | // If we don't have a good estimate for the type we can use the |
16957 | // type from the value being cast instead. |
16958 | if (castHnd == nullptr) |
16959 | { |
16960 | GenTree* valueArg = args->Rest()->Current(); |
16961 | castHnd = gtGetClassHandle(valueArg, pIsExact, pIsNonNull); |
16962 | } |
16963 | |
16964 | // We don't know at jit time if the cast will succeed or fail, but if it |
16965 | // fails at runtime then an exception is thrown for cast helpers, or the |
16966 | // result is set null for instance helpers. |
16967 | // |
16968 | // So it safe to claim the result has the cast type. |
16969 | // Note we don't know for sure that it is exactly this type. |
16970 | if (castHnd != nullptr) |
16971 | { |
16972 | objClass = castHnd; |
16973 | } |
16974 | |
16975 | break; |
16976 | } |
16977 | |
16978 | default: |
16979 | break; |
16980 | } |
16981 | |
16982 | return objClass; |
16983 | } |
16984 | |
16985 | //------------------------------------------------------------------------ |
16986 | // gtGetArrayElementClassHandle: find class handle for elements of an array |
16987 | // of ref types |
16988 | // |
16989 | // Arguments: |
16990 | // array -- array to find handle for |
16991 | // |
16992 | // Return Value: |
16993 | // nullptr if element class handle is unknown, otherwise the class handle. |
16994 | |
16995 | CORINFO_CLASS_HANDLE Compiler::gtGetArrayElementClassHandle(GenTree* array) |
16996 | { |
16997 | bool isArrayExact = false; |
16998 | bool isArrayNonNull = false; |
16999 | CORINFO_CLASS_HANDLE arrayClassHnd = gtGetClassHandle(array, &isArrayExact, &isArrayNonNull); |
17000 | |
17001 | if (arrayClassHnd != nullptr) |
17002 | { |
17003 | // We know the class of the reference |
17004 | DWORD attribs = info.compCompHnd->getClassAttribs(arrayClassHnd); |
17005 | |
17006 | if ((attribs & CORINFO_FLG_ARRAY) != 0) |
17007 | { |
17008 | // We know for sure it is an array |
17009 | CORINFO_CLASS_HANDLE elemClassHnd = nullptr; |
17010 | CorInfoType arrayElemType = info.compCompHnd->getChildType(arrayClassHnd, &elemClassHnd); |
17011 | |
17012 | if (arrayElemType == CORINFO_TYPE_CLASS) |
17013 | { |
17014 | // We know it is an array of ref types |
17015 | return elemClassHnd; |
17016 | } |
17017 | } |
17018 | } |
17019 | |
17020 | return nullptr; |
17021 | } |
17022 | |
17023 | //------------------------------------------------------------------------ |
17024 | // gtGetFieldClassHandle: find class handle for a field |
17025 | // |
17026 | // Arguments: |
17027 | // fieldHnd - field handle for field in question |
17028 | // pIsExact - [OUT] true if type is known exactly |
17029 | // pIsNonNull - [OUT] true if field value is not null |
17030 | // |
17031 | // Return Value: |
17032 | // nullptr if helper call result is not a ref class, or the class handle |
17033 | // is unknown, otherwise the class handle. |
17034 | // |
17035 | // May examine runtime state of static field instances. |
17036 | |
17037 | CORINFO_CLASS_HANDLE Compiler::gtGetFieldClassHandle(CORINFO_FIELD_HANDLE fieldHnd, bool* pIsExact, bool* pIsNonNull) |
17038 | { |
17039 | CORINFO_CLASS_HANDLE fieldClass = nullptr; |
17040 | CorInfoType fieldCorType = info.compCompHnd->getFieldType(fieldHnd, &fieldClass); |
17041 | |
17042 | if (fieldCorType == CORINFO_TYPE_CLASS) |
17043 | { |
17044 | // Optionally, look at the actual type of the field's value |
17045 | bool queryForCurrentClass = true; |
17046 | INDEBUG(queryForCurrentClass = (JitConfig.JitQueryCurrentStaticFieldClass() > 0);); |
17047 | |
17048 | if (queryForCurrentClass) |
17049 | { |
17050 | |
17051 | #if DEBUG |
17052 | const char* fieldClassName = nullptr; |
17053 | const char* fieldName = eeGetFieldName(fieldHnd, &fieldClassName); |
17054 | JITDUMP("Querying runtime about current class of field %s.%s (declared as %s)\n" , fieldClassName, fieldName, |
17055 | eeGetClassName(fieldClass)); |
17056 | #endif // DEBUG |
17057 | |
17058 | // Is this a fully initialized init-only static field? |
17059 | // |
17060 | // Note we're not asking for speculative results here, yet. |
17061 | CORINFO_CLASS_HANDLE currentClass = info.compCompHnd->getStaticFieldCurrentClass(fieldHnd); |
17062 | |
17063 | if (currentClass != NO_CLASS_HANDLE) |
17064 | { |
17065 | // Yes! We know the class exactly and can rely on this to always be true. |
17066 | fieldClass = currentClass; |
17067 | *pIsExact = true; |
17068 | *pIsNonNull = true; |
17069 | JITDUMP("Runtime reports field is init-only and initialized and has class %s\n" , |
17070 | eeGetClassName(fieldClass)); |
17071 | } |
17072 | else |
17073 | { |
17074 | JITDUMP("Field's current class not available\n" ); |
17075 | } |
17076 | } |
17077 | } |
17078 | |
17079 | return fieldClass; |
17080 | } |
17081 | |
17082 | //------------------------------------------------------------------------ |
17083 | // gtIsGCStaticBaseHelperCall: true if tree is fetching the gc static base |
17084 | // for a subsequent static field access |
17085 | // |
17086 | // Arguments: |
17087 | // tree - tree to consider |
17088 | // |
17089 | // Return Value: |
17090 | // true if the tree is a suitable helper call |
17091 | // |
17092 | // Notes: |
17093 | // Excludes R2R helpers as they specify the target field in a way |
17094 | // that is opaque to the jit. |
17095 | |
17096 | bool Compiler::gtIsStaticGCBaseHelperCall(GenTree* tree) |
17097 | { |
17098 | if (tree->OperGet() != GT_CALL) |
17099 | { |
17100 | return false; |
17101 | } |
17102 | |
17103 | GenTreeCall* call = tree->AsCall(); |
17104 | |
17105 | if (call->gtCallType != CT_HELPER) |
17106 | { |
17107 | return false; |
17108 | } |
17109 | |
17110 | const CorInfoHelpFunc helper = eeGetHelperNum(call->gtCallMethHnd); |
17111 | |
17112 | switch (helper) |
17113 | { |
17114 | // We are looking for a REF type so only need to check for the GC base helpers |
17115 | case CORINFO_HELP_GETGENERICS_GCSTATIC_BASE: |
17116 | case CORINFO_HELP_GETSHARED_GCSTATIC_BASE: |
17117 | case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_NOCTOR: |
17118 | case CORINFO_HELP_GETSHARED_GCSTATIC_BASE_DYNAMICCLASS: |
17119 | case CORINFO_HELP_GETGENERICS_GCTHREADSTATIC_BASE: |
17120 | case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE: |
17121 | case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_NOCTOR: |
17122 | case CORINFO_HELP_GETSHARED_GCTHREADSTATIC_BASE_DYNAMICCLASS: |
17123 | return true; |
17124 | default: |
17125 | break; |
17126 | } |
17127 | |
17128 | return false; |
17129 | } |
17130 | |
17131 | void GenTree::ParseArrayAddress( |
17132 | Compiler* comp, ArrayInfo* arrayInfo, GenTree** pArr, ValueNum* pInxVN, FieldSeqNode** pFldSeq) |
17133 | { |
17134 | *pArr = nullptr; |
17135 | ValueNum inxVN = ValueNumStore::NoVN; |
17136 | target_ssize_t offset = 0; |
17137 | FieldSeqNode* fldSeq = nullptr; |
17138 | |
17139 | ParseArrayAddressWork(comp, 1, pArr, &inxVN, &offset, &fldSeq); |
17140 | |
17141 | // If we didn't find an array reference (perhaps it is the constant null?) we will give up. |
17142 | if (*pArr == nullptr) |
17143 | { |
17144 | return; |
17145 | } |
17146 | |
17147 | // OK, new we have to figure out if any part of the "offset" is a constant contribution to the index. |
17148 | // First, sum the offsets of any fields in fldSeq. |
17149 | unsigned fieldOffsets = 0; |
17150 | FieldSeqNode* fldSeqIter = fldSeq; |
17151 | // Also, find the first non-pseudo field... |
17152 | assert(*pFldSeq == nullptr); |
17153 | while (fldSeqIter != nullptr) |
17154 | { |
17155 | if (fldSeqIter == FieldSeqStore::NotAField()) |
17156 | { |
17157 | // TODO-Review: A NotAField here indicates a failure to properly maintain the field sequence |
17158 | // See test case self_host_tests_x86\jit\regression\CLR-x86-JIT\v1-m12-beta2\ b70992\ b70992.exe |
17159 | // Safest thing to do here is to drop back to MinOpts |
17160 | CLANG_FORMAT_COMMENT_ANCHOR; |
17161 | |
17162 | #ifdef DEBUG |
17163 | if (comp->opts.optRepeat) |
17164 | { |
17165 | // We don't guarantee preserving these annotations through the entire optimizer, so |
17166 | // just conservatively return null if under optRepeat. |
17167 | *pArr = nullptr; |
17168 | return; |
17169 | } |
17170 | #endif // DEBUG |
17171 | noway_assert(!"fldSeqIter is NotAField() in ParseArrayAddress" ); |
17172 | } |
17173 | |
17174 | if (!FieldSeqStore::IsPseudoField(fldSeqIter->m_fieldHnd)) |
17175 | { |
17176 | if (*pFldSeq == nullptr) |
17177 | { |
17178 | *pFldSeq = fldSeqIter; |
17179 | } |
17180 | CORINFO_CLASS_HANDLE fldCls = nullptr; |
17181 | noway_assert(fldSeqIter->m_fieldHnd != nullptr); |
17182 | CorInfoType cit = comp->info.compCompHnd->getFieldType(fldSeqIter->m_fieldHnd, &fldCls); |
17183 | fieldOffsets += comp->compGetTypeSize(cit, fldCls); |
17184 | } |
17185 | fldSeqIter = fldSeqIter->m_next; |
17186 | } |
17187 | |
17188 | // Is there some portion of the "offset" beyond the first-elem offset and the struct field suffix we just computed? |
17189 | if (!FitsIn<target_ssize_t>(fieldOffsets + arrayInfo->m_elemOffset) || |
17190 | !FitsIn<target_ssize_t>(arrayInfo->m_elemSize)) |
17191 | { |
17192 | // This seems unlikely, but no harm in being safe... |
17193 | *pInxVN = comp->GetValueNumStore()->VNForExpr(nullptr, TYP_INT); |
17194 | return; |
17195 | } |
17196 | // Otherwise... |
17197 | target_ssize_t offsetAccountedFor = static_cast<target_ssize_t>(fieldOffsets + arrayInfo->m_elemOffset); |
17198 | target_ssize_t elemSize = static_cast<target_ssize_t>(arrayInfo->m_elemSize); |
17199 | |
17200 | target_ssize_t constIndOffset = offset - offsetAccountedFor; |
17201 | // This should be divisible by the element size... |
17202 | assert((constIndOffset % elemSize) == 0); |
17203 | target_ssize_t constInd = constIndOffset / elemSize; |
17204 | |
17205 | ValueNumStore* vnStore = comp->GetValueNumStore(); |
17206 | |
17207 | if (inxVN == ValueNumStore::NoVN) |
17208 | { |
17209 | // Must be a constant index. |
17210 | *pInxVN = vnStore->VNForPtrSizeIntCon(constInd); |
17211 | } |
17212 | else |
17213 | { |
17214 | // |
17215 | // Perform ((inxVN / elemSizeVN) + vnForConstInd) |
17216 | // |
17217 | |
17218 | // The value associated with the index value number (inxVN) is the offset into the array, |
17219 | // which has been scaled by element size. We need to recover the array index from that offset |
17220 | if (vnStore->IsVNConstant(inxVN)) |
17221 | { |
17222 | target_ssize_t index = vnStore->CoercedConstantValue<target_ssize_t>(inxVN); |
17223 | noway_assert(elemSize > 0 && ((index % elemSize) == 0)); |
17224 | *pInxVN = vnStore->VNForPtrSizeIntCon((index / elemSize) + constInd); |
17225 | } |
17226 | else |
17227 | { |
17228 | bool canFoldDiv = false; |
17229 | |
17230 | // If the index VN is a MUL by elemSize, see if we can eliminate it instead of adding |
17231 | // the division by elemSize. |
17232 | VNFuncApp funcApp; |
17233 | if (vnStore->GetVNFunc(inxVN, &funcApp) && funcApp.m_func == (VNFunc)GT_MUL) |
17234 | { |
17235 | ValueNum vnForElemSize = vnStore->VNForLongCon(elemSize); |
17236 | |
17237 | // One of the multiply operand is elemSize, so the resulting |
17238 | // index VN should simply be the other operand. |
17239 | if (funcApp.m_args[1] == vnForElemSize) |
17240 | { |
17241 | *pInxVN = funcApp.m_args[0]; |
17242 | canFoldDiv = true; |
17243 | } |
17244 | else if (funcApp.m_args[0] == vnForElemSize) |
17245 | { |
17246 | *pInxVN = funcApp.m_args[1]; |
17247 | canFoldDiv = true; |
17248 | } |
17249 | } |
17250 | |
17251 | // Perform ((inxVN / elemSizeVN) + vnForConstInd) |
17252 | if (!canFoldDiv) |
17253 | { |
17254 | ValueNum vnForElemSize = vnStore->VNForPtrSizeIntCon(elemSize); |
17255 | ValueNum vnForScaledInx = |
17256 | vnStore->VNForFunc(TYP_I_IMPL, GetVNFuncForOper(GT_DIV, VOK_Default), inxVN, vnForElemSize); |
17257 | *pInxVN = vnForScaledInx; |
17258 | } |
17259 | |
17260 | if (constInd != 0) |
17261 | { |
17262 | ValueNum vnForConstInd = comp->GetValueNumStore()->VNForPtrSizeIntCon(constInd); |
17263 | VNFunc vnFunc = GetVNFuncForOper(GT_ADD, VOK_Default); |
17264 | |
17265 | *pInxVN = comp->GetValueNumStore()->VNForFunc(TYP_I_IMPL, vnFunc, *pInxVN, vnForConstInd); |
17266 | } |
17267 | } |
17268 | } |
17269 | } |
17270 | |
17271 | void GenTree::ParseArrayAddressWork(Compiler* comp, |
17272 | target_ssize_t inputMul, |
17273 | GenTree** pArr, |
17274 | ValueNum* pInxVN, |
17275 | target_ssize_t* pOffset, |
17276 | FieldSeqNode** pFldSeq) |
17277 | { |
17278 | if (TypeGet() == TYP_REF) |
17279 | { |
17280 | // This must be the array pointer. |
17281 | *pArr = this; |
17282 | assert(inputMul == 1); // Can't multiply the array pointer by anything. |
17283 | } |
17284 | else |
17285 | { |
17286 | switch (OperGet()) |
17287 | { |
17288 | case GT_CNS_INT: |
17289 | *pFldSeq = comp->GetFieldSeqStore()->Append(*pFldSeq, gtIntCon.gtFieldSeq); |
17290 | assert(!gtIntCon.ImmedValNeedsReloc(comp)); |
17291 | // TODO-CrossBitness: we wouldn't need the cast below if GenTreeIntCon::gtIconVal had target_ssize_t |
17292 | // type. |
17293 | *pOffset += (inputMul * (target_ssize_t)(gtIntCon.gtIconVal)); |
17294 | return; |
17295 | |
17296 | case GT_ADD: |
17297 | case GT_SUB: |
17298 | gtOp.gtOp1->ParseArrayAddressWork(comp, inputMul, pArr, pInxVN, pOffset, pFldSeq); |
17299 | if (OperGet() == GT_SUB) |
17300 | { |
17301 | inputMul = -inputMul; |
17302 | } |
17303 | gtOp.gtOp2->ParseArrayAddressWork(comp, inputMul, pArr, pInxVN, pOffset, pFldSeq); |
17304 | return; |
17305 | |
17306 | case GT_MUL: |
17307 | { |
17308 | // If one op is a constant, continue parsing down. |
17309 | target_ssize_t subMul = 0; |
17310 | GenTree* nonConst = nullptr; |
17311 | if (gtOp.gtOp1->IsCnsIntOrI()) |
17312 | { |
17313 | // If the other arg is an int constant, and is a "not-a-field", choose |
17314 | // that as the multiplier, thus preserving constant index offsets... |
17315 | if (gtOp.gtOp2->OperGet() == GT_CNS_INT && |
17316 | gtOp.gtOp2->gtIntCon.gtFieldSeq == FieldSeqStore::NotAField()) |
17317 | { |
17318 | assert(!gtOp.gtOp2->gtIntCon.ImmedValNeedsReloc(comp)); |
17319 | // TODO-CrossBitness: we wouldn't need the cast below if GenTreeIntConCommon::gtIconVal had |
17320 | // target_ssize_t type. |
17321 | subMul = (target_ssize_t)gtOp.gtOp2->gtIntConCommon.IconValue(); |
17322 | nonConst = gtOp.gtOp1; |
17323 | } |
17324 | else |
17325 | { |
17326 | assert(!gtOp.gtOp1->gtIntCon.ImmedValNeedsReloc(comp)); |
17327 | // TODO-CrossBitness: we wouldn't need the cast below if GenTreeIntConCommon::gtIconVal had |
17328 | // target_ssize_t type. |
17329 | subMul = (target_ssize_t)gtOp.gtOp1->gtIntConCommon.IconValue(); |
17330 | nonConst = gtOp.gtOp2; |
17331 | } |
17332 | } |
17333 | else if (gtOp.gtOp2->IsCnsIntOrI()) |
17334 | { |
17335 | assert(!gtOp.gtOp2->gtIntCon.ImmedValNeedsReloc(comp)); |
17336 | // TODO-CrossBitness: we wouldn't need the cast below if GenTreeIntConCommon::gtIconVal had |
17337 | // target_ssize_t type. |
17338 | subMul = (target_ssize_t)gtOp.gtOp2->gtIntConCommon.IconValue(); |
17339 | nonConst = gtOp.gtOp1; |
17340 | } |
17341 | if (nonConst != nullptr) |
17342 | { |
17343 | nonConst->ParseArrayAddressWork(comp, inputMul * subMul, pArr, pInxVN, pOffset, pFldSeq); |
17344 | return; |
17345 | } |
17346 | // Otherwise, exit the switch, treat as a contribution to the index. |
17347 | } |
17348 | break; |
17349 | |
17350 | case GT_LSH: |
17351 | // If one op is a constant, continue parsing down. |
17352 | if (gtOp.gtOp2->IsCnsIntOrI()) |
17353 | { |
17354 | assert(!gtOp.gtOp2->gtIntCon.ImmedValNeedsReloc(comp)); |
17355 | // TODO-CrossBitness: we wouldn't need the cast below if GenTreeIntCon::gtIconVal had target_ssize_t |
17356 | // type. |
17357 | target_ssize_t subMul = target_ssize_t{1} << (target_ssize_t)gtOp.gtOp2->gtIntConCommon.IconValue(); |
17358 | gtOp.gtOp1->ParseArrayAddressWork(comp, inputMul * subMul, pArr, pInxVN, pOffset, pFldSeq); |
17359 | return; |
17360 | } |
17361 | // Otherwise, exit the switch, treat as a contribution to the index. |
17362 | break; |
17363 | |
17364 | case GT_COMMA: |
17365 | // We don't care about exceptions for this purpose. |
17366 | if ((gtOp.gtOp1->OperGet() == GT_ARR_BOUNDS_CHECK) || gtOp.gtOp1->IsNothingNode()) |
17367 | { |
17368 | gtOp.gtOp2->ParseArrayAddressWork(comp, inputMul, pArr, pInxVN, pOffset, pFldSeq); |
17369 | return; |
17370 | } |
17371 | break; |
17372 | |
17373 | default: |
17374 | break; |
17375 | } |
17376 | // If we didn't return above, must be a contribution to the non-constant part of the index VN. |
17377 | ValueNum vn = comp->GetValueNumStore()->VNLiberalNormalValue(gtVNPair); |
17378 | if (inputMul != 1) |
17379 | { |
17380 | ValueNum mulVN = comp->GetValueNumStore()->VNForLongCon(inputMul); |
17381 | vn = comp->GetValueNumStore()->VNForFunc(TypeGet(), GetVNFuncForOper(GT_MUL, VOK_Default), mulVN, vn); |
17382 | } |
17383 | if (*pInxVN == ValueNumStore::NoVN) |
17384 | { |
17385 | *pInxVN = vn; |
17386 | } |
17387 | else |
17388 | { |
17389 | *pInxVN = |
17390 | comp->GetValueNumStore()->VNForFunc(TypeGet(), GetVNFuncForOper(GT_ADD, VOK_Default), *pInxVN, vn); |
17391 | } |
17392 | } |
17393 | } |
17394 | |
17395 | bool GenTree::ParseArrayElemForm(Compiler* comp, ArrayInfo* arrayInfo, FieldSeqNode** pFldSeq) |
17396 | { |
17397 | if (OperIsIndir()) |
17398 | { |
17399 | if (gtFlags & GTF_IND_ARR_INDEX) |
17400 | { |
17401 | bool b = comp->GetArrayInfoMap()->Lookup(this, arrayInfo); |
17402 | assert(b); |
17403 | return true; |
17404 | } |
17405 | |
17406 | // Otherwise... |
17407 | GenTree* addr = AsIndir()->Addr(); |
17408 | return addr->ParseArrayElemAddrForm(comp, arrayInfo, pFldSeq); |
17409 | } |
17410 | else |
17411 | { |
17412 | return false; |
17413 | } |
17414 | } |
17415 | |
17416 | bool GenTree::ParseArrayElemAddrForm(Compiler* comp, ArrayInfo* arrayInfo, FieldSeqNode** pFldSeq) |
17417 | { |
17418 | switch (OperGet()) |
17419 | { |
17420 | case GT_ADD: |
17421 | { |
17422 | GenTree* arrAddr = nullptr; |
17423 | GenTree* offset = nullptr; |
17424 | if (gtOp.gtOp1->TypeGet() == TYP_BYREF) |
17425 | { |
17426 | arrAddr = gtOp.gtOp1; |
17427 | offset = gtOp.gtOp2; |
17428 | } |
17429 | else if (gtOp.gtOp2->TypeGet() == TYP_BYREF) |
17430 | { |
17431 | arrAddr = gtOp.gtOp2; |
17432 | offset = gtOp.gtOp1; |
17433 | } |
17434 | else |
17435 | { |
17436 | return false; |
17437 | } |
17438 | if (!offset->ParseOffsetForm(comp, pFldSeq)) |
17439 | { |
17440 | return false; |
17441 | } |
17442 | return arrAddr->ParseArrayElemAddrForm(comp, arrayInfo, pFldSeq); |
17443 | } |
17444 | |
17445 | case GT_ADDR: |
17446 | { |
17447 | GenTree* addrArg = gtOp.gtOp1; |
17448 | if (addrArg->OperGet() != GT_IND) |
17449 | { |
17450 | return false; |
17451 | } |
17452 | else |
17453 | { |
17454 | // The "Addr" node might be annotated with a zero-offset field sequence. |
17455 | FieldSeqNode* zeroOffsetFldSeq = nullptr; |
17456 | if (comp->GetZeroOffsetFieldMap()->Lookup(this, &zeroOffsetFldSeq)) |
17457 | { |
17458 | *pFldSeq = comp->GetFieldSeqStore()->Append(*pFldSeq, zeroOffsetFldSeq); |
17459 | } |
17460 | return addrArg->ParseArrayElemForm(comp, arrayInfo, pFldSeq); |
17461 | } |
17462 | } |
17463 | |
17464 | default: |
17465 | return false; |
17466 | } |
17467 | } |
17468 | |
17469 | bool GenTree::ParseOffsetForm(Compiler* comp, FieldSeqNode** pFldSeq) |
17470 | { |
17471 | switch (OperGet()) |
17472 | { |
17473 | case GT_CNS_INT: |
17474 | { |
17475 | GenTreeIntCon* icon = AsIntCon(); |
17476 | *pFldSeq = comp->GetFieldSeqStore()->Append(*pFldSeq, icon->gtFieldSeq); |
17477 | return true; |
17478 | } |
17479 | |
17480 | case GT_ADD: |
17481 | if (!gtOp.gtOp1->ParseOffsetForm(comp, pFldSeq)) |
17482 | { |
17483 | return false; |
17484 | } |
17485 | return gtOp.gtOp2->ParseOffsetForm(comp, pFldSeq); |
17486 | |
17487 | default: |
17488 | return false; |
17489 | } |
17490 | } |
17491 | |
17492 | void GenTree::LabelIndex(Compiler* comp, bool isConst) |
17493 | { |
17494 | switch (OperGet()) |
17495 | { |
17496 | case GT_CNS_INT: |
17497 | // If we got here, this is a contribution to the constant part of the index. |
17498 | if (isConst) |
17499 | { |
17500 | gtIntCon.gtFieldSeq = |
17501 | comp->GetFieldSeqStore()->CreateSingleton(FieldSeqStore::ConstantIndexPseudoField); |
17502 | } |
17503 | return; |
17504 | |
17505 | case GT_LCL_VAR: |
17506 | gtFlags |= GTF_VAR_ARR_INDEX; |
17507 | return; |
17508 | |
17509 | case GT_ADD: |
17510 | case GT_SUB: |
17511 | gtOp.gtOp1->LabelIndex(comp, isConst); |
17512 | gtOp.gtOp2->LabelIndex(comp, isConst); |
17513 | break; |
17514 | |
17515 | case GT_CAST: |
17516 | gtOp.gtOp1->LabelIndex(comp, isConst); |
17517 | break; |
17518 | |
17519 | case GT_ARR_LENGTH: |
17520 | gtFlags |= GTF_ARRLEN_ARR_IDX; |
17521 | return; |
17522 | |
17523 | default: |
17524 | // For all other operators, peel off one constant; and then label the other if it's also a constant. |
17525 | if (OperIsArithmetic() || OperIsCompare()) |
17526 | { |
17527 | if (gtOp.gtOp2->OperGet() == GT_CNS_INT) |
17528 | { |
17529 | gtOp.gtOp1->LabelIndex(comp, isConst); |
17530 | break; |
17531 | } |
17532 | else if (gtOp.gtOp1->OperGet() == GT_CNS_INT) |
17533 | { |
17534 | gtOp.gtOp2->LabelIndex(comp, isConst); |
17535 | break; |
17536 | } |
17537 | // Otherwise continue downward on both, labeling vars. |
17538 | gtOp.gtOp1->LabelIndex(comp, false); |
17539 | gtOp.gtOp2->LabelIndex(comp, false); |
17540 | } |
17541 | break; |
17542 | } |
17543 | } |
17544 | |
17545 | // Note that the value of the below field doesn't matter; it exists only to provide a distinguished address. |
17546 | // |
17547 | // static |
17548 | FieldSeqNode FieldSeqStore::s_notAField(nullptr, nullptr); |
17549 | |
17550 | // FieldSeqStore methods. |
17551 | FieldSeqStore::FieldSeqStore(CompAllocator alloc) : m_alloc(alloc), m_canonMap(new (alloc) FieldSeqNodeCanonMap(alloc)) |
17552 | { |
17553 | } |
17554 | |
17555 | FieldSeqNode* FieldSeqStore::CreateSingleton(CORINFO_FIELD_HANDLE fieldHnd) |
17556 | { |
17557 | FieldSeqNode fsn(fieldHnd, nullptr); |
17558 | FieldSeqNode* res = nullptr; |
17559 | if (m_canonMap->Lookup(fsn, &res)) |
17560 | { |
17561 | return res; |
17562 | } |
17563 | else |
17564 | { |
17565 | res = m_alloc.allocate<FieldSeqNode>(1); |
17566 | *res = fsn; |
17567 | m_canonMap->Set(fsn, res); |
17568 | return res; |
17569 | } |
17570 | } |
17571 | |
17572 | FieldSeqNode* FieldSeqStore::Append(FieldSeqNode* a, FieldSeqNode* b) |
17573 | { |
17574 | if (a == nullptr) |
17575 | { |
17576 | return b; |
17577 | } |
17578 | else if (a == NotAField()) |
17579 | { |
17580 | return NotAField(); |
17581 | } |
17582 | else if (b == nullptr) |
17583 | { |
17584 | return a; |
17585 | } |
17586 | else if (b == NotAField()) |
17587 | { |
17588 | return NotAField(); |
17589 | // Extremely special case for ConstantIndex pseudo-fields -- appending consecutive such |
17590 | // together collapse to one. |
17591 | } |
17592 | else if (a->m_next == nullptr && a->m_fieldHnd == ConstantIndexPseudoField && |
17593 | b->m_fieldHnd == ConstantIndexPseudoField) |
17594 | { |
17595 | return b; |
17596 | } |
17597 | else |
17598 | { |
17599 | FieldSeqNode* tmp = Append(a->m_next, b); |
17600 | FieldSeqNode fsn(a->m_fieldHnd, tmp); |
17601 | FieldSeqNode* res = nullptr; |
17602 | if (m_canonMap->Lookup(fsn, &res)) |
17603 | { |
17604 | return res; |
17605 | } |
17606 | else |
17607 | { |
17608 | res = m_alloc.allocate<FieldSeqNode>(1); |
17609 | *res = fsn; |
17610 | m_canonMap->Set(fsn, res); |
17611 | return res; |
17612 | } |
17613 | } |
17614 | } |
17615 | |
17616 | // Static vars. |
17617 | int FieldSeqStore::FirstElemPseudoFieldStruct; |
17618 | int FieldSeqStore::ConstantIndexPseudoFieldStruct; |
17619 | |
17620 | CORINFO_FIELD_HANDLE FieldSeqStore::FirstElemPseudoField = |
17621 | (CORINFO_FIELD_HANDLE)&FieldSeqStore::FirstElemPseudoFieldStruct; |
17622 | CORINFO_FIELD_HANDLE FieldSeqStore::ConstantIndexPseudoField = |
17623 | (CORINFO_FIELD_HANDLE)&FieldSeqStore::ConstantIndexPseudoFieldStruct; |
17624 | |
17625 | bool FieldSeqNode::IsFirstElemFieldSeq() |
17626 | { |
17627 | // this must be non-null per ISO C++ |
17628 | return m_fieldHnd == FieldSeqStore::FirstElemPseudoField; |
17629 | } |
17630 | |
17631 | bool FieldSeqNode::IsConstantIndexFieldSeq() |
17632 | { |
17633 | // this must be non-null per ISO C++ |
17634 | return m_fieldHnd == FieldSeqStore::ConstantIndexPseudoField; |
17635 | } |
17636 | |
17637 | bool FieldSeqNode::IsPseudoField() |
17638 | { |
17639 | if (this == nullptr) |
17640 | { |
17641 | return false; |
17642 | } |
17643 | return m_fieldHnd == FieldSeqStore::FirstElemPseudoField || m_fieldHnd == FieldSeqStore::ConstantIndexPseudoField; |
17644 | } |
17645 | |
17646 | #ifdef FEATURE_SIMD |
17647 | GenTreeSIMD* Compiler::gtNewSIMDNode( |
17648 | var_types type, GenTree* op1, SIMDIntrinsicID simdIntrinsicID, var_types baseType, unsigned size) |
17649 | { |
17650 | assert(op1 != nullptr); |
17651 | SetOpLclRelatedToSIMDIntrinsic(op1); |
17652 | |
17653 | return new (this, GT_SIMD) GenTreeSIMD(type, op1, simdIntrinsicID, baseType, size); |
17654 | } |
17655 | |
17656 | GenTreeSIMD* Compiler::gtNewSIMDNode( |
17657 | var_types type, GenTree* op1, GenTree* op2, SIMDIntrinsicID simdIntrinsicID, var_types baseType, unsigned size) |
17658 | { |
17659 | assert(op1 != nullptr); |
17660 | SetOpLclRelatedToSIMDIntrinsic(op1); |
17661 | SetOpLclRelatedToSIMDIntrinsic(op2); |
17662 | |
17663 | return new (this, GT_SIMD) GenTreeSIMD(type, op1, op2, simdIntrinsicID, baseType, size); |
17664 | } |
17665 | |
17666 | //------------------------------------------------------------------- |
17667 | // SetOpLclRelatedToSIMDIntrinsic: Determine if the tree has a local var that needs to be set |
17668 | // as used by a SIMD intrinsic, and if so, set that local var appropriately. |
17669 | // |
17670 | // Arguments: |
17671 | // op - The tree, to be an operand of a new GT_SIMD node, to check. |
17672 | // |
17673 | void Compiler::SetOpLclRelatedToSIMDIntrinsic(GenTree* op) |
17674 | { |
17675 | if (op != nullptr) |
17676 | { |
17677 | if (op->OperIsLocal()) |
17678 | { |
17679 | setLclRelatedToSIMDIntrinsic(op); |
17680 | } |
17681 | else if ((op->OperGet() == GT_OBJ) && (op->gtOp.gtOp1->OperGet() == GT_ADDR) && |
17682 | op->gtOp.gtOp1->gtOp.gtOp1->OperIsLocal()) |
17683 | { |
17684 | setLclRelatedToSIMDIntrinsic(op->gtOp.gtOp1->gtOp.gtOp1); |
17685 | } |
17686 | } |
17687 | } |
17688 | |
17689 | bool GenTree::isCommutativeSIMDIntrinsic() |
17690 | { |
17691 | assert(gtOper == GT_SIMD); |
17692 | switch (AsSIMD()->gtSIMDIntrinsicID) |
17693 | { |
17694 | case SIMDIntrinsicAdd: |
17695 | case SIMDIntrinsicBitwiseAnd: |
17696 | case SIMDIntrinsicBitwiseOr: |
17697 | case SIMDIntrinsicBitwiseXor: |
17698 | case SIMDIntrinsicEqual: |
17699 | case SIMDIntrinsicMax: |
17700 | case SIMDIntrinsicMin: |
17701 | case SIMDIntrinsicMul: |
17702 | case SIMDIntrinsicOpEquality: |
17703 | case SIMDIntrinsicOpInEquality: |
17704 | return true; |
17705 | default: |
17706 | return false; |
17707 | } |
17708 | } |
17709 | #endif // FEATURE_SIMD |
17710 | |
17711 | #ifdef FEATURE_HW_INTRINSICS |
17712 | bool GenTree::isCommutativeHWIntrinsic() const |
17713 | { |
17714 | assert(gtOper == GT_HWIntrinsic); |
17715 | |
17716 | #ifdef _TARGET_XARCH_ |
17717 | return HWIntrinsicInfo::IsCommutative(AsHWIntrinsic()->gtHWIntrinsicId); |
17718 | #else |
17719 | return false; |
17720 | #endif // _TARGET_XARCH_ |
17721 | } |
17722 | |
17723 | bool GenTree::isContainableHWIntrinsic() const |
17724 | { |
17725 | assert(gtOper == GT_HWIntrinsic); |
17726 | |
17727 | #ifdef _TARGET_XARCH_ |
17728 | switch (AsHWIntrinsic()->gtHWIntrinsicId) |
17729 | { |
17730 | case NI_SSE_LoadAlignedVector128: |
17731 | case NI_SSE_LoadScalarVector128: |
17732 | case NI_SSE_LoadVector128: |
17733 | case NI_SSE2_LoadAlignedVector128: |
17734 | case NI_SSE2_LoadScalarVector128: |
17735 | case NI_SSE2_LoadVector128: |
17736 | case NI_AVX_LoadAlignedVector256: |
17737 | case NI_AVX_LoadVector256: |
17738 | { |
17739 | return true; |
17740 | } |
17741 | |
17742 | default: |
17743 | { |
17744 | return false; |
17745 | } |
17746 | } |
17747 | #else |
17748 | return false; |
17749 | #endif // _TARGET_XARCH_ |
17750 | } |
17751 | |
17752 | bool GenTree::isRMWHWIntrinsic(Compiler* comp) |
17753 | { |
17754 | assert(gtOper == GT_HWIntrinsic); |
17755 | assert(comp != nullptr); |
17756 | |
17757 | #ifdef _TARGET_XARCH_ |
17758 | if (!comp->canUseVexEncoding()) |
17759 | { |
17760 | return HWIntrinsicInfo::HasRMWSemantics(AsHWIntrinsic()->gtHWIntrinsicId); |
17761 | } |
17762 | |
17763 | switch (AsHWIntrinsic()->gtHWIntrinsicId) |
17764 | { |
17765 | // TODO-XArch-Cleanup: Move this switch block to be table driven. |
17766 | |
17767 | case NI_SSE42_Crc32: |
17768 | case NI_SSE42_X64_Crc32: |
17769 | case NI_FMA_MultiplyAdd: |
17770 | case NI_FMA_MultiplyAddNegated: |
17771 | case NI_FMA_MultiplyAddNegatedScalar: |
17772 | case NI_FMA_MultiplyAddScalar: |
17773 | case NI_FMA_MultiplyAddSubtract: |
17774 | case NI_FMA_MultiplySubtract: |
17775 | case NI_FMA_MultiplySubtractAdd: |
17776 | case NI_FMA_MultiplySubtractNegated: |
17777 | case NI_FMA_MultiplySubtractNegatedScalar: |
17778 | case NI_FMA_MultiplySubtractScalar: |
17779 | { |
17780 | return true; |
17781 | } |
17782 | |
17783 | default: |
17784 | { |
17785 | return false; |
17786 | } |
17787 | } |
17788 | #else |
17789 | return false; |
17790 | #endif // _TARGET_XARCH_ |
17791 | } |
17792 | |
17793 | GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type, |
17794 | NamedIntrinsic hwIntrinsicID, |
17795 | var_types baseType, |
17796 | unsigned size) |
17797 | { |
17798 | return new (this, GT_HWIntrinsic) GenTreeHWIntrinsic(type, hwIntrinsicID, baseType, size); |
17799 | } |
17800 | |
17801 | GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode( |
17802 | var_types type, GenTree* op1, NamedIntrinsic hwIntrinsicID, var_types baseType, unsigned simdSize) |
17803 | { |
17804 | SetOpLclRelatedToSIMDIntrinsic(op1); |
17805 | |
17806 | return new (this, GT_HWIntrinsic) GenTreeHWIntrinsic(type, op1, hwIntrinsicID, baseType, simdSize); |
17807 | } |
17808 | |
17809 | GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode( |
17810 | var_types type, GenTree* op1, GenTree* op2, NamedIntrinsic hwIntrinsicID, var_types baseType, unsigned simdSize) |
17811 | { |
17812 | SetOpLclRelatedToSIMDIntrinsic(op1); |
17813 | SetOpLclRelatedToSIMDIntrinsic(op2); |
17814 | |
17815 | return new (this, GT_HWIntrinsic) GenTreeHWIntrinsic(type, op1, op2, hwIntrinsicID, baseType, simdSize); |
17816 | } |
17817 | |
17818 | GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type, |
17819 | GenTree* op1, |
17820 | GenTree* op2, |
17821 | GenTree* op3, |
17822 | NamedIntrinsic hwIntrinsicID, |
17823 | var_types baseType, |
17824 | unsigned size) |
17825 | { |
17826 | SetOpLclRelatedToSIMDIntrinsic(op1); |
17827 | SetOpLclRelatedToSIMDIntrinsic(op2); |
17828 | SetOpLclRelatedToSIMDIntrinsic(op3); |
17829 | |
17830 | return new (this, GT_HWIntrinsic) |
17831 | GenTreeHWIntrinsic(type, gtNewArgList(op1, op2, op3), hwIntrinsicID, baseType, size); |
17832 | } |
17833 | |
17834 | GenTreeHWIntrinsic* Compiler::gtNewSimdHWIntrinsicNode(var_types type, |
17835 | GenTree* op1, |
17836 | GenTree* op2, |
17837 | GenTree* op3, |
17838 | GenTree* op4, |
17839 | NamedIntrinsic hwIntrinsicID, |
17840 | var_types baseType, |
17841 | unsigned size) |
17842 | { |
17843 | SetOpLclRelatedToSIMDIntrinsic(op1); |
17844 | SetOpLclRelatedToSIMDIntrinsic(op2); |
17845 | SetOpLclRelatedToSIMDIntrinsic(op3); |
17846 | SetOpLclRelatedToSIMDIntrinsic(op4); |
17847 | |
17848 | return new (this, GT_HWIntrinsic) |
17849 | GenTreeHWIntrinsic(type, gtNewArgList(op1, op2, op3, op4), hwIntrinsicID, baseType, size); |
17850 | } |
17851 | |
17852 | GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode(var_types type, GenTree* op1, NamedIntrinsic hwIntrinsicID) |
17853 | { |
17854 | SetOpLclRelatedToSIMDIntrinsic(op1); |
17855 | |
17856 | return new (this, GT_HWIntrinsic) GenTreeHWIntrinsic(type, op1, hwIntrinsicID, TYP_UNKNOWN, 0); |
17857 | } |
17858 | |
17859 | GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode(var_types type, |
17860 | GenTree* op1, |
17861 | GenTree* op2, |
17862 | NamedIntrinsic hwIntrinsicID) |
17863 | { |
17864 | SetOpLclRelatedToSIMDIntrinsic(op1); |
17865 | SetOpLclRelatedToSIMDIntrinsic(op2); |
17866 | |
17867 | return new (this, GT_HWIntrinsic) GenTreeHWIntrinsic(type, op1, op2, hwIntrinsicID, TYP_UNKNOWN, 0); |
17868 | } |
17869 | |
17870 | GenTreeHWIntrinsic* Compiler::gtNewScalarHWIntrinsicNode( |
17871 | var_types type, GenTree* op1, GenTree* op2, GenTree* op3, NamedIntrinsic hwIntrinsicID) |
17872 | { |
17873 | SetOpLclRelatedToSIMDIntrinsic(op1); |
17874 | SetOpLclRelatedToSIMDIntrinsic(op2); |
17875 | SetOpLclRelatedToSIMDIntrinsic(op3); |
17876 | |
17877 | return new (this, GT_HWIntrinsic) |
17878 | GenTreeHWIntrinsic(type, gtNewArgList(op1, op2, op3), hwIntrinsicID, TYP_UNKNOWN, 0); |
17879 | } |
17880 | |
17881 | //--------------------------------------------------------------------------------------- |
17882 | // gtNewMustThrowException: |
17883 | // create a throw node (calling into JIT helper) that must be thrown. |
17884 | // The result would be a comma node: COMMA(jithelperthrow(void), x) where x's type should be specified. |
17885 | // |
17886 | // Arguments |
17887 | // helper - JIT helper ID |
17888 | // type - return type of the node |
17889 | // |
17890 | // Return Value |
17891 | // pointer to the throw node |
17892 | // |
17893 | GenTree* Compiler::gtNewMustThrowException(unsigned helper, var_types type, CORINFO_CLASS_HANDLE clsHnd) |
17894 | { |
17895 | GenTreeCall* node = gtNewHelperCallNode(helper, TYP_VOID); |
17896 | node->gtCallMoreFlags |= GTF_CALL_M_DOES_NOT_RETURN; |
17897 | if (type != TYP_VOID) |
17898 | { |
17899 | unsigned dummyTemp = lvaGrabTemp(true DEBUGARG("dummy temp of must thrown exception" )); |
17900 | if (type == TYP_STRUCT) |
17901 | { |
17902 | lvaSetStruct(dummyTemp, clsHnd, false); |
17903 | type = lvaTable[dummyTemp].lvType; // struct type is normalized |
17904 | } |
17905 | else |
17906 | { |
17907 | lvaTable[dummyTemp].lvType = type; |
17908 | } |
17909 | GenTree* dummyNode = gtNewLclvNode(dummyTemp, type); |
17910 | return gtNewOperNode(GT_COMMA, type, node, dummyNode); |
17911 | } |
17912 | return node; |
17913 | } |
17914 | |
17915 | // Returns true for the HW Instrinsic instructions that have MemoryLoad semantics, false otherwise |
17916 | bool GenTreeHWIntrinsic::OperIsMemoryLoad() |
17917 | { |
17918 | #ifdef _TARGET_XARCH_ |
17919 | // Some xarch instructions have MemoryLoad sematics |
17920 | HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(gtHWIntrinsicId); |
17921 | if (category == HW_Category_MemoryLoad) |
17922 | { |
17923 | return true; |
17924 | } |
17925 | else if (HWIntrinsicInfo::MaybeMemoryLoad(gtHWIntrinsicId)) |
17926 | { |
17927 | // Some AVX intrinsic (without HW_Category_MemoryLoad) also have MemoryLoad semantics |
17928 | if (category == HW_Category_SIMDScalar) |
17929 | { |
17930 | // Avx2.BroadcastScalarToVector128/256 have vector and pointer overloads both, e.g., |
17931 | // Vector128<byte> BroadcastScalarToVector128(Vector128<byte> value) |
17932 | // Vector128<byte> BroadcastScalarToVector128(byte* source) |
17933 | // So, we need to check the argument's type is memory-reference (TYP_I_IMPL) or not |
17934 | assert(HWIntrinsicInfo::lookupNumArgs(this) == 1); |
17935 | return (gtHWIntrinsicId == NI_AVX2_BroadcastScalarToVector128 || |
17936 | gtHWIntrinsicId == NI_AVX2_BroadcastScalarToVector256) && |
17937 | gtOp.gtOp1->TypeGet() == TYP_I_IMPL; |
17938 | } |
17939 | else if (category == HW_Category_IMM) |
17940 | { |
17941 | // Do we have less than 3 operands? |
17942 | if (HWIntrinsicInfo::lookupNumArgs(this) < 3) |
17943 | { |
17944 | return false; |
17945 | } |
17946 | else // We have 3 or more operands/args |
17947 | { |
17948 | // All the Avx2.Gather* are "load" instructions |
17949 | if (HWIntrinsicInfo::isAVX2GatherIntrinsic(gtHWIntrinsicId)) |
17950 | { |
17951 | return true; |
17952 | } |
17953 | |
17954 | GenTreeArgList* argList = gtOp.gtOp1->AsArgList(); |
17955 | |
17956 | // Avx/Avx2.InsertVector128 have vector and pointer overloads both, e.g., |
17957 | // Vector256<sbyte> InsertVector128(Vector256<sbyte> value, Vector128<sbyte> data, byte index) |
17958 | // Vector256<sbyte> InsertVector128(Vector256<sbyte> value, sbyte* address, byte index) |
17959 | // So, we need to check the second argument's type is memory-reference (TYP_I_IMPL) or not |
17960 | if ((gtHWIntrinsicId == NI_AVX_InsertVector128 || gtHWIntrinsicId == NI_AVX2_InsertVector128) && |
17961 | (argList->Rest()->Current()->TypeGet() == TYP_I_IMPL)) // Is the type of the second arg TYP_I_IMPL? |
17962 | { |
17963 | // This is Avx/Avx2.InsertVector128 |
17964 | return true; |
17965 | } |
17966 | } |
17967 | } |
17968 | } |
17969 | #endif // _TARGET_XARCH_ |
17970 | return false; |
17971 | } |
17972 | |
17973 | // Returns true for the HW Instrinsic instructions that have MemoryStore semantics, false otherwise |
17974 | bool GenTreeHWIntrinsic::OperIsMemoryStore() |
17975 | { |
17976 | #ifdef _TARGET_XARCH_ |
17977 | // Some xarch instructions have MemoryStore sematics |
17978 | HWIntrinsicCategory category = HWIntrinsicInfo::lookupCategory(gtHWIntrinsicId); |
17979 | if (category == HW_Category_MemoryStore) |
17980 | { |
17981 | return true; |
17982 | } |
17983 | else if (HWIntrinsicInfo::MaybeMemoryStore(gtHWIntrinsicId) && |
17984 | (category == HW_Category_IMM || category == HW_Category_Scalar)) |
17985 | { |
17986 | // Some AVX intrinsic (without HW_Category_MemoryStore) also have MemoryStore semantics |
17987 | |
17988 | // Avx/Avx2.InsertVector128 have vector and pointer overloads both, e.g., |
17989 | // Vector128<sbyte> ExtractVector128(Vector256<sbyte> value, byte index) |
17990 | // void ExtractVector128(sbyte* address, Vector256<sbyte> value, byte index) |
17991 | // Bmi2/Bmi2.X64.MultiplyNoFlags may return the lower half result by a out argument |
17992 | // unsafe ulong MultiplyNoFlags(ulong left, ulong right, ulong* low) |
17993 | // |
17994 | // So, the 3-argument form is MemoryStore |
17995 | if (HWIntrinsicInfo::lookupNumArgs(this) == 3) |
17996 | { |
17997 | switch (gtHWIntrinsicId) |
17998 | { |
17999 | case NI_AVX_ExtractVector128: |
18000 | case NI_AVX2_ExtractVector128: |
18001 | case NI_BMI2_MultiplyNoFlags: |
18002 | case NI_BMI2_X64_MultiplyNoFlags: |
18003 | return true; |
18004 | default: |
18005 | return false; |
18006 | } |
18007 | } |
18008 | } |
18009 | #endif // _TARGET_XARCH_ |
18010 | return false; |
18011 | } |
18012 | |
18013 | // Returns true for the HW Instrinsic instructions that have MemoryLoad semantics, false otherwise |
18014 | bool GenTreeHWIntrinsic::OperIsMemoryLoadOrStore() |
18015 | { |
18016 | #ifdef _TARGET_XARCH_ |
18017 | return OperIsMemoryLoad() || OperIsMemoryStore(); |
18018 | #endif // _TARGET_XARCH_ |
18019 | return false; |
18020 | } |
18021 | |
18022 | #endif // FEATURE_HW_INTRINSICS |
18023 | |
18024 | //--------------------------------------------------------------------------------------- |
18025 | // InitializeStructReturnType: |
18026 | // Initialize the Return Type Descriptor for a method that returns a struct type |
18027 | // |
18028 | // Arguments |
18029 | // comp - Compiler Instance |
18030 | // retClsHnd - VM handle to the struct type returned by the method |
18031 | // |
18032 | // Return Value |
18033 | // None |
18034 | // |
18035 | void ReturnTypeDesc::InitializeStructReturnType(Compiler* comp, CORINFO_CLASS_HANDLE retClsHnd) |
18036 | { |
18037 | assert(!m_inited); |
18038 | |
18039 | #if FEATURE_MULTIREG_RET |
18040 | |
18041 | assert(retClsHnd != NO_CLASS_HANDLE); |
18042 | unsigned structSize = comp->info.compCompHnd->getClassSize(retClsHnd); |
18043 | |
18044 | Compiler::structPassingKind howToReturnStruct; |
18045 | var_types returnType = comp->getReturnTypeForStruct(retClsHnd, &howToReturnStruct, structSize); |
18046 | |
18047 | switch (howToReturnStruct) |
18048 | { |
18049 | case Compiler::SPK_EnclosingType: |
18050 | m_isEnclosingType = true; |
18051 | __fallthrough; |
18052 | |
18053 | case Compiler::SPK_PrimitiveType: |
18054 | { |
18055 | assert(returnType != TYP_UNKNOWN); |
18056 | assert(!varTypeIsStruct(returnType)); |
18057 | m_regType[0] = returnType; |
18058 | break; |
18059 | } |
18060 | |
18061 | case Compiler::SPK_ByValueAsHfa: |
18062 | { |
18063 | assert(varTypeIsStruct(returnType)); |
18064 | var_types hfaType = comp->GetHfaType(retClsHnd); |
18065 | |
18066 | // We should have an hfa struct type |
18067 | assert(varTypeIsFloating(hfaType)); |
18068 | |
18069 | // Note that the retail build issues a warning about a potential divsion by zero without this Max function |
18070 | unsigned elemSize = Max((unsigned)1, EA_SIZE_IN_BYTES(emitActualTypeSize(hfaType))); |
18071 | |
18072 | // The size of this struct should be evenly divisible by elemSize |
18073 | assert((structSize % elemSize) == 0); |
18074 | |
18075 | unsigned hfaCount = (structSize / elemSize); |
18076 | for (unsigned i = 0; i < hfaCount; ++i) |
18077 | { |
18078 | m_regType[i] = hfaType; |
18079 | } |
18080 | |
18081 | if (comp->compFloatingPointUsed == false) |
18082 | { |
18083 | comp->compFloatingPointUsed = true; |
18084 | } |
18085 | break; |
18086 | } |
18087 | |
18088 | case Compiler::SPK_ByValue: |
18089 | { |
18090 | assert(varTypeIsStruct(returnType)); |
18091 | |
18092 | #ifdef UNIX_AMD64_ABI |
18093 | |
18094 | SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc; |
18095 | comp->eeGetSystemVAmd64PassStructInRegisterDescriptor(retClsHnd, &structDesc); |
18096 | |
18097 | assert(structDesc.passedInRegisters); |
18098 | for (int i = 0; i < structDesc.eightByteCount; i++) |
18099 | { |
18100 | assert(i < MAX_RET_REG_COUNT); |
18101 | m_regType[i] = comp->GetEightByteType(structDesc, i); |
18102 | } |
18103 | |
18104 | #elif defined(_TARGET_ARM64_) |
18105 | |
18106 | // a non-HFA struct returned using two registers |
18107 | // |
18108 | assert((structSize > TARGET_POINTER_SIZE) && (structSize <= (2 * TARGET_POINTER_SIZE))); |
18109 | |
18110 | BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE}; |
18111 | comp->info.compCompHnd->getClassGClayout(retClsHnd, &gcPtrs[0]); |
18112 | for (unsigned i = 0; i < 2; ++i) |
18113 | { |
18114 | m_regType[i] = comp->getJitGCType(gcPtrs[i]); |
18115 | } |
18116 | |
18117 | #else // _TARGET_XXX_ |
18118 | |
18119 | // This target needs support here! |
18120 | // |
18121 | NYI("Unsupported TARGET returning a TYP_STRUCT in InitializeStructReturnType" ); |
18122 | |
18123 | #endif // UNIX_AMD64_ABI |
18124 | |
18125 | break; // for case SPK_ByValue |
18126 | } |
18127 | |
18128 | case Compiler::SPK_ByReference: |
18129 | |
18130 | // We are returning using the return buffer argument |
18131 | // There are no return registers |
18132 | break; |
18133 | |
18134 | default: |
18135 | |
18136 | unreached(); // By the contract of getReturnTypeForStruct we should never get here. |
18137 | |
18138 | } // end of switch (howToReturnStruct) |
18139 | |
18140 | #endif // FEATURE_MULTIREG_RET |
18141 | |
18142 | #ifdef DEBUG |
18143 | m_inited = true; |
18144 | #endif |
18145 | } |
18146 | |
18147 | //--------------------------------------------------------------------------------------- |
18148 | // InitializeLongReturnType: |
18149 | // Initialize the Return Type Descriptor for a method that returns a TYP_LONG |
18150 | // |
18151 | // Arguments |
18152 | // comp - Compiler Instance |
18153 | // |
18154 | // Return Value |
18155 | // None |
18156 | // |
18157 | void ReturnTypeDesc::InitializeLongReturnType(Compiler* comp) |
18158 | { |
18159 | #if defined(_TARGET_X86_) || defined(_TARGET_ARM_) |
18160 | |
18161 | // Setups up a ReturnTypeDesc for returning a long using two registers |
18162 | // |
18163 | assert(MAX_RET_REG_COUNT >= 2); |
18164 | m_regType[0] = TYP_INT; |
18165 | m_regType[1] = TYP_INT; |
18166 | |
18167 | #else // not (_TARGET_X86_ or _TARGET_ARM_) |
18168 | |
18169 | m_regType[0] = TYP_LONG; |
18170 | |
18171 | #endif // _TARGET_X86_ or _TARGET_ARM_ |
18172 | |
18173 | #ifdef DEBUG |
18174 | m_inited = true; |
18175 | #endif |
18176 | } |
18177 | |
18178 | //------------------------------------------------------------------- |
18179 | // GetABIReturnReg: Return ith return register as per target ABI |
18180 | // |
18181 | // Arguments: |
18182 | // idx - Index of the return register. |
18183 | // The first return register has an index of 0 and so on. |
18184 | // |
18185 | // Return Value: |
18186 | // Returns ith return register as per target ABI. |
18187 | // |
18188 | // Notes: |
18189 | // x86 and ARM return long in multiple registers. |
18190 | // ARM and ARM64 return HFA struct in multiple registers. |
18191 | // |
18192 | regNumber ReturnTypeDesc::GetABIReturnReg(unsigned idx) |
18193 | { |
18194 | unsigned count = GetReturnRegCount(); |
18195 | assert(idx < count); |
18196 | |
18197 | regNumber resultReg = REG_NA; |
18198 | |
18199 | #ifdef UNIX_AMD64_ABI |
18200 | var_types regType0 = GetReturnRegType(0); |
18201 | |
18202 | if (idx == 0) |
18203 | { |
18204 | if (varTypeIsIntegralOrI(regType0)) |
18205 | { |
18206 | resultReg = REG_INTRET; |
18207 | } |
18208 | else |
18209 | { |
18210 | noway_assert(varTypeIsFloating(regType0)); |
18211 | resultReg = REG_FLOATRET; |
18212 | } |
18213 | } |
18214 | else if (idx == 1) |
18215 | { |
18216 | var_types regType1 = GetReturnRegType(1); |
18217 | |
18218 | if (varTypeIsIntegralOrI(regType1)) |
18219 | { |
18220 | if (varTypeIsIntegralOrI(regType0)) |
18221 | { |
18222 | resultReg = REG_INTRET_1; |
18223 | } |
18224 | else |
18225 | { |
18226 | resultReg = REG_INTRET; |
18227 | } |
18228 | } |
18229 | else |
18230 | { |
18231 | noway_assert(varTypeIsFloating(regType1)); |
18232 | |
18233 | if (varTypeIsFloating(regType0)) |
18234 | { |
18235 | resultReg = REG_FLOATRET_1; |
18236 | } |
18237 | else |
18238 | { |
18239 | resultReg = REG_FLOATRET; |
18240 | } |
18241 | } |
18242 | } |
18243 | |
18244 | #elif defined(_TARGET_X86_) |
18245 | |
18246 | if (idx == 0) |
18247 | { |
18248 | resultReg = REG_LNGRET_LO; |
18249 | } |
18250 | else if (idx == 1) |
18251 | { |
18252 | resultReg = REG_LNGRET_HI; |
18253 | } |
18254 | |
18255 | #elif defined(_TARGET_ARM_) |
18256 | |
18257 | var_types regType = GetReturnRegType(idx); |
18258 | if (varTypeIsIntegralOrI(regType)) |
18259 | { |
18260 | // Ints are returned in one return register. |
18261 | // Longs are returned in two return registers. |
18262 | if (idx == 0) |
18263 | { |
18264 | resultReg = REG_LNGRET_LO; |
18265 | } |
18266 | else if (idx == 1) |
18267 | { |
18268 | resultReg = REG_LNGRET_HI; |
18269 | } |
18270 | } |
18271 | else |
18272 | { |
18273 | // Floats are returned in one return register (f0). |
18274 | // Doubles are returned in one return register (d0). |
18275 | // Structs are returned in four registers with HFAs. |
18276 | assert(idx < MAX_RET_REG_COUNT); // Up to 4 return registers for HFA's |
18277 | if (regType == TYP_DOUBLE) |
18278 | { |
18279 | resultReg = (regNumber)((unsigned)(REG_FLOATRET) + idx * 2); // d0, d1, d2 or d3 |
18280 | } |
18281 | else |
18282 | { |
18283 | resultReg = (regNumber)((unsigned)(REG_FLOATRET) + idx); // f0, f1, f2 or f3 |
18284 | } |
18285 | } |
18286 | |
18287 | #elif defined(_TARGET_ARM64_) |
18288 | |
18289 | var_types regType = GetReturnRegType(idx); |
18290 | if (varTypeIsIntegralOrI(regType)) |
18291 | { |
18292 | noway_assert(idx < 2); // Up to 2 return registers for 16-byte structs |
18293 | resultReg = (idx == 0) ? REG_INTRET : REG_INTRET_1; // X0 or X1 |
18294 | } |
18295 | else |
18296 | { |
18297 | noway_assert(idx < 4); // Up to 4 return registers for HFA's |
18298 | resultReg = (regNumber)((unsigned)(REG_FLOATRET) + idx); // V0, V1, V2 or V3 |
18299 | } |
18300 | |
18301 | #endif // TARGET_XXX |
18302 | |
18303 | assert(resultReg != REG_NA); |
18304 | return resultReg; |
18305 | } |
18306 | |
18307 | //-------------------------------------------------------------------------------- |
18308 | // GetABIReturnRegs: get the mask of return registers as per target arch ABI. |
18309 | // |
18310 | // Arguments: |
18311 | // None |
18312 | // |
18313 | // Return Value: |
18314 | // reg mask of return registers in which the return type is returned. |
18315 | // |
18316 | // Note: |
18317 | // This routine can be used when the caller is not particular about the order |
18318 | // of return registers and wants to know the set of return registers. |
18319 | // |
18320 | // static |
18321 | regMaskTP ReturnTypeDesc::GetABIReturnRegs() |
18322 | { |
18323 | regMaskTP resultMask = RBM_NONE; |
18324 | |
18325 | unsigned count = GetReturnRegCount(); |
18326 | for (unsigned i = 0; i < count; ++i) |
18327 | { |
18328 | resultMask |= genRegMask(GetABIReturnReg(i)); |
18329 | } |
18330 | |
18331 | return resultMask; |
18332 | } |
18333 | |
18334 | //------------------------------------------------------------------------ |
18335 | // The following functions manage the gtRsvdRegs set of temporary registers |
18336 | // created by LSRA during code generation. |
18337 | |
18338 | //------------------------------------------------------------------------ |
18339 | // AvailableTempRegCount: return the number of available temporary registers in the (optional) given set |
18340 | // (typically, RBM_ALLINT or RBM_ALLFLOAT). |
18341 | // |
18342 | // Arguments: |
18343 | // mask - (optional) Check for available temporary registers only in this set. |
18344 | // |
18345 | // Return Value: |
18346 | // Count of available temporary registers in given set. |
18347 | // |
18348 | unsigned GenTree::AvailableTempRegCount(regMaskTP mask /* = (regMaskTP)-1 */) const |
18349 | { |
18350 | return genCountBits(gtRsvdRegs & mask); |
18351 | } |
18352 | |
18353 | //------------------------------------------------------------------------ |
18354 | // GetSingleTempReg: There is expected to be exactly one available temporary register |
18355 | // in the given mask in the gtRsvdRegs set. Get that register. No future calls to get |
18356 | // a temporary register are expected. Removes the register from the set, but only in |
18357 | // DEBUG to avoid doing unnecessary work in non-DEBUG builds. |
18358 | // |
18359 | // Arguments: |
18360 | // mask - (optional) Get an available temporary register only in this set. |
18361 | // |
18362 | // Return Value: |
18363 | // Available temporary register in given mask. |
18364 | // |
18365 | regNumber GenTree::GetSingleTempReg(regMaskTP mask /* = (regMaskTP)-1 */) |
18366 | { |
18367 | regMaskTP availableSet = gtRsvdRegs & mask; |
18368 | assert(genCountBits(availableSet) == 1); |
18369 | regNumber tempReg = genRegNumFromMask(availableSet); |
18370 | INDEBUG(gtRsvdRegs &= ~availableSet;) // Remove the register from the set, so it can't be used again. |
18371 | return tempReg; |
18372 | } |
18373 | |
18374 | //------------------------------------------------------------------------ |
18375 | // ExtractTempReg: Find the lowest number temporary register from the gtRsvdRegs set |
18376 | // that is also in the optional given mask (typically, RBM_ALLINT or RBM_ALLFLOAT), |
18377 | // and return it. Remove this register from the temporary register set, so it won't |
18378 | // be returned again. |
18379 | // |
18380 | // Arguments: |
18381 | // mask - (optional) Extract an available temporary register only in this set. |
18382 | // |
18383 | // Return Value: |
18384 | // Available temporary register in given mask. |
18385 | // |
18386 | regNumber GenTree::(regMaskTP mask /* = (regMaskTP)-1 */) |
18387 | { |
18388 | regMaskTP availableSet = gtRsvdRegs & mask; |
18389 | assert(genCountBits(availableSet) >= 1); |
18390 | regMaskTP tempRegMask = genFindLowestBit(availableSet); |
18391 | gtRsvdRegs &= ~tempRegMask; |
18392 | return genRegNumFromMask(tempRegMask); |
18393 | } |
18394 | |