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 Code Generation Support Methods for Linear Codegen XX |
9 | XX XX |
10 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
11 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
12 | */ |
13 | #include "jitpch.h" |
14 | #ifdef _MSC_VER |
15 | #pragma hdrstop |
16 | #endif |
17 | |
18 | #include "emit.h" |
19 | #include "codegen.h" |
20 | |
21 | //------------------------------------------------------------------------ |
22 | // genCodeForBBlist: Generate code for all the blocks in a method |
23 | // |
24 | // Arguments: |
25 | // None |
26 | // |
27 | // Notes: |
28 | // This is the main method for linear codegen. It calls genCodeForTreeNode |
29 | // to generate the code for each node in each BasicBlock, and handles BasicBlock |
30 | // boundaries and branches. |
31 | // |
32 | void CodeGen::genCodeForBBlist() |
33 | { |
34 | unsigned varNum; |
35 | LclVarDsc* varDsc; |
36 | |
37 | unsigned savedStkLvl; |
38 | |
39 | #ifdef DEBUG |
40 | genInterruptibleUsed = true; |
41 | |
42 | // You have to be careful if you create basic blocks from now on |
43 | compiler->fgSafeBasicBlockCreation = false; |
44 | #endif // DEBUG |
45 | |
46 | #if defined(DEBUG) && defined(_TARGET_X86_) |
47 | |
48 | // Check stack pointer on call stress mode is not compatible with fully interruptible GC. REVIEW: why? |
49 | // |
50 | if (genInterruptible && compiler->opts.compStackCheckOnCall) |
51 | { |
52 | compiler->opts.compStackCheckOnCall = false; |
53 | } |
54 | |
55 | #endif // defined(DEBUG) && defined(_TARGET_X86_) |
56 | |
57 | #if defined(DEBUG) && defined(_TARGET_XARCH_) |
58 | |
59 | // Check stack pointer on return stress mode is not compatible with fully interruptible GC. REVIEW: why? |
60 | // It is also not compatible with any function that makes a tailcall: we aren't smart enough to only |
61 | // insert the SP check in the non-tailcall returns. |
62 | // |
63 | if ((genInterruptible || compiler->compTailCallUsed) && compiler->opts.compStackCheckOnRet) |
64 | { |
65 | compiler->opts.compStackCheckOnRet = false; |
66 | } |
67 | |
68 | #endif // defined(DEBUG) && defined(_TARGET_XARCH_) |
69 | |
70 | // Prepare the blocks for exception handling codegen: mark the blocks that needs labels. |
71 | genPrepForEHCodegen(); |
72 | |
73 | assert(!compiler->fgFirstBBScratch || |
74 | compiler->fgFirstBB == compiler->fgFirstBBScratch); // compiler->fgFirstBBScratch has to be first. |
75 | |
76 | /* Initialize the spill tracking logic */ |
77 | |
78 | regSet.rsSpillBeg(); |
79 | |
80 | /* Initialize the line# tracking logic */ |
81 | |
82 | if (compiler->opts.compScopeInfo) |
83 | { |
84 | siInit(); |
85 | } |
86 | |
87 | // The current implementation of switch tables requires the first block to have a label so it |
88 | // can generate offsets to the switch label targets. |
89 | // TODO-CQ: remove this when switches have been re-implemented to not use this. |
90 | if (compiler->fgHasSwitch) |
91 | { |
92 | compiler->fgFirstBB->bbFlags |= BBF_JMP_TARGET; |
93 | } |
94 | |
95 | genPendingCallLabel = nullptr; |
96 | |
97 | /* Initialize the pointer tracking code */ |
98 | |
99 | gcInfo.gcRegPtrSetInit(); |
100 | gcInfo.gcVarPtrSetInit(); |
101 | |
102 | /* If any arguments live in registers, mark those regs as such */ |
103 | |
104 | for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++) |
105 | { |
106 | /* Is this variable a parameter assigned to a register? */ |
107 | |
108 | if (!varDsc->lvIsParam || !varDsc->lvRegister) |
109 | { |
110 | continue; |
111 | } |
112 | |
113 | /* Is the argument live on entry to the method? */ |
114 | |
115 | if (!VarSetOps::IsMember(compiler, compiler->fgFirstBB->bbLiveIn, varDsc->lvVarIndex)) |
116 | { |
117 | continue; |
118 | } |
119 | |
120 | /* Is this a floating-point argument? */ |
121 | |
122 | if (varDsc->IsFloatRegType()) |
123 | { |
124 | continue; |
125 | } |
126 | |
127 | noway_assert(!varTypeIsFloating(varDsc->TypeGet())); |
128 | |
129 | /* Mark the register as holding the variable */ |
130 | |
131 | assert(varDsc->lvRegNum != REG_STK); |
132 | if (!varDsc->lvAddrExposed) |
133 | { |
134 | regSet.verifyRegUsed(varDsc->lvRegNum); |
135 | } |
136 | } |
137 | |
138 | unsigned finallyNesting = 0; |
139 | |
140 | // Make sure a set is allocated for compiler->compCurLife (in the long case), so we can set it to empty without |
141 | // allocation at the start of each basic block. |
142 | VarSetOps::AssignNoCopy(compiler, compiler->compCurLife, VarSetOps::MakeEmpty(compiler)); |
143 | |
144 | /*------------------------------------------------------------------------- |
145 | * |
146 | * Walk the basic blocks and generate code for each one |
147 | * |
148 | */ |
149 | |
150 | BasicBlock* block; |
151 | |
152 | for (block = compiler->fgFirstBB; block != nullptr; block = block->bbNext) |
153 | { |
154 | #ifdef DEBUG |
155 | if (compiler->verbose) |
156 | { |
157 | printf("\n=============== Generating " ); |
158 | block->dspBlockHeader(compiler, true, true); |
159 | compiler->fgDispBBLiveness(block); |
160 | } |
161 | #endif // DEBUG |
162 | |
163 | assert(LIR::AsRange(block).CheckLIR(compiler)); |
164 | |
165 | // Figure out which registers hold variables on entry to this block |
166 | |
167 | regSet.ClearMaskVars(); |
168 | gcInfo.gcRegGCrefSetCur = RBM_NONE; |
169 | gcInfo.gcRegByrefSetCur = RBM_NONE; |
170 | |
171 | compiler->m_pLinearScan->recordVarLocationsAtStartOfBB(block); |
172 | |
173 | genUpdateLife(block->bbLiveIn); |
174 | |
175 | // Even if liveness didn't change, we need to update the registers containing GC references. |
176 | // genUpdateLife will update the registers live due to liveness changes. But what about registers that didn't |
177 | // change? We cleared them out above. Maybe we should just not clear them out, but update the ones that change |
178 | // here. That would require handling the changes in recordVarLocationsAtStartOfBB(). |
179 | |
180 | regMaskTP newLiveRegSet = RBM_NONE; |
181 | regMaskTP newRegGCrefSet = RBM_NONE; |
182 | regMaskTP newRegByrefSet = RBM_NONE; |
183 | #ifdef DEBUG |
184 | VARSET_TP removedGCVars(VarSetOps::MakeEmpty(compiler)); |
185 | VARSET_TP addedGCVars(VarSetOps::MakeEmpty(compiler)); |
186 | #endif |
187 | VarSetOps::Iter iter(compiler, block->bbLiveIn); |
188 | unsigned varIndex = 0; |
189 | while (iter.NextElem(&varIndex)) |
190 | { |
191 | unsigned varNum = compiler->lvaTrackedToVarNum[varIndex]; |
192 | LclVarDsc* varDsc = &(compiler->lvaTable[varNum]); |
193 | |
194 | if (varDsc->lvIsInReg()) |
195 | { |
196 | newLiveRegSet |= varDsc->lvRegMask(); |
197 | if (varDsc->lvType == TYP_REF) |
198 | { |
199 | newRegGCrefSet |= varDsc->lvRegMask(); |
200 | } |
201 | else if (varDsc->lvType == TYP_BYREF) |
202 | { |
203 | newRegByrefSet |= varDsc->lvRegMask(); |
204 | } |
205 | #ifdef DEBUG |
206 | if (verbose && VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varIndex)) |
207 | { |
208 | VarSetOps::AddElemD(compiler, removedGCVars, varIndex); |
209 | } |
210 | #endif // DEBUG |
211 | VarSetOps::RemoveElemD(compiler, gcInfo.gcVarPtrSetCur, varIndex); |
212 | } |
213 | else if (compiler->lvaIsGCTracked(varDsc)) |
214 | { |
215 | #ifdef DEBUG |
216 | if (verbose && !VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varIndex)) |
217 | { |
218 | VarSetOps::AddElemD(compiler, addedGCVars, varIndex); |
219 | } |
220 | #endif // DEBUG |
221 | VarSetOps::AddElemD(compiler, gcInfo.gcVarPtrSetCur, varIndex); |
222 | } |
223 | } |
224 | |
225 | regSet.rsMaskVars = newLiveRegSet; |
226 | |
227 | #ifdef DEBUG |
228 | if (compiler->verbose) |
229 | { |
230 | if (!VarSetOps::IsEmpty(compiler, addedGCVars)) |
231 | { |
232 | printf("\t\t\t\t\t\t\tAdded GCVars: " ); |
233 | dumpConvertedVarSet(compiler, addedGCVars); |
234 | printf("\n" ); |
235 | } |
236 | if (!VarSetOps::IsEmpty(compiler, removedGCVars)) |
237 | { |
238 | printf("\t\t\t\t\t\t\tRemoved GCVars: " ); |
239 | dumpConvertedVarSet(compiler, removedGCVars); |
240 | printf("\n" ); |
241 | } |
242 | } |
243 | #endif // DEBUG |
244 | |
245 | gcInfo.gcMarkRegSetGCref(newRegGCrefSet DEBUGARG(true)); |
246 | gcInfo.gcMarkRegSetByref(newRegByrefSet DEBUGARG(true)); |
247 | |
248 | /* Blocks with handlerGetsXcptnObj()==true use GT_CATCH_ARG to |
249 | represent the exception object (TYP_REF). |
250 | We mark REG_EXCEPTION_OBJECT as holding a GC object on entry |
251 | to the block, it will be the first thing evaluated |
252 | (thanks to GTF_ORDER_SIDEEFF). |
253 | */ |
254 | |
255 | if (handlerGetsXcptnObj(block->bbCatchTyp)) |
256 | { |
257 | for (GenTree* node : LIR::AsRange(block)) |
258 | { |
259 | if (node->OperGet() == GT_CATCH_ARG) |
260 | { |
261 | gcInfo.gcMarkRegSetGCref(RBM_EXCEPTION_OBJECT); |
262 | break; |
263 | } |
264 | } |
265 | } |
266 | |
267 | #if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_) |
268 | genInsertNopForUnwinder(block); |
269 | #endif |
270 | |
271 | /* Start a new code output block */ |
272 | |
273 | genUpdateCurrentFunclet(block); |
274 | |
275 | #ifdef _TARGET_XARCH_ |
276 | if (genAlignLoops && block->bbFlags & BBF_LOOP_HEAD) |
277 | { |
278 | getEmitter()->emitLoopAlign(); |
279 | } |
280 | #endif |
281 | |
282 | #ifdef DEBUG |
283 | if (compiler->opts.dspCode) |
284 | { |
285 | printf("\n L_M%03u_" FMT_BB ":\n" , Compiler::s_compMethodsCount, block->bbNum); |
286 | } |
287 | #endif |
288 | |
289 | block->bbEmitCookie = nullptr; |
290 | |
291 | if (block->bbFlags & (BBF_JMP_TARGET | BBF_HAS_LABEL)) |
292 | { |
293 | /* Mark a label and update the current set of live GC refs */ |
294 | |
295 | block->bbEmitCookie = getEmitter()->emitAddLabel(gcInfo.gcVarPtrSetCur, gcInfo.gcRegGCrefSetCur, |
296 | gcInfo.gcRegByrefSetCur, FALSE); |
297 | } |
298 | |
299 | if (block == compiler->fgFirstColdBlock) |
300 | { |
301 | #ifdef DEBUG |
302 | if (compiler->verbose) |
303 | { |
304 | printf("\nThis is the start of the cold region of the method\n" ); |
305 | } |
306 | #endif |
307 | // We should never have a block that falls through into the Cold section |
308 | noway_assert(!block->bbPrev->bbFallsThrough()); |
309 | |
310 | // We require the block that starts the Cold section to have a label |
311 | noway_assert(block->bbEmitCookie); |
312 | getEmitter()->emitSetFirstColdIGCookie(block->bbEmitCookie); |
313 | } |
314 | |
315 | /* Both stacks are always empty on entry to a basic block */ |
316 | |
317 | SetStackLevel(0); |
318 | genAdjustStackLevel(block); |
319 | savedStkLvl = genStackLevel; |
320 | |
321 | /* Tell everyone which basic block we're working on */ |
322 | |
323 | compiler->compCurBB = block; |
324 | |
325 | siBeginBlock(block); |
326 | |
327 | // BBF_INTERNAL blocks don't correspond to any single IL instruction. |
328 | if (compiler->opts.compDbgInfo && (block->bbFlags & BBF_INTERNAL) && |
329 | !compiler->fgBBisScratch(block)) // If the block is the distinguished first scratch block, then no need to |
330 | // emit a NO_MAPPING entry, immediately after the prolog. |
331 | { |
332 | genIPmappingAdd((IL_OFFSETX)ICorDebugInfo::NO_MAPPING, true); |
333 | } |
334 | |
335 | bool firstMapping = true; |
336 | |
337 | #if FEATURE_EH_FUNCLETS |
338 | if (block->bbFlags & BBF_FUNCLET_BEG) |
339 | { |
340 | genReserveFuncletProlog(block); |
341 | } |
342 | #endif // FEATURE_EH_FUNCLETS |
343 | |
344 | // Clear compCurStmt and compCurLifeTree. |
345 | compiler->compCurStmt = nullptr; |
346 | compiler->compCurLifeTree = nullptr; |
347 | |
348 | // Traverse the block in linear order, generating code for each node as we |
349 | // as we encounter it. |
350 | CLANG_FORMAT_COMMENT_ANCHOR; |
351 | |
352 | #ifdef DEBUG |
353 | // Set the use-order numbers for each node. |
354 | { |
355 | int useNum = 0; |
356 | for (GenTree* node : LIR::AsRange(block).NonPhiNodes()) |
357 | { |
358 | assert((node->gtDebugFlags & GTF_DEBUG_NODE_CG_CONSUMED) == 0); |
359 | |
360 | node->gtUseNum = -1; |
361 | if (node->isContained() || node->IsCopyOrReload()) |
362 | { |
363 | continue; |
364 | } |
365 | |
366 | for (GenTree* operand : node->Operands()) |
367 | { |
368 | genNumberOperandUse(operand, useNum); |
369 | } |
370 | } |
371 | } |
372 | #endif // DEBUG |
373 | |
374 | IL_OFFSETX currentILOffset = BAD_IL_OFFSET; |
375 | for (GenTree* node : LIR::AsRange(block).NonPhiNodes()) |
376 | { |
377 | // Do we have a new IL offset? |
378 | if (node->OperGet() == GT_IL_OFFSET) |
379 | { |
380 | genEnsureCodeEmitted(currentILOffset); |
381 | currentILOffset = node->gtStmt.gtStmtILoffsx; |
382 | genIPmappingAdd(currentILOffset, firstMapping); |
383 | firstMapping = false; |
384 | } |
385 | |
386 | #ifdef DEBUG |
387 | if (node->OperGet() == GT_IL_OFFSET) |
388 | { |
389 | noway_assert(node->gtStmt.gtStmtLastILoffs <= compiler->info.compILCodeSize || |
390 | node->gtStmt.gtStmtLastILoffs == BAD_IL_OFFSET); |
391 | |
392 | if (compiler->opts.dspCode && compiler->opts.dspInstrs && |
393 | node->gtStmt.gtStmtLastILoffs != BAD_IL_OFFSET) |
394 | { |
395 | while (genCurDispOffset <= node->gtStmt.gtStmtLastILoffs) |
396 | { |
397 | genCurDispOffset += dumpSingleInstr(compiler->info.compCode, genCurDispOffset, "> " ); |
398 | } |
399 | } |
400 | } |
401 | #endif // DEBUG |
402 | |
403 | genCodeForTreeNode(node); |
404 | if (node->gtHasReg() && node->IsUnusedValue()) |
405 | { |
406 | genConsumeReg(node); |
407 | } |
408 | } // end for each node in block |
409 | |
410 | #ifdef DEBUG |
411 | // The following set of register spill checks and GC pointer tracking checks used to be |
412 | // performed at statement boundaries. Now, with LIR, there are no statements, so they are |
413 | // performed at the end of each block. |
414 | // TODO: could these checks be performed more frequently? E.g., at each location where |
415 | // the register allocator says there are no live non-variable registers. Perhaps this could |
416 | // be done by using the map maintained by LSRA (operandToLocationInfoMap) to mark a node |
417 | // somehow when, after the execution of that node, there will be no live non-variable registers. |
418 | |
419 | regSet.rsSpillChk(); |
420 | |
421 | /* Make sure we didn't bungle pointer register tracking */ |
422 | |
423 | regMaskTP ptrRegs = gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur; |
424 | regMaskTP nonVarPtrRegs = ptrRegs & ~regSet.rsMaskVars; |
425 | |
426 | // If return is a GC-type, clear it. Note that if a common |
427 | // epilog is generated (genReturnBB) it has a void return |
428 | // even though we might return a ref. We can't use the compRetType |
429 | // as the determiner because something we are tracking as a byref |
430 | // might be used as a return value of a int function (which is legal) |
431 | GenTree* blockLastNode = block->lastNode(); |
432 | if ((blockLastNode != nullptr) && (blockLastNode->gtOper == GT_RETURN) && |
433 | (varTypeIsGC(compiler->info.compRetType) || |
434 | (blockLastNode->gtOp.gtOp1 != nullptr && varTypeIsGC(blockLastNode->gtOp.gtOp1->TypeGet())))) |
435 | { |
436 | nonVarPtrRegs &= ~RBM_INTRET; |
437 | } |
438 | |
439 | if (nonVarPtrRegs) |
440 | { |
441 | printf("Regset after " FMT_BB " gcr=" , block->bbNum); |
442 | printRegMaskInt(gcInfo.gcRegGCrefSetCur & ~regSet.rsMaskVars); |
443 | compiler->getEmitter()->emitDispRegSet(gcInfo.gcRegGCrefSetCur & ~regSet.rsMaskVars); |
444 | printf(", byr=" ); |
445 | printRegMaskInt(gcInfo.gcRegByrefSetCur & ~regSet.rsMaskVars); |
446 | compiler->getEmitter()->emitDispRegSet(gcInfo.gcRegByrefSetCur & ~regSet.rsMaskVars); |
447 | printf(", regVars=" ); |
448 | printRegMaskInt(regSet.rsMaskVars); |
449 | compiler->getEmitter()->emitDispRegSet(regSet.rsMaskVars); |
450 | printf("\n" ); |
451 | } |
452 | |
453 | noway_assert(nonVarPtrRegs == RBM_NONE); |
454 | #endif // DEBUG |
455 | |
456 | #if defined(DEBUG) |
457 | if (block->bbNext == nullptr) |
458 | { |
459 | // Unit testing of the emitter: generate a bunch of instructions into the last block |
460 | // (it's as good as any, but better than the prologue, which can only be a single instruction |
461 | // group) then use COMPlus_JitLateDisasm=* to see if the late disassembler |
462 | // thinks the instructions are the same as we do. |
463 | #if defined(_TARGET_AMD64_) && defined(LATE_DISASM) |
464 | genAmd64EmitterUnitTests(); |
465 | #elif defined(_TARGET_ARM64_) |
466 | genArm64EmitterUnitTests(); |
467 | #endif // _TARGET_ARM64_ |
468 | } |
469 | #endif // defined(DEBUG) |
470 | |
471 | // It is possible to reach the end of the block without generating code for the current IL offset. |
472 | // For example, if the following IR ends the current block, no code will have been generated for |
473 | // offset 21: |
474 | // |
475 | // ( 0, 0) [000040] ------------ il_offset void IL offset: 21 |
476 | // |
477 | // N001 ( 0, 0) [000039] ------------ nop void |
478 | // |
479 | // This can lead to problems when debugging the generated code. To prevent these issues, make sure |
480 | // we've generated code for the last IL offset we saw in the block. |
481 | genEnsureCodeEmitted(currentILOffset); |
482 | |
483 | if (compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0)) |
484 | { |
485 | siEndBlock(block); |
486 | |
487 | /* Is this the last block, and are there any open scopes left ? */ |
488 | |
489 | bool isLastBlockProcessed = (block->bbNext == nullptr); |
490 | if (block->isBBCallAlwaysPair()) |
491 | { |
492 | isLastBlockProcessed = (block->bbNext->bbNext == nullptr); |
493 | } |
494 | |
495 | if (isLastBlockProcessed && siOpenScopeList.scNext) |
496 | { |
497 | /* This assert no longer holds, because we may insert a throw |
498 | block to demarcate the end of a try or finally region when they |
499 | are at the end of the method. It would be nice if we could fix |
500 | our code so that this throw block will no longer be necessary. */ |
501 | |
502 | // noway_assert(block->bbCodeOffsEnd != compiler->info.compILCodeSize); |
503 | |
504 | siCloseAllOpenScopes(); |
505 | } |
506 | } |
507 | |
508 | SubtractStackLevel(savedStkLvl); |
509 | |
510 | #ifdef DEBUG |
511 | // compCurLife should be equal to the liveOut set, except that we don't keep |
512 | // it up to date for vars that are not register candidates |
513 | // (it would be nice to have a xor set function) |
514 | |
515 | VARSET_TP mismatchLiveVars(VarSetOps::Diff(compiler, block->bbLiveOut, compiler->compCurLife)); |
516 | VarSetOps::UnionD(compiler, mismatchLiveVars, |
517 | VarSetOps::Diff(compiler, compiler->compCurLife, block->bbLiveOut)); |
518 | VarSetOps::Iter mismatchLiveVarIter(compiler, mismatchLiveVars); |
519 | unsigned mismatchLiveVarIndex = 0; |
520 | bool foundMismatchedRegVar = false; |
521 | while (mismatchLiveVarIter.NextElem(&mismatchLiveVarIndex)) |
522 | { |
523 | unsigned varNum = compiler->lvaTrackedToVarNum[mismatchLiveVarIndex]; |
524 | LclVarDsc* varDsc = compiler->lvaTable + varNum; |
525 | if (varDsc->lvIsRegCandidate()) |
526 | { |
527 | if (!foundMismatchedRegVar) |
528 | { |
529 | JITDUMP("Mismatched live reg vars after BB%02u:" , block->bbNum); |
530 | foundMismatchedRegVar = true; |
531 | } |
532 | JITDUMP(" V%02u" , varNum); |
533 | } |
534 | } |
535 | if (foundMismatchedRegVar) |
536 | { |
537 | assert(!"Found mismatched live reg var(s) after block" ); |
538 | JITDUMP("\n" ); |
539 | } |
540 | #endif |
541 | |
542 | /* Both stacks should always be empty on exit from a basic block */ |
543 | noway_assert(genStackLevel == 0); |
544 | |
545 | #ifdef _TARGET_AMD64_ |
546 | // On AMD64, we need to generate a NOP after a call that is the last instruction of the block, in several |
547 | // situations, to support proper exception handling semantics. This is mostly to ensure that when the stack |
548 | // walker computes an instruction pointer for a frame, that instruction pointer is in the correct EH region. |
549 | // The document "X64 and ARM ABIs.docx" has more details. The situations: |
550 | // 1. If the call instruction is in a different EH region as the instruction that follows it. |
551 | // 2. If the call immediately precedes an OS epilog. (Note that what the JIT or VM consider an epilog might |
552 | // be slightly different from what the OS considers an epilog, and it is the OS-reported epilog that matters |
553 | // here.) |
554 | // We handle case #1 here, and case #2 in the emitter. |
555 | if (getEmitter()->emitIsLastInsCall()) |
556 | { |
557 | // Ok, the last instruction generated is a call instruction. Do any of the other conditions hold? |
558 | // Note: we may be generating a few too many NOPs for the case of call preceding an epilog. Technically, |
559 | // if the next block is a BBJ_RETURN, an epilog will be generated, but there may be some instructions |
560 | // generated before the OS epilog starts, such as a GS cookie check. |
561 | if ((block->bbNext == nullptr) || !BasicBlock::sameEHRegion(block, block->bbNext)) |
562 | { |
563 | // We only need the NOP if we're not going to generate any more code as part of the block end. |
564 | |
565 | switch (block->bbJumpKind) |
566 | { |
567 | case BBJ_ALWAYS: |
568 | case BBJ_THROW: |
569 | case BBJ_CALLFINALLY: |
570 | case BBJ_EHCATCHRET: |
571 | // We're going to generate more code below anyway, so no need for the NOP. |
572 | |
573 | case BBJ_RETURN: |
574 | case BBJ_EHFINALLYRET: |
575 | case BBJ_EHFILTERRET: |
576 | // These are the "epilog follows" case, handled in the emitter. |
577 | |
578 | break; |
579 | |
580 | case BBJ_NONE: |
581 | if (block->bbNext == nullptr) |
582 | { |
583 | // Call immediately before the end of the code; we should never get here . |
584 | instGen(INS_BREAKPOINT); // This should never get executed |
585 | } |
586 | else |
587 | { |
588 | // We need the NOP |
589 | instGen(INS_nop); |
590 | } |
591 | break; |
592 | |
593 | case BBJ_COND: |
594 | case BBJ_SWITCH: |
595 | // These can't have a call as the last instruction! |
596 | |
597 | default: |
598 | noway_assert(!"Unexpected bbJumpKind" ); |
599 | break; |
600 | } |
601 | } |
602 | } |
603 | #endif // _TARGET_AMD64_ |
604 | |
605 | /* Do we need to generate a jump or return? */ |
606 | |
607 | switch (block->bbJumpKind) |
608 | { |
609 | case BBJ_ALWAYS: |
610 | inst_JMP(EJ_jmp, block->bbJumpDest); |
611 | break; |
612 | |
613 | case BBJ_RETURN: |
614 | genExitCode(block); |
615 | break; |
616 | |
617 | case BBJ_THROW: |
618 | // If we have a throw at the end of a function or funclet, we need to emit another instruction |
619 | // afterwards to help the OS unwinder determine the correct context during unwind. |
620 | // We insert an unexecuted breakpoint instruction in several situations |
621 | // following a throw instruction: |
622 | // 1. If the throw is the last instruction of the function or funclet. This helps |
623 | // the OS unwinder determine the correct context during an unwind from the |
624 | // thrown exception. |
625 | // 2. If this is this is the last block of the hot section. |
626 | // 3. If the subsequent block is a special throw block. |
627 | // 4. On AMD64, if the next block is in a different EH region. |
628 | if ((block->bbNext == nullptr) || (block->bbNext->bbFlags & BBF_FUNCLET_BEG) || |
629 | !BasicBlock::sameEHRegion(block, block->bbNext) || |
630 | (!isFramePointerUsed() && compiler->fgIsThrowHlpBlk(block->bbNext)) || |
631 | block->bbNext == compiler->fgFirstColdBlock) |
632 | { |
633 | instGen(INS_BREAKPOINT); // This should never get executed |
634 | } |
635 | // Do likewise for blocks that end in DOES_NOT_RETURN calls |
636 | // that were not caught by the above rules. This ensures that |
637 | // gc register liveness doesn't change across call instructions |
638 | // in fully-interruptible mode. |
639 | else |
640 | { |
641 | GenTree* call = block->lastNode(); |
642 | |
643 | if ((call != nullptr) && (call->gtOper == GT_CALL)) |
644 | { |
645 | if ((call->gtCall.gtCallMoreFlags & GTF_CALL_M_DOES_NOT_RETURN) != 0) |
646 | { |
647 | instGen(INS_BREAKPOINT); // This should never get executed |
648 | } |
649 | } |
650 | } |
651 | |
652 | break; |
653 | |
654 | case BBJ_CALLFINALLY: |
655 | block = genCallFinally(block); |
656 | break; |
657 | |
658 | #if FEATURE_EH_FUNCLETS |
659 | |
660 | case BBJ_EHCATCHRET: |
661 | genEHCatchRet(block); |
662 | __fallthrough; |
663 | |
664 | case BBJ_EHFINALLYRET: |
665 | case BBJ_EHFILTERRET: |
666 | genReserveFuncletEpilog(block); |
667 | break; |
668 | |
669 | #else // !FEATURE_EH_FUNCLETS |
670 | |
671 | case BBJ_EHCATCHRET: |
672 | noway_assert(!"Unexpected BBJ_EHCATCHRET" ); // not used on x86 |
673 | |
674 | case BBJ_EHFINALLYRET: |
675 | case BBJ_EHFILTERRET: |
676 | genEHFinallyOrFilterRet(block); |
677 | break; |
678 | |
679 | #endif // !FEATURE_EH_FUNCLETS |
680 | |
681 | case BBJ_NONE: |
682 | case BBJ_COND: |
683 | case BBJ_SWITCH: |
684 | break; |
685 | |
686 | default: |
687 | noway_assert(!"Unexpected bbJumpKind" ); |
688 | break; |
689 | } |
690 | |
691 | #ifdef DEBUG |
692 | compiler->compCurBB = nullptr; |
693 | #endif |
694 | |
695 | } //------------------ END-FOR each block of the method ------------------- |
696 | |
697 | /* Nothing is live at this point */ |
698 | genUpdateLife(VarSetOps::MakeEmpty(compiler)); |
699 | |
700 | /* Finalize the spill tracking logic */ |
701 | |
702 | regSet.rsSpillEnd(); |
703 | |
704 | /* Finalize the temp tracking logic */ |
705 | |
706 | regSet.tmpEnd(); |
707 | |
708 | #ifdef DEBUG |
709 | if (compiler->verbose) |
710 | { |
711 | printf("\n# " ); |
712 | printf("compCycleEstimate = %6d, compSizeEstimate = %5d " , compiler->compCycleEstimate, |
713 | compiler->compSizeEstimate); |
714 | printf("%s\n" , compiler->info.compFullName); |
715 | } |
716 | #endif |
717 | } |
718 | |
719 | /* |
720 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
721 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
722 | XX XX |
723 | XX Register Management XX |
724 | XX XX |
725 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
726 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
727 | */ |
728 | |
729 | //------------------------------------------------------------------------ |
730 | // genSpillVar: Spill a local variable |
731 | // |
732 | // Arguments: |
733 | // tree - the lclVar node for the variable being spilled |
734 | // |
735 | // Return Value: |
736 | // None. |
737 | // |
738 | // Assumptions: |
739 | // The lclVar must be a register candidate (lvRegCandidate) |
740 | |
741 | void CodeGen::genSpillVar(GenTree* tree) |
742 | { |
743 | unsigned varNum = tree->gtLclVarCommon.gtLclNum; |
744 | LclVarDsc* varDsc = &(compiler->lvaTable[varNum]); |
745 | |
746 | assert(varDsc->lvIsRegCandidate()); |
747 | |
748 | // We don't actually need to spill if it is already living in memory |
749 | bool needsSpill = ((tree->gtFlags & GTF_VAR_DEF) == 0 && varDsc->lvIsInReg()); |
750 | if (needsSpill) |
751 | { |
752 | // In order for a lclVar to have been allocated to a register, it must not have been aliasable, and can |
753 | // therefore be store-normalized (rather than load-normalized). In fact, not performing store normalization |
754 | // can lead to problems on architectures where a lclVar may be allocated to a register that is not |
755 | // addressable at the granularity of the lclVar's defined type (e.g. x86). |
756 | var_types lclTyp = genActualType(varDsc->TypeGet()); |
757 | emitAttr size = emitTypeSize(lclTyp); |
758 | |
759 | instruction storeIns = ins_Store(lclTyp, compiler->isSIMDTypeLocalAligned(varNum)); |
760 | assert(varDsc->lvRegNum == tree->gtRegNum); |
761 | inst_TT_RV(storeIns, tree, tree->gtRegNum, 0, size); |
762 | |
763 | genUpdateRegLife(varDsc, /*isBorn*/ false, /*isDying*/ true DEBUGARG(tree)); |
764 | gcInfo.gcMarkRegSetNpt(varDsc->lvRegMask()); |
765 | |
766 | if (VarSetOps::IsMember(compiler, gcInfo.gcTrkStkPtrLcls, varDsc->lvVarIndex)) |
767 | { |
768 | #ifdef DEBUG |
769 | if (!VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex)) |
770 | { |
771 | JITDUMP("\t\t\t\t\t\t\tVar V%02u becoming live\n" , varNum); |
772 | } |
773 | else |
774 | { |
775 | JITDUMP("\t\t\t\t\t\t\tVar V%02u continuing live\n" , varNum); |
776 | } |
777 | #endif |
778 | VarSetOps::AddElemD(compiler, gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex); |
779 | } |
780 | } |
781 | |
782 | tree->gtFlags &= ~GTF_SPILL; |
783 | varDsc->lvRegNum = REG_STK; |
784 | if (varTypeIsMultiReg(tree)) |
785 | { |
786 | varDsc->lvOtherReg = REG_STK; |
787 | } |
788 | } |
789 | |
790 | //------------------------------------------------------------------------ |
791 | // genUpdateVarReg: Update the current register location for a lclVar |
792 | // |
793 | // Arguments: |
794 | // varDsc - the LclVarDsc for the lclVar |
795 | // tree - the lclVar node |
796 | // |
797 | // inline |
798 | void CodeGenInterface::genUpdateVarReg(LclVarDsc* varDsc, GenTree* tree) |
799 | { |
800 | assert(tree->OperIsScalarLocal() || (tree->gtOper == GT_COPY)); |
801 | varDsc->lvRegNum = tree->gtRegNum; |
802 | } |
803 | |
804 | //------------------------------------------------------------------------ |
805 | // sameRegAsDst: Return the child that has the same reg as the dst (if any) |
806 | // |
807 | // Arguments: |
808 | // tree - the node of interest |
809 | // other - an out parameter to return the other child |
810 | // |
811 | // Notes: |
812 | // If 'tree' has a child with the same assigned register as its target reg, |
813 | // that child will be returned, and 'other' will contain the non-matching child. |
814 | // Otherwise, both other and the return value will be nullptr. |
815 | // |
816 | GenTree* sameRegAsDst(GenTree* tree, GenTree*& other /*out*/) |
817 | { |
818 | if (tree->gtRegNum == REG_NA) |
819 | { |
820 | other = nullptr; |
821 | return nullptr; |
822 | } |
823 | |
824 | GenTree* op1 = tree->gtOp.gtOp1; |
825 | GenTree* op2 = tree->gtOp.gtOp2; |
826 | if (op1->gtRegNum == tree->gtRegNum) |
827 | { |
828 | other = op2; |
829 | return op1; |
830 | } |
831 | if (op2->gtRegNum == tree->gtRegNum) |
832 | { |
833 | other = op1; |
834 | return op2; |
835 | } |
836 | else |
837 | { |
838 | other = nullptr; |
839 | return nullptr; |
840 | } |
841 | } |
842 | |
843 | //------------------------------------------------------------------------ |
844 | // genUnspillRegIfNeeded: Reload the value into a register, if needed |
845 | // |
846 | // Arguments: |
847 | // tree - the node of interest. |
848 | // |
849 | // Notes: |
850 | // In the normal case, the value will be reloaded into the register it |
851 | // was originally computed into. However, if that register is not available, |
852 | // the register allocator will have allocated a different register, and |
853 | // inserted a GT_RELOAD to indicate the register into which it should be |
854 | // reloaded. |
855 | // |
856 | void CodeGen::genUnspillRegIfNeeded(GenTree* tree) |
857 | { |
858 | regNumber dstReg = tree->gtRegNum; |
859 | GenTree* unspillTree = tree; |
860 | |
861 | if (tree->gtOper == GT_RELOAD) |
862 | { |
863 | unspillTree = tree->gtOp.gtOp1; |
864 | } |
865 | |
866 | if ((unspillTree->gtFlags & GTF_SPILLED) != 0) |
867 | { |
868 | if (genIsRegCandidateLocal(unspillTree)) |
869 | { |
870 | // Reset spilled flag, since we are going to load a local variable from its home location. |
871 | unspillTree->gtFlags &= ~GTF_SPILLED; |
872 | |
873 | GenTreeLclVarCommon* lcl = unspillTree->AsLclVarCommon(); |
874 | LclVarDsc* varDsc = &compiler->lvaTable[lcl->gtLclNum]; |
875 | |
876 | // TODO-Cleanup: The following code could probably be further merged and cleaned up. |
877 | #ifdef _TARGET_XARCH_ |
878 | // Load local variable from its home location. |
879 | // In most cases the tree type will indicate the correct type to use for the load. |
880 | // However, if it is NOT a normalizeOnLoad lclVar (i.e. NOT a small int that always gets |
881 | // widened when loaded into a register), and its size is not the same as genActualType of |
882 | // the type of the lclVar, then we need to change the type of the tree node when loading. |
883 | // This situation happens due to "optimizations" that avoid a cast and |
884 | // simply retype the node when using long type lclVar as an int. |
885 | // While loading the int in that case would work for this use of the lclVar, if it is |
886 | // later used as a long, we will have incorrectly truncated the long. |
887 | // In the normalizeOnLoad case ins_Load will return an appropriate sign- or zero- |
888 | // extending load. |
889 | |
890 | var_types treeType = unspillTree->TypeGet(); |
891 | if (treeType != genActualType(varDsc->lvType) && !varTypeIsGC(treeType) && !varDsc->lvNormalizeOnLoad()) |
892 | { |
893 | assert(!varTypeIsGC(varDsc)); |
894 | var_types spillType = genActualType(varDsc->lvType); |
895 | unspillTree->gtType = spillType; |
896 | inst_RV_TT(ins_Load(spillType, compiler->isSIMDTypeLocalAligned(lcl->gtLclNum)), dstReg, unspillTree); |
897 | unspillTree->gtType = treeType; |
898 | } |
899 | else |
900 | { |
901 | inst_RV_TT(ins_Load(treeType, compiler->isSIMDTypeLocalAligned(lcl->gtLclNum)), dstReg, unspillTree); |
902 | } |
903 | #elif defined(_TARGET_ARM64_) |
904 | var_types targetType = unspillTree->gtType; |
905 | if (targetType != genActualType(varDsc->lvType) && !varTypeIsGC(targetType) && !varDsc->lvNormalizeOnLoad()) |
906 | { |
907 | assert(!varTypeIsGC(varDsc)); |
908 | targetType = genActualType(varDsc->lvType); |
909 | } |
910 | instruction ins = ins_Load(targetType, compiler->isSIMDTypeLocalAligned(lcl->gtLclNum)); |
911 | emitAttr attr = emitTypeSize(targetType); |
912 | emitter* emit = getEmitter(); |
913 | |
914 | // Fixes Issue #3326 |
915 | attr = varTypeIsFloating(targetType) ? attr : emit->emitInsAdjustLoadStoreAttr(ins, attr); |
916 | |
917 | // Load local variable from its home location. |
918 | inst_RV_TT(ins, dstReg, unspillTree, 0, attr); |
919 | #elif defined(_TARGET_ARM_) |
920 | var_types targetType = unspillTree->gtType; |
921 | instruction ins = ins_Load(targetType, compiler->isSIMDTypeLocalAligned(lcl->gtLclNum)); |
922 | emitAttr attr = emitTypeSize(targetType); |
923 | |
924 | // Load local variable from its home location. |
925 | inst_RV_TT(ins, dstReg, unspillTree, 0, attr); |
926 | #else |
927 | NYI("Unspilling not implemented for this target architecture." ); |
928 | #endif |
929 | |
930 | // TODO-Review: We would like to call: |
931 | // genUpdateRegLife(varDsc, /*isBorn*/ true, /*isDying*/ false DEBUGARG(tree)); |
932 | // instead of the following code, but this ends up hitting this assert: |
933 | // assert((regSet.rsMaskVars & regMask) == 0); |
934 | // due to issues with LSRA resolution moves. |
935 | // So, just force it for now. This probably indicates a condition that creates a GC hole! |
936 | // |
937 | // Extra note: I think we really want to call something like gcInfo.gcUpdateForRegVarMove, |
938 | // because the variable is not really going live or dead, but that method is somewhat poorly |
939 | // factored because it, in turn, updates rsMaskVars which is part of RegSet not GCInfo. |
940 | // TODO-Cleanup: This code exists in other CodeGen*.cpp files, and should be moved to CodeGenCommon.cpp. |
941 | |
942 | // Don't update the variable's location if we are just re-spilling it again. |
943 | |
944 | if ((unspillTree->gtFlags & GTF_SPILL) == 0) |
945 | { |
946 | genUpdateVarReg(varDsc, tree); |
947 | #ifdef DEBUG |
948 | if (VarSetOps::IsMember(compiler, gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex)) |
949 | { |
950 | JITDUMP("\t\t\t\t\t\t\tRemoving V%02u from gcVarPtrSetCur\n" , lcl->gtLclNum); |
951 | } |
952 | #endif // DEBUG |
953 | VarSetOps::RemoveElemD(compiler, gcInfo.gcVarPtrSetCur, varDsc->lvVarIndex); |
954 | |
955 | #ifdef DEBUG |
956 | if (compiler->verbose) |
957 | { |
958 | printf("\t\t\t\t\t\t\tV%02u in reg " , lcl->gtLclNum); |
959 | varDsc->PrintVarReg(); |
960 | printf(" is becoming live " ); |
961 | compiler->printTreeID(unspillTree); |
962 | printf("\n" ); |
963 | } |
964 | #endif // DEBUG |
965 | |
966 | regSet.AddMaskVars(genGetRegMask(varDsc)); |
967 | } |
968 | |
969 | gcInfo.gcMarkRegPtrVal(dstReg, unspillTree->TypeGet()); |
970 | } |
971 | else if (unspillTree->IsMultiRegCall()) |
972 | { |
973 | GenTreeCall* call = unspillTree->AsCall(); |
974 | ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); |
975 | unsigned regCount = retTypeDesc->GetReturnRegCount(); |
976 | GenTreeCopyOrReload* reloadTree = nullptr; |
977 | if (tree->OperGet() == GT_RELOAD) |
978 | { |
979 | reloadTree = tree->AsCopyOrReload(); |
980 | } |
981 | |
982 | // In case of multi-reg call node, GTF_SPILLED flag on it indicates that |
983 | // one or more of its result regs are spilled. Call node needs to be |
984 | // queried to know which specific result regs to be unspilled. |
985 | for (unsigned i = 0; i < regCount; ++i) |
986 | { |
987 | unsigned flags = call->GetRegSpillFlagByIdx(i); |
988 | if ((flags & GTF_SPILLED) != 0) |
989 | { |
990 | var_types dstType = retTypeDesc->GetReturnRegType(i); |
991 | regNumber unspillTreeReg = call->GetRegNumByIdx(i); |
992 | |
993 | if (reloadTree != nullptr) |
994 | { |
995 | dstReg = reloadTree->GetRegNumByIdx(i); |
996 | if (dstReg == REG_NA) |
997 | { |
998 | dstReg = unspillTreeReg; |
999 | } |
1000 | } |
1001 | else |
1002 | { |
1003 | dstReg = unspillTreeReg; |
1004 | } |
1005 | |
1006 | TempDsc* t = regSet.rsUnspillInPlace(call, unspillTreeReg, i); |
1007 | getEmitter()->emitIns_R_S(ins_Load(dstType), emitActualTypeSize(dstType), dstReg, t->tdTempNum(), |
1008 | 0); |
1009 | regSet.tmpRlsTemp(t); |
1010 | gcInfo.gcMarkRegPtrVal(dstReg, dstType); |
1011 | } |
1012 | } |
1013 | |
1014 | unspillTree->gtFlags &= ~GTF_SPILLED; |
1015 | } |
1016 | #if FEATURE_ARG_SPLIT |
1017 | else if (unspillTree->OperIsPutArgSplit()) |
1018 | { |
1019 | GenTreePutArgSplit* splitArg = unspillTree->AsPutArgSplit(); |
1020 | unsigned regCount = splitArg->gtNumRegs; |
1021 | |
1022 | // In case of split struct argument node, GTF_SPILLED flag on it indicates that |
1023 | // one or more of its result regs are spilled. Call node needs to be |
1024 | // queried to know which specific result regs to be unspilled. |
1025 | for (unsigned i = 0; i < regCount; ++i) |
1026 | { |
1027 | unsigned flags = splitArg->GetRegSpillFlagByIdx(i); |
1028 | if ((flags & GTF_SPILLED) != 0) |
1029 | { |
1030 | BYTE* gcPtrs = splitArg->gtGcPtrs; |
1031 | var_types dstType = splitArg->GetRegType(i); |
1032 | regNumber dstReg = splitArg->GetRegNumByIdx(i); |
1033 | |
1034 | TempDsc* t = regSet.rsUnspillInPlace(splitArg, dstReg, i); |
1035 | getEmitter()->emitIns_R_S(ins_Load(dstType), emitActualTypeSize(dstType), dstReg, t->tdTempNum(), |
1036 | 0); |
1037 | regSet.tmpRlsTemp(t); |
1038 | gcInfo.gcMarkRegPtrVal(dstReg, dstType); |
1039 | } |
1040 | } |
1041 | |
1042 | unspillTree->gtFlags &= ~GTF_SPILLED; |
1043 | } |
1044 | #ifdef _TARGET_ARM_ |
1045 | else if (unspillTree->OperIsMultiRegOp()) |
1046 | { |
1047 | GenTreeMultiRegOp* multiReg = unspillTree->AsMultiRegOp(); |
1048 | unsigned regCount = multiReg->GetRegCount(); |
1049 | |
1050 | // In case of split struct argument node, GTF_SPILLED flag on it indicates that |
1051 | // one or more of its result regs are spilled. Call node needs to be |
1052 | // queried to know which specific result regs to be unspilled. |
1053 | for (unsigned i = 0; i < regCount; ++i) |
1054 | { |
1055 | unsigned flags = multiReg->GetRegSpillFlagByIdx(i); |
1056 | if ((flags & GTF_SPILLED) != 0) |
1057 | { |
1058 | var_types dstType = multiReg->GetRegType(i); |
1059 | regNumber dstReg = multiReg->GetRegNumByIdx(i); |
1060 | |
1061 | TempDsc* t = regSet.rsUnspillInPlace(multiReg, dstReg, i); |
1062 | getEmitter()->emitIns_R_S(ins_Load(dstType), emitActualTypeSize(dstType), dstReg, t->tdTempNum(), |
1063 | 0); |
1064 | regSet.tmpRlsTemp(t); |
1065 | gcInfo.gcMarkRegPtrVal(dstReg, dstType); |
1066 | } |
1067 | } |
1068 | |
1069 | unspillTree->gtFlags &= ~GTF_SPILLED; |
1070 | } |
1071 | #endif //_TARGET_ARM_ |
1072 | #endif // FEATURE_ARG_SPLIT |
1073 | else |
1074 | { |
1075 | TempDsc* t = regSet.rsUnspillInPlace(unspillTree, unspillTree->gtRegNum); |
1076 | getEmitter()->emitIns_R_S(ins_Load(unspillTree->gtType), emitActualTypeSize(unspillTree->TypeGet()), dstReg, |
1077 | t->tdTempNum(), 0); |
1078 | regSet.tmpRlsTemp(t); |
1079 | |
1080 | unspillTree->gtFlags &= ~GTF_SPILLED; |
1081 | gcInfo.gcMarkRegPtrVal(dstReg, unspillTree->TypeGet()); |
1082 | } |
1083 | } |
1084 | } |
1085 | |
1086 | //------------------------------------------------------------------------ |
1087 | // genCopyRegIfNeeded: Copy the given node into the specified register |
1088 | // |
1089 | // Arguments: |
1090 | // node - The node that has been evaluated (consumed). |
1091 | // needReg - The register in which its value is needed. |
1092 | // |
1093 | // Notes: |
1094 | // This must be a node that has a register. |
1095 | // |
1096 | void CodeGen::genCopyRegIfNeeded(GenTree* node, regNumber needReg) |
1097 | { |
1098 | assert((node->gtRegNum != REG_NA) && (needReg != REG_NA)); |
1099 | assert(!node->isUsedFromSpillTemp()); |
1100 | if (node->gtRegNum != needReg) |
1101 | { |
1102 | inst_RV_RV(INS_mov, needReg, node->gtRegNum, node->TypeGet()); |
1103 | } |
1104 | } |
1105 | |
1106 | // Do Liveness update for a subnodes that is being consumed by codegen |
1107 | // including the logic for reload in case is needed and also takes care |
1108 | // of locating the value on the desired register. |
1109 | void CodeGen::genConsumeRegAndCopy(GenTree* node, regNumber needReg) |
1110 | { |
1111 | if (needReg == REG_NA) |
1112 | { |
1113 | return; |
1114 | } |
1115 | regNumber treeReg = genConsumeReg(node); |
1116 | genCopyRegIfNeeded(node, needReg); |
1117 | } |
1118 | |
1119 | // Check that registers are consumed in the right order for the current node being generated. |
1120 | #ifdef DEBUG |
1121 | void CodeGen::genNumberOperandUse(GenTree* const operand, int& useNum) const |
1122 | { |
1123 | assert(operand != nullptr); |
1124 | |
1125 | // Ignore argument placeholders. |
1126 | if (operand->OperGet() == GT_ARGPLACE) |
1127 | { |
1128 | return; |
1129 | } |
1130 | |
1131 | assert(operand->gtUseNum == -1); |
1132 | |
1133 | if (!operand->isContained() && !operand->IsCopyOrReload()) |
1134 | { |
1135 | operand->gtUseNum = useNum; |
1136 | useNum++; |
1137 | } |
1138 | else |
1139 | { |
1140 | for (GenTree* operand : operand->Operands()) |
1141 | { |
1142 | genNumberOperandUse(operand, useNum); |
1143 | } |
1144 | } |
1145 | } |
1146 | |
1147 | void CodeGen::genCheckConsumeNode(GenTree* const node) |
1148 | { |
1149 | assert(node != nullptr); |
1150 | |
1151 | if (verbose) |
1152 | { |
1153 | if (node->gtUseNum == -1) |
1154 | { |
1155 | // nothing wrong if the node was not consumed |
1156 | } |
1157 | else if ((node->gtDebugFlags & GTF_DEBUG_NODE_CG_CONSUMED) != 0) |
1158 | { |
1159 | printf("Node was consumed twice:\n" ); |
1160 | compiler->gtDispTree(node, nullptr, nullptr, true); |
1161 | } |
1162 | else if ((lastConsumedNode != nullptr) && (node->gtUseNum < lastConsumedNode->gtUseNum)) |
1163 | { |
1164 | printf("Nodes were consumed out-of-order:\n" ); |
1165 | compiler->gtDispTree(lastConsumedNode, nullptr, nullptr, true); |
1166 | compiler->gtDispTree(node, nullptr, nullptr, true); |
1167 | } |
1168 | } |
1169 | |
1170 | assert((node->OperGet() == GT_CATCH_ARG) || ((node->gtDebugFlags & GTF_DEBUG_NODE_CG_CONSUMED) == 0)); |
1171 | assert((lastConsumedNode == nullptr) || (node->gtUseNum == -1) || (node->gtUseNum > lastConsumedNode->gtUseNum)); |
1172 | |
1173 | node->gtDebugFlags |= GTF_DEBUG_NODE_CG_CONSUMED; |
1174 | lastConsumedNode = node; |
1175 | } |
1176 | #endif // DEBUG |
1177 | |
1178 | //-------------------------------------------------------------------- |
1179 | // genConsumeReg: Do liveness update for a subnode that is being |
1180 | // consumed by codegen. |
1181 | // |
1182 | // Arguments: |
1183 | // tree - GenTree node |
1184 | // |
1185 | // Return Value: |
1186 | // Returns the reg number of tree. |
1187 | // In case of multi-reg call node returns the first reg number |
1188 | // of the multi-reg return. |
1189 | regNumber CodeGen::genConsumeReg(GenTree* tree) |
1190 | { |
1191 | if (tree->OperGet() == GT_COPY) |
1192 | { |
1193 | genRegCopy(tree); |
1194 | } |
1195 | |
1196 | // Handle the case where we have a lclVar that needs to be copied before use (i.e. because it |
1197 | // interferes with one of the other sources (or the target, if it's a "delayed use" register)). |
1198 | // TODO-Cleanup: This is a special copyReg case in LSRA - consider eliminating these and |
1199 | // always using GT_COPY to make the lclVar location explicit. |
1200 | // Note that we have to do this before calling genUpdateLife because otherwise if we spill it |
1201 | // the lvRegNum will be set to REG_STK and we will lose track of what register currently holds |
1202 | // the lclVar (normally when a lclVar is spilled it is then used from its former register |
1203 | // location, which matches the gtRegNum on the node). |
1204 | // (Note that it doesn't matter if we call this before or after genUnspillRegIfNeeded |
1205 | // because if it's on the stack it will always get reloaded into tree->gtRegNum). |
1206 | if (genIsRegCandidateLocal(tree)) |
1207 | { |
1208 | GenTreeLclVarCommon* lcl = tree->AsLclVarCommon(); |
1209 | LclVarDsc* varDsc = &compiler->lvaTable[lcl->GetLclNum()]; |
1210 | if (varDsc->lvRegNum != REG_STK && varDsc->lvRegNum != tree->gtRegNum) |
1211 | { |
1212 | inst_RV_RV(ins_Copy(tree->TypeGet()), tree->gtRegNum, varDsc->lvRegNum); |
1213 | } |
1214 | } |
1215 | |
1216 | genUnspillRegIfNeeded(tree); |
1217 | |
1218 | // genUpdateLife() will also spill local var if marked as GTF_SPILL by calling CodeGen::genSpillVar |
1219 | genUpdateLife(tree); |
1220 | |
1221 | assert(tree->gtHasReg()); |
1222 | |
1223 | // there are three cases where consuming a reg means clearing the bit in the live mask |
1224 | // 1. it was not produced by a local |
1225 | // 2. it was produced by a local that is going dead |
1226 | // 3. it was produced by a local that does not live in that reg (like one allocated on the stack) |
1227 | |
1228 | if (genIsRegCandidateLocal(tree)) |
1229 | { |
1230 | GenTreeLclVarCommon* lcl = tree->AsLclVarCommon(); |
1231 | LclVarDsc* varDsc = &compiler->lvaTable[lcl->GetLclNum()]; |
1232 | assert(varDsc->lvLRACandidate); |
1233 | |
1234 | if ((tree->gtFlags & GTF_VAR_DEATH) != 0) |
1235 | { |
1236 | gcInfo.gcMarkRegSetNpt(genRegMask(varDsc->lvRegNum)); |
1237 | } |
1238 | else if (varDsc->lvRegNum == REG_STK) |
1239 | { |
1240 | // We have loaded this into a register only temporarily |
1241 | gcInfo.gcMarkRegSetNpt(genRegMask(tree->gtRegNum)); |
1242 | } |
1243 | } |
1244 | else |
1245 | { |
1246 | gcInfo.gcMarkRegSetNpt(tree->gtGetRegMask()); |
1247 | } |
1248 | |
1249 | genCheckConsumeNode(tree); |
1250 | return tree->gtRegNum; |
1251 | } |
1252 | |
1253 | // Do liveness update for an address tree: one of GT_LEA, GT_LCL_VAR, or GT_CNS_INT (for call indirect). |
1254 | void CodeGen::genConsumeAddress(GenTree* addr) |
1255 | { |
1256 | if (!addr->isContained()) |
1257 | { |
1258 | genConsumeReg(addr); |
1259 | } |
1260 | else if (addr->OperGet() == GT_LEA) |
1261 | { |
1262 | genConsumeAddrMode(addr->AsAddrMode()); |
1263 | } |
1264 | } |
1265 | |
1266 | // do liveness update for a subnode that is being consumed by codegen |
1267 | void CodeGen::genConsumeAddrMode(GenTreeAddrMode* addr) |
1268 | { |
1269 | genConsumeOperands(addr); |
1270 | } |
1271 | |
1272 | void CodeGen::genConsumeRegs(GenTree* tree) |
1273 | { |
1274 | #if !defined(_TARGET_64BIT_) |
1275 | if (tree->OperGet() == GT_LONG) |
1276 | { |
1277 | genConsumeRegs(tree->gtGetOp1()); |
1278 | genConsumeRegs(tree->gtGetOp2()); |
1279 | return; |
1280 | } |
1281 | #endif // !defined(_TARGET_64BIT_) |
1282 | |
1283 | if (tree->isUsedFromSpillTemp()) |
1284 | { |
1285 | // spill temps are un-tracked and hence no need to update life |
1286 | } |
1287 | else if (tree->isContained()) |
1288 | { |
1289 | if (tree->isIndir()) |
1290 | { |
1291 | genConsumeAddress(tree->AsIndir()->Addr()); |
1292 | } |
1293 | #ifdef _TARGET_XARCH_ |
1294 | else if (tree->OperIsLocalRead()) |
1295 | { |
1296 | // A contained lcl var must be living on stack and marked as reg optional, or not be a |
1297 | // register candidate. |
1298 | unsigned varNum = tree->AsLclVarCommon()->GetLclNum(); |
1299 | LclVarDsc* varDsc = compiler->lvaTable + varNum; |
1300 | |
1301 | noway_assert(varDsc->lvRegNum == REG_STK); |
1302 | noway_assert(tree->IsRegOptional() || !varDsc->lvLRACandidate); |
1303 | |
1304 | // Update the life of the lcl var. |
1305 | genUpdateLife(tree); |
1306 | } |
1307 | #endif // _TARGET_XARCH_ |
1308 | else if (tree->OperIsInitVal()) |
1309 | { |
1310 | genConsumeReg(tree->gtGetOp1()); |
1311 | } |
1312 | else if (tree->OperIsHWIntrinsic()) |
1313 | { |
1314 | genConsumeReg(tree->gtGetOp1()); |
1315 | } |
1316 | else |
1317 | { |
1318 | #ifdef FEATURE_SIMD |
1319 | // (In)Equality operation that produces bool result, when compared |
1320 | // against Vector zero, marks its Vector Zero operand as contained. |
1321 | assert(tree->OperIsLeaf() || tree->IsIntegralConstVector(0)); |
1322 | #else |
1323 | assert(tree->OperIsLeaf()); |
1324 | #endif |
1325 | } |
1326 | } |
1327 | else |
1328 | { |
1329 | genConsumeReg(tree); |
1330 | } |
1331 | } |
1332 | |
1333 | //------------------------------------------------------------------------ |
1334 | // genConsumeOperands: Do liveness update for the operands of a unary or binary tree |
1335 | // |
1336 | // Arguments: |
1337 | // tree - the GenTreeOp whose operands will have their liveness updated. |
1338 | // |
1339 | // Return Value: |
1340 | // None. |
1341 | // |
1342 | // Notes: |
1343 | // Note that this logic is localized here because we must do the liveness update in |
1344 | // the correct execution order. This is important because we may have two operands |
1345 | // that involve the same lclVar, and if one is marked "lastUse" we must handle it |
1346 | // after the first. |
1347 | |
1348 | void CodeGen::genConsumeOperands(GenTreeOp* tree) |
1349 | { |
1350 | GenTree* firstOp = tree->gtOp1; |
1351 | GenTree* secondOp = tree->gtOp2; |
1352 | |
1353 | if (firstOp != nullptr) |
1354 | { |
1355 | genConsumeRegs(firstOp); |
1356 | } |
1357 | if (secondOp != nullptr) |
1358 | { |
1359 | genConsumeRegs(secondOp); |
1360 | } |
1361 | } |
1362 | |
1363 | #if FEATURE_PUT_STRUCT_ARG_STK |
1364 | //------------------------------------------------------------------------ |
1365 | // genConsumePutStructArgStk: Do liveness update for the operands of a PutArgStk node. |
1366 | // Also loads in the right register the addresses of the |
1367 | // src/dst for rep mov operation. |
1368 | // |
1369 | // Arguments: |
1370 | // putArgNode - the PUTARG_STK tree. |
1371 | // dstReg - the dstReg for the rep move operation. |
1372 | // srcReg - the srcReg for the rep move operation. |
1373 | // sizeReg - the sizeReg for the rep move operation. |
1374 | // |
1375 | // Return Value: |
1376 | // None. |
1377 | // |
1378 | // Notes: |
1379 | // sizeReg can be REG_NA when this function is used to consume the dstReg and srcReg |
1380 | // for copying on the stack a struct with references. |
1381 | // The source address/offset is determined from the address on the GT_OBJ node, while |
1382 | // the destination address is the address contained in 'm_stkArgVarNum' plus the offset |
1383 | // provided in the 'putArgNode'. |
1384 | // m_stkArgVarNum must be set to the varnum for the local used for placing the "by-value" args on the stack. |
1385 | |
1386 | void CodeGen::genConsumePutStructArgStk(GenTreePutArgStk* putArgNode, |
1387 | regNumber dstReg, |
1388 | regNumber srcReg, |
1389 | regNumber sizeReg) |
1390 | { |
1391 | // The putArgNode children are always contained. We should not consume any registers. |
1392 | assert(putArgNode->gtGetOp1()->isContained()); |
1393 | |
1394 | // Get the source address. |
1395 | GenTree* src = putArgNode->gtGetOp1(); |
1396 | assert(varTypeIsStruct(src)); |
1397 | assert((src->gtOper == GT_OBJ) || ((src->gtOper == GT_IND && varTypeIsSIMD(src)))); |
1398 | GenTree* srcAddr = src->gtGetOp1(); |
1399 | |
1400 | unsigned int size = putArgNode->getArgSize(); |
1401 | |
1402 | assert(dstReg != REG_NA); |
1403 | assert(srcReg != REG_NA); |
1404 | |
1405 | // Consume the registers only if they are not contained or set to REG_NA. |
1406 | if (srcAddr->gtRegNum != REG_NA) |
1407 | { |
1408 | genConsumeReg(srcAddr); |
1409 | } |
1410 | |
1411 | // If the op1 is already in the dstReg - nothing to do. |
1412 | // Otherwise load the op1 (GT_ADDR) into the dstReg to copy the struct on the stack by value. |
1413 | CLANG_FORMAT_COMMENT_ANCHOR; |
1414 | |
1415 | #ifdef _TARGET_X86_ |
1416 | assert(dstReg != REG_SPBASE); |
1417 | inst_RV_RV(INS_mov, dstReg, REG_SPBASE); |
1418 | #else // !_TARGET_X86_ |
1419 | GenTree* dstAddr = putArgNode; |
1420 | if (dstAddr->gtRegNum != dstReg) |
1421 | { |
1422 | // Generate LEA instruction to load the stack of the outgoing var + SlotNum offset (or the incoming arg area |
1423 | // for tail calls) in RDI. |
1424 | // Destination is always local (on the stack) - use EA_PTRSIZE. |
1425 | assert(m_stkArgVarNum != BAD_VAR_NUM); |
1426 | getEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, dstReg, m_stkArgVarNum, putArgNode->getArgOffset()); |
1427 | } |
1428 | #endif // !_TARGET_X86_ |
1429 | |
1430 | if (srcAddr->gtRegNum != srcReg) |
1431 | { |
1432 | if (srcAddr->OperIsLocalAddr()) |
1433 | { |
1434 | // The OperLocalAddr is always contained. |
1435 | assert(srcAddr->isContained()); |
1436 | GenTreeLclVarCommon* lclNode = srcAddr->AsLclVarCommon(); |
1437 | |
1438 | // Generate LEA instruction to load the LclVar address in RSI. |
1439 | // Source is known to be on the stack. Use EA_PTRSIZE. |
1440 | unsigned int offset = 0; |
1441 | if (srcAddr->OperGet() == GT_LCL_FLD_ADDR) |
1442 | { |
1443 | offset = srcAddr->AsLclFld()->gtLclOffs; |
1444 | } |
1445 | getEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, srcReg, lclNode->gtLclNum, offset); |
1446 | } |
1447 | else |
1448 | { |
1449 | assert(srcAddr->gtRegNum != REG_NA); |
1450 | // Source is not known to be on the stack. Use EA_BYREF. |
1451 | getEmitter()->emitIns_R_R(INS_mov, EA_BYREF, srcReg, srcAddr->gtRegNum); |
1452 | } |
1453 | } |
1454 | |
1455 | if (sizeReg != REG_NA) |
1456 | { |
1457 | inst_RV_IV(INS_mov, sizeReg, size, EA_PTRSIZE); |
1458 | } |
1459 | } |
1460 | #endif // FEATURE_PUT_STRUCT_ARG_STK |
1461 | |
1462 | #if FEATURE_ARG_SPLIT |
1463 | //------------------------------------------------------------------------ |
1464 | // genConsumeArgRegSplit: Consume register(s) in Call node to set split struct argument. |
1465 | // Liveness update for the PutArgSplit node is not needed |
1466 | // |
1467 | // Arguments: |
1468 | // putArgNode - the PUTARG_STK tree. |
1469 | // |
1470 | // Return Value: |
1471 | // None. |
1472 | // |
1473 | void CodeGen::genConsumeArgSplitStruct(GenTreePutArgSplit* putArgNode) |
1474 | { |
1475 | assert(putArgNode->OperGet() == GT_PUTARG_SPLIT); |
1476 | assert(putArgNode->gtHasReg()); |
1477 | |
1478 | genUnspillRegIfNeeded(putArgNode); |
1479 | |
1480 | // Skip updating GC info |
1481 | // GC info for all argument registers will be cleared in caller |
1482 | |
1483 | genCheckConsumeNode(putArgNode); |
1484 | } |
1485 | #endif // FEATURE_ARG_SPLIT |
1486 | |
1487 | //------------------------------------------------------------------------ |
1488 | // genPutArgStkFieldList: Generate code for a putArgStk whose source is a GT_FIELD_LIST |
1489 | // |
1490 | // Arguments: |
1491 | // putArgStk - The putArgStk node |
1492 | // outArgVarNum - The lclVar num for the argument |
1493 | // |
1494 | // Notes: |
1495 | // The x86 version of this is in codegenxarch.cpp, and doesn't take an |
1496 | // outArgVarNum, as it pushes its args onto the stack. |
1497 | // |
1498 | #ifndef _TARGET_X86_ |
1499 | void CodeGen::genPutArgStkFieldList(GenTreePutArgStk* putArgStk, unsigned outArgVarNum) |
1500 | { |
1501 | assert(putArgStk->gtOp1->OperIs(GT_FIELD_LIST)); |
1502 | |
1503 | // Evaluate each of the GT_FIELD_LIST items into their register |
1504 | // and store their register into the outgoing argument area. |
1505 | unsigned argOffset = putArgStk->getArgOffset(); |
1506 | for (GenTreeFieldList* fieldListPtr = putArgStk->gtOp1->AsFieldList(); fieldListPtr != nullptr; |
1507 | fieldListPtr = fieldListPtr->Rest()) |
1508 | { |
1509 | GenTree* nextArgNode = fieldListPtr->gtOp.gtOp1; |
1510 | genConsumeReg(nextArgNode); |
1511 | |
1512 | regNumber reg = nextArgNode->gtRegNum; |
1513 | var_types type = nextArgNode->TypeGet(); |
1514 | emitAttr attr = emitTypeSize(type); |
1515 | |
1516 | // Emit store instructions to store the registers produced by the GT_FIELD_LIST into the outgoing |
1517 | // argument area. |
1518 | unsigned thisFieldOffset = argOffset + fieldListPtr->gtFieldOffset; |
1519 | getEmitter()->emitIns_S_R(ins_Store(type), attr, reg, outArgVarNum, thisFieldOffset); |
1520 | |
1521 | // We can't write beyound the arg area |
1522 | assert((thisFieldOffset + EA_SIZE_IN_BYTES(attr)) <= compiler->lvaLclSize(outArgVarNum)); |
1523 | } |
1524 | } |
1525 | #endif // !_TARGET_X86_ |
1526 | |
1527 | //------------------------------------------------------------------------ |
1528 | // genSetBlockSize: Ensure that the block size is in the given register |
1529 | // |
1530 | // Arguments: |
1531 | // blkNode - The block node |
1532 | // sizeReg - The register into which the block's size should go |
1533 | // |
1534 | |
1535 | void CodeGen::genSetBlockSize(GenTreeBlk* blkNode, regNumber sizeReg) |
1536 | { |
1537 | if (sizeReg != REG_NA) |
1538 | { |
1539 | unsigned blockSize = blkNode->Size(); |
1540 | if (blockSize != 0) |
1541 | { |
1542 | assert((blkNode->gtRsvdRegs & genRegMask(sizeReg)) != 0); |
1543 | genSetRegToIcon(sizeReg, blockSize); |
1544 | } |
1545 | else |
1546 | { |
1547 | noway_assert(blkNode->gtOper == GT_STORE_DYN_BLK); |
1548 | GenTree* sizeNode = blkNode->AsDynBlk()->gtDynamicSize; |
1549 | if (sizeNode->gtRegNum != sizeReg) |
1550 | { |
1551 | inst_RV_RV(INS_mov, sizeReg, sizeNode->gtRegNum, sizeNode->TypeGet()); |
1552 | } |
1553 | } |
1554 | } |
1555 | } |
1556 | |
1557 | //------------------------------------------------------------------------ |
1558 | // genConsumeBlockSrc: Consume the source address register of a block node, if any. |
1559 | // |
1560 | // Arguments: |
1561 | // blkNode - The block node |
1562 | |
1563 | void CodeGen::genConsumeBlockSrc(GenTreeBlk* blkNode) |
1564 | { |
1565 | GenTree* src = blkNode->Data(); |
1566 | if (blkNode->OperIsCopyBlkOp()) |
1567 | { |
1568 | // For a CopyBlk we need the address of the source. |
1569 | if (src->OperGet() == GT_IND) |
1570 | { |
1571 | src = src->gtOp.gtOp1; |
1572 | } |
1573 | else |
1574 | { |
1575 | // This must be a local. |
1576 | // For this case, there is no source address register, as it is a |
1577 | // stack-based address. |
1578 | assert(src->OperIsLocal()); |
1579 | return; |
1580 | } |
1581 | } |
1582 | else |
1583 | { |
1584 | if (src->OperIsInitVal()) |
1585 | { |
1586 | src = src->gtGetOp1(); |
1587 | } |
1588 | } |
1589 | genConsumeReg(src); |
1590 | } |
1591 | |
1592 | //------------------------------------------------------------------------ |
1593 | // genSetBlockSrc: Ensure that the block source is in its allocated register. |
1594 | // |
1595 | // Arguments: |
1596 | // blkNode - The block node |
1597 | // srcReg - The register in which to set the source (address or init val). |
1598 | // |
1599 | void CodeGen::genSetBlockSrc(GenTreeBlk* blkNode, regNumber srcReg) |
1600 | { |
1601 | GenTree* src = blkNode->Data(); |
1602 | if (blkNode->OperIsCopyBlkOp()) |
1603 | { |
1604 | // For a CopyBlk we need the address of the source. |
1605 | if (src->OperGet() == GT_IND) |
1606 | { |
1607 | src = src->gtOp.gtOp1; |
1608 | } |
1609 | else |
1610 | { |
1611 | // This must be a local struct. |
1612 | // Load its address into srcReg. |
1613 | inst_RV_TT(INS_lea, srcReg, src, 0, EA_BYREF); |
1614 | return; |
1615 | } |
1616 | } |
1617 | else |
1618 | { |
1619 | if (src->OperIsInitVal()) |
1620 | { |
1621 | src = src->gtGetOp1(); |
1622 | } |
1623 | } |
1624 | genCopyRegIfNeeded(src, srcReg); |
1625 | } |
1626 | |
1627 | //------------------------------------------------------------------------ |
1628 | // genConsumeBlockOp: Ensure that the block's operands are enregistered |
1629 | // as needed. |
1630 | // Arguments: |
1631 | // blkNode - The block node |
1632 | // |
1633 | // Notes: |
1634 | // This ensures that the operands are consumed in the proper order to |
1635 | // obey liveness modeling. |
1636 | |
1637 | void CodeGen::genConsumeBlockOp(GenTreeBlk* blkNode, regNumber dstReg, regNumber srcReg, regNumber sizeReg) |
1638 | { |
1639 | // We have to consume the registers, and perform any copies, in the actual execution order: dst, src, size. |
1640 | // |
1641 | // Note that the register allocator ensures that the registers ON THE NODES will not interfere |
1642 | // with one another if consumed (i.e. reloaded or moved to their ASSIGNED reg) in execution order. |
1643 | // Further, it ensures that they will not interfere with one another if they are then copied |
1644 | // to the REQUIRED register (if a fixed register requirement) in execution order. This requires, |
1645 | // then, that we first consume all the operands, then do any necessary moves. |
1646 | |
1647 | GenTree* const dstAddr = blkNode->Addr(); |
1648 | |
1649 | // First, consume all the sources in order. |
1650 | genConsumeReg(dstAddr); |
1651 | genConsumeBlockSrc(blkNode); |
1652 | if (blkNode->OperGet() == GT_STORE_DYN_BLK) |
1653 | { |
1654 | genConsumeReg(blkNode->AsDynBlk()->gtDynamicSize); |
1655 | } |
1656 | |
1657 | // Next, perform any necessary moves. |
1658 | genCopyRegIfNeeded(dstAddr, dstReg); |
1659 | genSetBlockSrc(blkNode, srcReg); |
1660 | genSetBlockSize(blkNode, sizeReg); |
1661 | } |
1662 | |
1663 | //------------------------------------------------------------------------- |
1664 | // genProduceReg: do liveness update for register produced by the current |
1665 | // node in codegen. |
1666 | // |
1667 | // Arguments: |
1668 | // tree - Gentree node |
1669 | // |
1670 | // Return Value: |
1671 | // None. |
1672 | void CodeGen::genProduceReg(GenTree* tree) |
1673 | { |
1674 | #ifdef DEBUG |
1675 | assert((tree->gtDebugFlags & GTF_DEBUG_NODE_CG_PRODUCED) == 0); |
1676 | tree->gtDebugFlags |= GTF_DEBUG_NODE_CG_PRODUCED; |
1677 | #endif |
1678 | |
1679 | if (tree->gtFlags & GTF_SPILL) |
1680 | { |
1681 | // Code for GT_COPY node gets generated as part of consuming regs by its parent. |
1682 | // A GT_COPY node in turn produces reg result and it should never be marked to |
1683 | // spill. |
1684 | // |
1685 | // Similarly GT_RELOAD node gets generated as part of consuming regs by its |
1686 | // parent and should never be marked for spilling. |
1687 | noway_assert(!tree->IsCopyOrReload()); |
1688 | |
1689 | if (genIsRegCandidateLocal(tree)) |
1690 | { |
1691 | // Store local variable to its home location. |
1692 | // Ensure that lclVar stores are typed correctly. |
1693 | unsigned varNum = tree->gtLclVarCommon.gtLclNum; |
1694 | assert(!compiler->lvaTable[varNum].lvNormalizeOnStore() || |
1695 | (tree->TypeGet() == genActualType(compiler->lvaTable[varNum].TypeGet()))); |
1696 | inst_TT_RV(ins_Store(tree->gtType, compiler->isSIMDTypeLocalAligned(varNum)), tree, tree->gtRegNum); |
1697 | } |
1698 | else |
1699 | { |
1700 | // In case of multi-reg call node, spill flag on call node |
1701 | // indicates that one or more of its allocated regs need to |
1702 | // be spilled. Call node needs to be further queried to |
1703 | // know which of its result regs needs to be spilled. |
1704 | if (tree->IsMultiRegCall()) |
1705 | { |
1706 | GenTreeCall* call = tree->AsCall(); |
1707 | ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); |
1708 | unsigned regCount = retTypeDesc->GetReturnRegCount(); |
1709 | |
1710 | for (unsigned i = 0; i < regCount; ++i) |
1711 | { |
1712 | unsigned flags = call->GetRegSpillFlagByIdx(i); |
1713 | if ((flags & GTF_SPILL) != 0) |
1714 | { |
1715 | regNumber reg = call->GetRegNumByIdx(i); |
1716 | regSet.rsSpillTree(reg, call, i); |
1717 | gcInfo.gcMarkRegSetNpt(genRegMask(reg)); |
1718 | } |
1719 | } |
1720 | } |
1721 | #if FEATURE_ARG_SPLIT |
1722 | else if (tree->OperIsPutArgSplit()) |
1723 | { |
1724 | GenTreePutArgSplit* argSplit = tree->AsPutArgSplit(); |
1725 | unsigned regCount = argSplit->gtNumRegs; |
1726 | |
1727 | for (unsigned i = 0; i < regCount; ++i) |
1728 | { |
1729 | unsigned flags = argSplit->GetRegSpillFlagByIdx(i); |
1730 | if ((flags & GTF_SPILL) != 0) |
1731 | { |
1732 | regNumber reg = argSplit->GetRegNumByIdx(i); |
1733 | regSet.rsSpillTree(reg, argSplit, i); |
1734 | gcInfo.gcMarkRegSetNpt(genRegMask(reg)); |
1735 | } |
1736 | } |
1737 | } |
1738 | #ifdef _TARGET_ARM_ |
1739 | else if (tree->OperIsMultiRegOp()) |
1740 | { |
1741 | GenTreeMultiRegOp* multiReg = tree->AsMultiRegOp(); |
1742 | unsigned regCount = multiReg->GetRegCount(); |
1743 | |
1744 | for (unsigned i = 0; i < regCount; ++i) |
1745 | { |
1746 | unsigned flags = multiReg->GetRegSpillFlagByIdx(i); |
1747 | if ((flags & GTF_SPILL) != 0) |
1748 | { |
1749 | regNumber reg = multiReg->GetRegNumByIdx(i); |
1750 | regSet.rsSpillTree(reg, multiReg, i); |
1751 | gcInfo.gcMarkRegSetNpt(genRegMask(reg)); |
1752 | } |
1753 | } |
1754 | } |
1755 | #endif // _TARGET_ARM_ |
1756 | #endif // FEATURE_ARG_SPLIT |
1757 | else |
1758 | { |
1759 | regSet.rsSpillTree(tree->gtRegNum, tree); |
1760 | gcInfo.gcMarkRegSetNpt(genRegMask(tree->gtRegNum)); |
1761 | } |
1762 | |
1763 | tree->gtFlags |= GTF_SPILLED; |
1764 | tree->gtFlags &= ~GTF_SPILL; |
1765 | |
1766 | return; |
1767 | } |
1768 | } |
1769 | |
1770 | genUpdateLife(tree); |
1771 | |
1772 | // If we've produced a register, mark it as a pointer, as needed. |
1773 | if (tree->gtHasReg()) |
1774 | { |
1775 | // We only mark the register in the following cases: |
1776 | // 1. It is not a register candidate local. In this case, we're producing a |
1777 | // register from a local, but the local is not a register candidate. Thus, |
1778 | // we must be loading it as a temp register, and any "last use" flag on |
1779 | // the register wouldn't be relevant. |
1780 | // 2. The register candidate local is going dead. There's no point to mark |
1781 | // the register as live, with a GC pointer, if the variable is dead. |
1782 | if (!genIsRegCandidateLocal(tree) || ((tree->gtFlags & GTF_VAR_DEATH) == 0)) |
1783 | { |
1784 | // Multi-reg call node will produce more than one register result. |
1785 | // Mark all the regs produced by call node. |
1786 | if (tree->IsMultiRegCall()) |
1787 | { |
1788 | GenTreeCall* call = tree->AsCall(); |
1789 | ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); |
1790 | unsigned regCount = retTypeDesc->GetReturnRegCount(); |
1791 | |
1792 | for (unsigned i = 0; i < regCount; ++i) |
1793 | { |
1794 | regNumber reg = call->GetRegNumByIdx(i); |
1795 | var_types type = retTypeDesc->GetReturnRegType(i); |
1796 | gcInfo.gcMarkRegPtrVal(reg, type); |
1797 | } |
1798 | } |
1799 | else if (tree->IsCopyOrReloadOfMultiRegCall()) |
1800 | { |
1801 | // we should never see reload of multi-reg call here |
1802 | // because GT_RELOAD gets generated in reg consuming path. |
1803 | noway_assert(tree->OperGet() == GT_COPY); |
1804 | |
1805 | // A multi-reg GT_COPY node produces those regs to which |
1806 | // copy has taken place. |
1807 | GenTreeCopyOrReload* copy = tree->AsCopyOrReload(); |
1808 | GenTreeCall* call = copy->gtGetOp1()->AsCall(); |
1809 | ReturnTypeDesc* retTypeDesc = call->GetReturnTypeDesc(); |
1810 | unsigned regCount = retTypeDesc->GetReturnRegCount(); |
1811 | |
1812 | for (unsigned i = 0; i < regCount; ++i) |
1813 | { |
1814 | var_types type = retTypeDesc->GetReturnRegType(i); |
1815 | regNumber fromReg = call->GetRegNumByIdx(i); |
1816 | regNumber toReg = copy->GetRegNumByIdx(i); |
1817 | |
1818 | if (toReg != REG_NA) |
1819 | { |
1820 | gcInfo.gcMarkRegPtrVal(toReg, type); |
1821 | } |
1822 | } |
1823 | } |
1824 | else |
1825 | { |
1826 | gcInfo.gcMarkRegPtrVal(tree->gtRegNum, tree->TypeGet()); |
1827 | } |
1828 | } |
1829 | } |
1830 | } |
1831 | |
1832 | // transfer gc/byref status of src reg to dst reg |
1833 | void CodeGen::genTransferRegGCState(regNumber dst, regNumber src) |
1834 | { |
1835 | regMaskTP srcMask = genRegMask(src); |
1836 | regMaskTP dstMask = genRegMask(dst); |
1837 | |
1838 | if (gcInfo.gcRegGCrefSetCur & srcMask) |
1839 | { |
1840 | gcInfo.gcMarkRegSetGCref(dstMask); |
1841 | } |
1842 | else if (gcInfo.gcRegByrefSetCur & srcMask) |
1843 | { |
1844 | gcInfo.gcMarkRegSetByref(dstMask); |
1845 | } |
1846 | else |
1847 | { |
1848 | gcInfo.gcMarkRegSetNpt(dstMask); |
1849 | } |
1850 | } |
1851 | |
1852 | // generates an ip-relative call or indirect call via reg ('call reg') |
1853 | // pass in 'addr' for a relative call or 'base' for a indirect register call |
1854 | // methHnd - optional, only used for pretty printing |
1855 | // retSize - emitter type of return for GC purposes, should be EA_BYREF, EA_GCREF, or EA_PTRSIZE(not GC) |
1856 | // |
1857 | // clang-format off |
1858 | void CodeGen::genEmitCall(int callType, |
1859 | CORINFO_METHOD_HANDLE methHnd, |
1860 | INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) |
1861 | void* addr |
1862 | X86_ARG(int argSize), |
1863 | emitAttr retSize |
1864 | MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize), |
1865 | IL_OFFSETX ilOffset, |
1866 | regNumber base, |
1867 | bool isJump) |
1868 | { |
1869 | #if !defined(_TARGET_X86_) |
1870 | int argSize = 0; |
1871 | #endif // !defined(_TARGET_X86_) |
1872 | getEmitter()->emitIns_Call(emitter::EmitCallType(callType), |
1873 | methHnd, |
1874 | INDEBUG_LDISASM_COMMA(sigInfo) |
1875 | addr, |
1876 | argSize, |
1877 | retSize |
1878 | MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), |
1879 | gcInfo.gcVarPtrSetCur, |
1880 | gcInfo.gcRegGCrefSetCur, |
1881 | gcInfo.gcRegByrefSetCur, |
1882 | ilOffset, base, REG_NA, 0, 0, isJump); |
1883 | } |
1884 | // clang-format on |
1885 | |
1886 | // generates an indirect call via addressing mode (call []) given an indir node |
1887 | // methHnd - optional, only used for pretty printing |
1888 | // retSize - emitter type of return for GC purposes, should be EA_BYREF, EA_GCREF, or EA_PTRSIZE(not GC) |
1889 | // |
1890 | // clang-format off |
1891 | void CodeGen::genEmitCall(int callType, |
1892 | CORINFO_METHOD_HANDLE methHnd, |
1893 | INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) |
1894 | GenTreeIndir* indir |
1895 | X86_ARG(int argSize), |
1896 | emitAttr retSize |
1897 | MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize), |
1898 | IL_OFFSETX ilOffset) |
1899 | { |
1900 | #if !defined(_TARGET_X86_) |
1901 | int argSize = 0; |
1902 | #endif // !defined(_TARGET_X86_) |
1903 | genConsumeAddress(indir->Addr()); |
1904 | |
1905 | getEmitter()->emitIns_Call(emitter::EmitCallType(callType), |
1906 | methHnd, |
1907 | INDEBUG_LDISASM_COMMA(sigInfo) |
1908 | nullptr, |
1909 | argSize, |
1910 | retSize |
1911 | MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(secondRetSize), |
1912 | gcInfo.gcVarPtrSetCur, |
1913 | gcInfo.gcRegGCrefSetCur, |
1914 | gcInfo.gcRegByrefSetCur, |
1915 | ilOffset, |
1916 | (indir->Base() != nullptr) ? indir->Base()->gtRegNum : REG_NA, |
1917 | (indir->Index() != nullptr) ? indir->Index()->gtRegNum : REG_NA, |
1918 | indir->Scale(), |
1919 | indir->Offset()); |
1920 | } |
1921 | // clang-format on |
1922 | |
1923 | //------------------------------------------------------------------------ |
1924 | // genCodeForCast: Generates the code for GT_CAST. |
1925 | // |
1926 | // Arguments: |
1927 | // tree - the GT_CAST node. |
1928 | // |
1929 | void CodeGen::genCodeForCast(GenTreeOp* tree) |
1930 | { |
1931 | assert(tree->OperIs(GT_CAST)); |
1932 | |
1933 | var_types targetType = tree->TypeGet(); |
1934 | |
1935 | if (varTypeIsFloating(targetType) && varTypeIsFloating(tree->gtOp1)) |
1936 | { |
1937 | // Casts float/double <--> double/float |
1938 | genFloatToFloatCast(tree); |
1939 | } |
1940 | else if (varTypeIsFloating(tree->gtOp1)) |
1941 | { |
1942 | // Casts float/double --> int32/int64 |
1943 | genFloatToIntCast(tree); |
1944 | } |
1945 | else if (varTypeIsFloating(targetType)) |
1946 | { |
1947 | // Casts int32/uint32/int64/uint64 --> float/double |
1948 | genIntToFloatCast(tree); |
1949 | } |
1950 | #ifndef _TARGET_64BIT_ |
1951 | else if (varTypeIsLong(tree->gtOp1)) |
1952 | { |
1953 | genLongToIntCast(tree); |
1954 | } |
1955 | #endif // !_TARGET_64BIT_ |
1956 | else |
1957 | { |
1958 | // Casts int <--> int |
1959 | genIntToIntCast(tree->AsCast()); |
1960 | } |
1961 | // The per-case functions call genProduceReg() |
1962 | } |
1963 | |
1964 | CodeGen::GenIntCastDesc::GenIntCastDesc(GenTreeCast* cast) |
1965 | { |
1966 | const var_types srcType = genActualType(cast->gtGetOp1()->TypeGet()); |
1967 | const bool srcUnsigned = cast->IsUnsigned(); |
1968 | const unsigned srcSize = genTypeSize(srcType); |
1969 | const var_types castType = cast->gtCastType; |
1970 | const bool castUnsigned = varTypeIsUnsigned(castType); |
1971 | const unsigned castSize = genTypeSize(castType); |
1972 | const var_types dstType = genActualType(cast->TypeGet()); |
1973 | const unsigned dstSize = genTypeSize(dstType); |
1974 | const bool overflow = cast->gtOverflow(); |
1975 | |
1976 | assert((srcSize == 4) || (srcSize == genTypeSize(TYP_I_IMPL))); |
1977 | assert((dstSize == 4) || (dstSize == genTypeSize(TYP_I_IMPL))); |
1978 | |
1979 | assert(dstSize == genTypeSize(genActualType(castType))); |
1980 | |
1981 | if (castSize < 4) // Cast to small int type |
1982 | { |
1983 | if (overflow) |
1984 | { |
1985 | m_checkKind = CHECK_SMALL_INT_RANGE; |
1986 | m_checkSrcSize = srcSize; |
1987 | // Since these are small int types we can compute the min and max |
1988 | // values of the castType without risk of integer overflow. |
1989 | const int castNumBits = (castSize * 8) - (castUnsigned ? 0 : 1); |
1990 | m_checkSmallIntMax = (1 << castNumBits) - 1; |
1991 | m_checkSmallIntMin = (castUnsigned | srcUnsigned) ? 0 : (-m_checkSmallIntMax - 1); |
1992 | |
1993 | m_extendKind = COPY; |
1994 | m_extendSrcSize = dstSize; |
1995 | } |
1996 | else |
1997 | { |
1998 | m_checkKind = CHECK_NONE; |
1999 | |
2000 | // Casting to a small type really means widening from that small type to INT/LONG. |
2001 | m_extendKind = castUnsigned ? ZERO_EXTEND_SMALL_INT : SIGN_EXTEND_SMALL_INT; |
2002 | m_extendSrcSize = castSize; |
2003 | } |
2004 | } |
2005 | #ifdef _TARGET_64BIT_ |
2006 | // castType cannot be (U)LONG on 32 bit targets, such casts should have been decomposed. |
2007 | // srcType cannot be a small int type since it's the "actual type" of the cast operand. |
2008 | // This means that widening casts do not occur on 32 bit targets. |
2009 | else if (castSize > srcSize) // (U)INT to (U)LONG widening cast |
2010 | { |
2011 | assert((srcSize == 4) && (castSize == 8)); |
2012 | |
2013 | if (overflow && !srcUnsigned && castUnsigned) |
2014 | { |
2015 | // Widening from INT to ULONG, check if the value is positive |
2016 | m_checkKind = CHECK_POSITIVE; |
2017 | m_checkSrcSize = 4; |
2018 | |
2019 | // This is the only overflow checking cast that requires changing the |
2020 | // source value (by zero extending), all others copy the value as is. |
2021 | assert((srcType == TYP_INT) && (castType == TYP_ULONG)); |
2022 | m_extendKind = ZERO_EXTEND_INT; |
2023 | m_extendSrcSize = 4; |
2024 | } |
2025 | else |
2026 | { |
2027 | m_checkKind = CHECK_NONE; |
2028 | |
2029 | m_extendKind = srcUnsigned ? ZERO_EXTEND_INT : SIGN_EXTEND_INT; |
2030 | m_extendSrcSize = 4; |
2031 | } |
2032 | } |
2033 | else if (castSize < srcSize) // (U)LONG to (U)INT narrowing cast |
2034 | { |
2035 | assert((srcSize == 8) && (castSize == 4)); |
2036 | |
2037 | if (overflow) |
2038 | { |
2039 | if (castUnsigned) // (U)LONG to UINT cast |
2040 | { |
2041 | m_checkKind = CHECK_UINT_RANGE; |
2042 | } |
2043 | else if (srcUnsigned) // ULONG to INT cast |
2044 | { |
2045 | m_checkKind = CHECK_POSITIVE_INT_RANGE; |
2046 | } |
2047 | else // LONG to INT cast |
2048 | { |
2049 | m_checkKind = CHECK_INT_RANGE; |
2050 | } |
2051 | |
2052 | m_checkSrcSize = 8; |
2053 | } |
2054 | else |
2055 | { |
2056 | m_checkKind = CHECK_NONE; |
2057 | } |
2058 | |
2059 | m_extendKind = COPY; |
2060 | m_extendSrcSize = 4; |
2061 | } |
2062 | #endif |
2063 | else // if (castSize == srcSize) // Sign changing or same type cast |
2064 | { |
2065 | assert(castSize == srcSize); |
2066 | |
2067 | if (overflow && (srcUnsigned != castUnsigned)) |
2068 | { |
2069 | m_checkKind = CHECK_POSITIVE; |
2070 | m_checkSrcSize = srcSize; |
2071 | } |
2072 | else |
2073 | { |
2074 | m_checkKind = CHECK_NONE; |
2075 | } |
2076 | |
2077 | m_extendKind = COPY; |
2078 | m_extendSrcSize = srcSize; |
2079 | } |
2080 | } |
2081 | |
2082 | #if !defined(_TARGET_64BIT_) |
2083 | //------------------------------------------------------------------------ |
2084 | // genStoreLongLclVar: Generate code to store a non-enregistered long lclVar |
2085 | // |
2086 | // Arguments: |
2087 | // treeNode - A TYP_LONG lclVar node. |
2088 | // |
2089 | // Return Value: |
2090 | // None. |
2091 | // |
2092 | // Assumptions: |
2093 | // 'treeNode' must be a TYP_LONG lclVar node for a lclVar that has NOT been promoted. |
2094 | // Its operand must be a GT_LONG node. |
2095 | // |
2096 | void CodeGen::genStoreLongLclVar(GenTree* treeNode) |
2097 | { |
2098 | emitter* emit = getEmitter(); |
2099 | |
2100 | GenTreeLclVarCommon* lclNode = treeNode->AsLclVarCommon(); |
2101 | unsigned lclNum = lclNode->gtLclNum; |
2102 | LclVarDsc* varDsc = &(compiler->lvaTable[lclNum]); |
2103 | assert(varDsc->TypeGet() == TYP_LONG); |
2104 | assert(!varDsc->lvPromoted); |
2105 | GenTree* op1 = treeNode->gtOp.gtOp1; |
2106 | |
2107 | // A GT_LONG is always contained, so it cannot have RELOAD or COPY inserted between it and its consumer, |
2108 | // but a MUL_LONG may. |
2109 | noway_assert(op1->OperIs(GT_LONG) || op1->gtSkipReloadOrCopy()->OperIs(GT_MUL_LONG)); |
2110 | genConsumeRegs(op1); |
2111 | |
2112 | if (op1->OperGet() == GT_LONG) |
2113 | { |
2114 | GenTree* loVal = op1->gtGetOp1(); |
2115 | GenTree* hiVal = op1->gtGetOp2(); |
2116 | |
2117 | noway_assert((loVal->gtRegNum != REG_NA) && (hiVal->gtRegNum != REG_NA)); |
2118 | |
2119 | emit->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, loVal->gtRegNum, lclNum, 0); |
2120 | emit->emitIns_S_R(ins_Store(TYP_INT), EA_4BYTE, hiVal->gtRegNum, lclNum, genTypeSize(TYP_INT)); |
2121 | } |
2122 | else |
2123 | { |
2124 | assert((op1->gtSkipReloadOrCopy()->gtFlags & GTF_MUL_64RSLT) != 0); |
2125 | // This is either a multi-reg MUL_LONG, or a multi-reg reload or copy. |
2126 | assert(op1->IsMultiRegNode() && (op1->GetMultiRegCount() == 2)); |
2127 | |
2128 | // Stack store |
2129 | emit->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), op1->GetRegByIndex(0), lclNum, 0); |
2130 | emit->emitIns_S_R(ins_Store(TYP_INT), emitTypeSize(TYP_INT), op1->GetRegByIndex(1), lclNum, |
2131 | genTypeSize(TYP_INT)); |
2132 | } |
2133 | } |
2134 | #endif // !defined(_TARGET_64BIT_) |
2135 | |