| 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 | |