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.
5 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
10 XX The variables to be used by the code generator. XX
12 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
13 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
22 #include "register_arg_convention.h"
24 /*****************************************************************************/
29 unsigned Compiler::s_lvaDoubleAlignedProcsCount = 0;
33 /*****************************************************************************/
35 void Compiler::lvaInit()
37 /* We haven't allocated stack variables yet */
38 lvaRefCountingStarted = false;
39 lvaLocalVarRefCounted = false;
41 lvaGenericsContextUseCount = 0;
43 lvaSortAgain = false; // false: We don't need to call lvaSortOnly()
44 lvaTrackedFixed = false; // false: We can still add new tracked variables
46 lvaDoneFrameLayout = NO_FRAME_LAYOUT;
47 #if !FEATURE_EH_FUNCLETS
48 lvaShadowSPslotsVar = BAD_VAR_NUM;
49 #endif // !FEATURE_EH_FUNCLETS
50 lvaInlinedPInvokeFrameVar = BAD_VAR_NUM;
51 lvaReversePInvokeFrameVar = BAD_VAR_NUM;
52 #if FEATURE_FIXED_OUT_ARGS
53 lvaPInvokeFrameRegSaveVar = BAD_VAR_NUM;
54 lvaOutgoingArgSpaceVar = BAD_VAR_NUM;
55 lvaOutgoingArgSpaceSize = PhasedVar<unsigned>();
56 #endif // FEATURE_FIXED_OUT_ARGS
58 lvaPromotedStructAssemblyScratchVar = BAD_VAR_NUM;
59 #endif // _TARGET_ARM_
60 lvaLocAllocSPvar = BAD_VAR_NUM;
61 lvaNewObjArrayArgs = BAD_VAR_NUM;
62 lvaGSSecurityCookie = BAD_VAR_NUM;
64 lvaVarargsBaseOfStkArgs = BAD_VAR_NUM;
65 #endif // _TARGET_X86_
66 lvaVarargsHandleArg = BAD_VAR_NUM;
67 lvaSecurityObject = BAD_VAR_NUM;
68 lvaStubArgumentVar = BAD_VAR_NUM;
69 lvaArg0Var = BAD_VAR_NUM;
70 lvaMonAcquired = BAD_VAR_NUM;
72 lvaInlineeReturnSpillTemp = BAD_VAR_NUM;
74 gsShadowVarInfo = nullptr;
75 #if FEATURE_EH_FUNCLETS
76 lvaPSPSym = BAD_VAR_NUM;
79 lvaSIMDInitTempVarNum = BAD_VAR_NUM;
80 #endif // FEATURE_SIMD
82 #ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
83 lvaFirstStackIncomingArgNum = BAD_VAR_NUM;
84 #endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING
87 /*****************************************************************************/
89 void Compiler::lvaInitTypeRef()
92 /* x86 args look something like this:
93 [this ptr] [hidden return buffer] [declared arguments]* [generic context] [var arg cookie]
95 x64 is closer to the native ABI:
96 [this ptr] [hidden return buffer] [generic context] [var arg cookie] [declared arguments]*
97 (Note: prior to .NET Framework 4.5.1 for Windows 8.1 (but not .NET Framework 4.5.1 "downlevel"),
98 the "hidden return buffer" came before the "this ptr". Now, the "this ptr" comes first. This
99 is different from the C++ order, where the "hidden return buffer" always comes first.)
101 ARM and ARM64 are the same as the current x64 convention:
102 [this ptr] [hidden return buffer] [generic context] [var arg cookie] [declared arguments]*
105 The var arg cookie and generic context are swapped with respect to the user arguments
108 /* Set compArgsCount and compLocalsCount */
110 info.compArgsCount = info.compMethodInfo->args.numArgs;
112 // Is there a 'this' pointer
114 if (!info.compIsStatic)
116 info.compArgsCount++;
120 info.compThisArg = BAD_VAR_NUM;
123 info.compILargsCount = info.compArgsCount;
126 if (featureSIMD && (info.compRetNativeType == TYP_STRUCT))
128 var_types structType = impNormStructType(info.compMethodInfo->args.retTypeClass);
129 info.compRetType = structType;
131 #endif // FEATURE_SIMD
133 // Are we returning a struct using a return buffer argument?
135 const bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo);
137 // Possibly change the compRetNativeType from TYP_STRUCT to a "primitive" type
138 // when we are returning a struct by value and it fits in one register
140 if (!hasRetBuffArg && varTypeIsStruct(info.compRetNativeType))
142 CORINFO_CLASS_HANDLE retClsHnd = info.compMethodInfo->args.retTypeClass;
144 Compiler::structPassingKind howToReturnStruct;
145 var_types returnType = getReturnTypeForStruct(retClsHnd, &howToReturnStruct);
147 if (howToReturnStruct == SPK_PrimitiveType)
149 assert(returnType != TYP_UNKNOWN);
150 assert(!varTypeIsStruct(returnType));
152 info.compRetNativeType = returnType;
154 // ToDo: Refactor this common code sequence into its own method as it is used 4+ times
155 if ((returnType == TYP_LONG) && (compLongUsed == false))
159 else if (((returnType == TYP_FLOAT) || (returnType == TYP_DOUBLE)) && (compFloatingPointUsed == false))
161 compFloatingPointUsed = true;
166 // Do we have a RetBuffArg?
170 info.compArgsCount++;
174 info.compRetBuffArg = BAD_VAR_NUM;
177 /* There is a 'hidden' cookie pushed last when the
178 calling convention is varargs */
180 if (info.compIsVarArgs)
182 info.compArgsCount++;
185 // Is there an extra parameter used to pass instantiation info to
186 // shared generic methods and shared generic struct instance methods?
187 if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
189 info.compArgsCount++;
193 info.compTypeCtxtArg = BAD_VAR_NUM;
196 lvaCount = info.compLocalsCount = info.compArgsCount + info.compMethodInfo->locals.numArgs;
198 info.compILlocalsCount = info.compILargsCount + info.compMethodInfo->locals.numArgs;
200 /* Now allocate the variable descriptor table */
202 if (compIsForInlining())
204 lvaTable = impInlineInfo->InlinerCompiler->lvaTable;
205 lvaCount = impInlineInfo->InlinerCompiler->lvaCount;
206 lvaTableCnt = impInlineInfo->InlinerCompiler->lvaTableCnt;
208 // No more stuff needs to be done.
212 lvaTableCnt = lvaCount * 2;
214 if (lvaTableCnt < 16)
219 lvaTable = (LclVarDsc*)compGetMemArray(lvaTableCnt, sizeof(*lvaTable), CMK_LvaTable);
220 size_t tableSize = lvaTableCnt * sizeof(*lvaTable);
221 memset(lvaTable, 0, tableSize);
222 for (unsigned i = 0; i < lvaTableCnt; i++)
224 new (&lvaTable[i], jitstd::placement_t()) LclVarDsc(this); // call the constructor.
227 //-------------------------------------------------------------------------
228 // Count the arguments and initialize the respective lvaTable[] entries
230 // First the implicit arguments
231 //-------------------------------------------------------------------------
233 InitVarDscInfo varDscInfo;
234 varDscInfo.Init(lvaTable, hasRetBuffArg);
236 lvaInitArgs(&varDscInfo);
238 //-------------------------------------------------------------------------
239 // Finally the local variables
240 //-------------------------------------------------------------------------
242 unsigned varNum = varDscInfo.varNum;
243 LclVarDsc* varDsc = varDscInfo.varDsc;
244 CORINFO_ARG_LIST_HANDLE localsSig = info.compMethodInfo->locals.args;
246 for (unsigned i = 0; i < info.compMethodInfo->locals.numArgs;
247 i++, varNum++, varDsc++, localsSig = info.compCompHnd->getArgNext(localsSig))
249 CORINFO_CLASS_HANDLE typeHnd;
250 CorInfoTypeWithMod corInfoTypeWithMod =
251 info.compCompHnd->getArgType(&info.compMethodInfo->locals, localsSig, &typeHnd);
252 CorInfoType corInfoType = strip(corInfoTypeWithMod);
254 lvaInitVarDsc(varDsc, varNum, corInfoType, typeHnd, localsSig, &info.compMethodInfo->locals);
256 varDsc->lvPinned = ((corInfoTypeWithMod & CORINFO_TYPE_MOD_PINNED) != 0);
257 varDsc->lvOnFrame = true; // The final home for this local variable might be our local stack frame
259 if (corInfoType == CORINFO_TYPE_CLASS)
261 CORINFO_CLASS_HANDLE clsHnd = info.compCompHnd->getArgClass(&info.compMethodInfo->locals, localsSig);
262 lvaSetClass(varNum, clsHnd);
266 if ( // If there already exist unsafe buffers, don't mark more structs as unsafe
267 // as that will cause them to be placed along with the real unsafe buffers,
268 // unnecessarily exposing them to overruns. This can affect GS tests which
269 // intentionally do buffer-overruns.
270 !getNeedsGSSecurityCookie() &&
271 // GS checks require the stack to be re-ordered, which can't be done with EnC
272 !opts.compDbgEnC && compStressCompile(STRESS_UNSAFE_BUFFER_CHECKS, 25))
274 setNeedsGSSecurityCookie();
275 compGSReorderStackLayout = true;
277 for (unsigned i = 0; i < lvaCount; i++)
279 if ((lvaTable[i].lvType == TYP_STRUCT) && compStressCompile(STRESS_GENERIC_VARN, 60))
281 lvaTable[i].lvIsUnsafeBuffer = true;
286 if (getNeedsGSSecurityCookie())
288 // Ensure that there will be at least one stack variable since
289 // we require that the GSCookie does not have a 0 stack offset.
290 unsigned dummy = lvaGrabTempWithImplicitUse(false DEBUGARG("GSCookie dummy"));
291 lvaTable[dummy].lvType = TYP_INT;
297 lvaTableDump(INITIAL_FRAME_LAYOUT);
302 /*****************************************************************************/
303 void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo)
307 #if defined(_TARGET_ARM_) && defined(PROFILING_SUPPORTED)
308 // Prespill all argument regs on to stack in case of Arm when under profiler.
309 if (compIsProfilerHookNeeded())
311 codeGen->regSet.rsMaskPreSpillRegArg |= RBM_ARG_REGS;
315 //----------------------------------------------------------------------
317 /* Is there a "this" pointer ? */
318 lvaInitThisPtr(varDscInfo);
320 /* If we have a hidden return-buffer parameter, that comes here */
321 lvaInitRetBuffArg(varDscInfo);
323 //======================================================================
325 #if USER_ARGS_COME_LAST
326 //@GENERICS: final instantiation-info argument for shared generic methods
327 // and shared generic struct instance methods
328 lvaInitGenericsCtxt(varDscInfo);
330 /* If the method is varargs, process the varargs cookie */
331 lvaInitVarArgsHandle(varDscInfo);
334 //-------------------------------------------------------------------------
335 // Now walk the function signature for the explicit user arguments
336 //-------------------------------------------------------------------------
337 lvaInitUserArgs(varDscInfo);
339 #if !USER_ARGS_COME_LAST
340 //@GENERICS: final instantiation-info argument for shared generic methods
341 // and shared generic struct instance methods
342 lvaInitGenericsCtxt(varDscInfo);
344 /* If the method is varargs, process the varargs cookie */
345 lvaInitVarArgsHandle(varDscInfo);
348 //----------------------------------------------------------------------
350 // We have set info.compArgsCount in compCompile()
351 noway_assert(varDscInfo->varNum == info.compArgsCount);
352 assert(varDscInfo->intRegArgNum <= MAX_REG_ARG);
354 codeGen->intRegState.rsCalleeRegArgCount = varDscInfo->intRegArgNum;
355 #if !FEATURE_STACK_FP_X87
356 codeGen->floatRegState.rsCalleeRegArgCount = varDscInfo->floatRegArgNum;
357 #endif // FEATURE_STACK_FP_X87
359 #if FEATURE_FASTTAILCALL
360 // Save the stack usage information
361 // We can get register usage information using codeGen->intRegState and
362 // codeGen->floatRegState
363 info.compArgStackSize = varDscInfo->stackArgSize;
364 #endif // FEATURE_FASTTAILCALL
366 // The total argument size must be aligned.
367 noway_assert((compArgSize % TARGET_POINTER_SIZE) == 0);
370 /* We can not pass more than 2^16 dwords as arguments as the "ret"
371 instruction can only pop 2^16 arguments. Could be handled correctly
372 but it will be very difficult for fully interruptible code */
374 if (compArgSize != (size_t)(unsigned short)compArgSize)
375 NO_WAY("Too many arguments for the \"ret\" instruction to pop");
379 /*****************************************************************************/
380 void Compiler::lvaInitThisPtr(InitVarDscInfo* varDscInfo)
382 LclVarDsc* varDsc = varDscInfo->varDsc;
383 if (!info.compIsStatic)
385 varDsc->lvIsParam = 1;
387 varDsc->lvSingleDef = 1;
392 lvaArg0Var = info.compThisArg = varDscInfo->varNum;
393 noway_assert(info.compThisArg == 0);
395 if (eeIsValueClass(info.compClassHnd))
397 varDsc->lvType = TYP_BYREF;
401 var_types simdBaseType = TYP_UNKNOWN;
402 var_types type = impNormStructType(info.compClassHnd, nullptr, nullptr, &simdBaseType);
403 if (simdBaseType != TYP_UNKNOWN)
405 assert(varTypeIsSIMD(type));
406 varDsc->lvSIMDType = true;
407 varDsc->lvBaseType = simdBaseType;
408 varDsc->lvExactSize = genTypeSize(type);
411 #endif // FEATURE_SIMD
415 varDsc->lvType = TYP_REF;
416 lvaSetClass(varDscInfo->varNum, info.compClassHnd);
419 if (tiVerificationNeeded)
421 varDsc->lvVerTypeInfo = verMakeTypeInfo(info.compClassHnd);
423 if (varDsc->lvVerTypeInfo.IsValueClass())
425 varDsc->lvVerTypeInfo.MakeByRef();
430 varDsc->lvVerTypeInfo = typeInfo();
433 // Mark the 'this' pointer for the method
434 varDsc->lvVerTypeInfo.SetIsThisPtr();
436 varDsc->lvIsRegArg = 1;
437 noway_assert(varDscInfo->intRegArgNum == 0);
439 varDsc->lvArgReg = genMapRegArgNumToRegNum(varDscInfo->allocRegArg(TYP_INT), varDsc->TypeGet());
440 #if FEATURE_MULTIREG_ARGS
441 varDsc->lvOtherArgReg = REG_NA;
443 varDsc->setPrefReg(varDsc->lvArgReg, this);
444 varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
449 printf("'this' passed in register %s\n", getRegName(varDsc->lvArgReg));
452 compArgSize += TARGET_POINTER_SIZE;
454 varDscInfo->varNum++;
455 varDscInfo->varDsc++;
459 /*****************************************************************************/
460 void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo)
462 LclVarDsc* varDsc = varDscInfo->varDsc;
463 bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo);
465 // These two should always match
466 noway_assert(hasRetBuffArg == varDscInfo->hasRetBufArg);
470 info.compRetBuffArg = varDscInfo->varNum;
471 varDsc->lvType = TYP_BYREF;
472 varDsc->lvIsParam = 1;
473 varDsc->lvIsRegArg = 1;
475 varDsc->lvSingleDef = 1;
477 if (hasFixedRetBuffReg())
479 varDsc->lvArgReg = theFixedRetBuffReg();
483 unsigned retBuffArgNum = varDscInfo->allocRegArg(TYP_INT);
484 varDsc->lvArgReg = genMapIntRegArgNumToRegNum(retBuffArgNum);
487 #if FEATURE_MULTIREG_ARGS
488 varDsc->lvOtherArgReg = REG_NA;
490 varDsc->setPrefReg(varDsc->lvArgReg, this);
491 varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
493 info.compRetBuffDefStack = 0;
494 if (info.compRetType == TYP_STRUCT)
496 CORINFO_SIG_INFO sigInfo;
497 info.compCompHnd->getMethodSig(info.compMethodHnd, &sigInfo);
498 assert(JITtype2varType(sigInfo.retType) == info.compRetType); // Else shouldn't have a ret buff.
500 info.compRetBuffDefStack =
501 (info.compCompHnd->isStructRequiringStackAllocRetBuf(sigInfo.retTypeClass) == TRUE);
502 if (info.compRetBuffDefStack)
504 // If we're assured that the ret buff argument points into a callers stack, we will type it as
506 // (native int/unmanaged pointer) so that it's not tracked as a GC ref.
507 varDsc->lvType = TYP_I_IMPL;
511 else if (featureSIMD && varTypeIsSIMD(info.compRetType))
513 varDsc->lvSIMDType = true;
515 getBaseTypeAndSizeOfSIMDType(info.compMethodInfo->args.retTypeClass, &varDsc->lvExactSize);
516 assert(varDsc->lvBaseType != TYP_UNKNOWN);
518 #endif // FEATURE_SIMD
520 assert(isValidIntArgReg(varDsc->lvArgReg));
525 printf("'__retBuf' passed in register %s\n", getRegName(varDsc->lvArgReg));
529 /* Update the total argument size, count and varDsc */
531 compArgSize += TARGET_POINTER_SIZE;
532 varDscInfo->varNum++;
533 varDscInfo->varDsc++;
537 /*****************************************************************************/
538 void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo)
540 //-------------------------------------------------------------------------
541 // Walk the function signature for the explicit arguments
542 //-------------------------------------------------------------------------
544 #if defined(_TARGET_X86_)
545 // Only (some of) the implicit args are enregistered for varargs
546 varDscInfo->maxIntRegArgNum = info.compIsVarArgs ? varDscInfo->intRegArgNum : MAX_REG_ARG;
547 #elif defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
548 // On System V type environment the float registers are not indexed together with the int ones.
549 varDscInfo->floatRegArgNum = varDscInfo->intRegArgNum;
552 CORINFO_ARG_LIST_HANDLE argLst = info.compMethodInfo->args.args;
554 const unsigned argSigLen = info.compMethodInfo->args.numArgs;
556 regMaskTP doubleAlignMask = RBM_NONE;
557 for (unsigned i = 0; i < argSigLen;
558 i++, varDscInfo->varNum++, varDscInfo->varDsc++, argLst = info.compCompHnd->getArgNext(argLst))
560 LclVarDsc* varDsc = varDscInfo->varDsc;
561 CORINFO_CLASS_HANDLE typeHnd = nullptr;
563 CorInfoTypeWithMod corInfoType = info.compCompHnd->getArgType(&info.compMethodInfo->args, argLst, &typeHnd);
564 varDsc->lvIsParam = 1;
566 varDsc->lvSingleDef = 1;
569 lvaInitVarDsc(varDsc, varDscInfo->varNum, strip(corInfoType), typeHnd, argLst, &info.compMethodInfo->args);
571 if (strip(corInfoType) == CORINFO_TYPE_CLASS)
573 CORINFO_CLASS_HANDLE clsHnd = info.compCompHnd->getArgClass(&info.compMethodInfo->args, argLst);
574 lvaSetClass(varDscInfo->varNum, clsHnd);
577 // For ARM, ARM64, and AMD64 varargs, all arguments go in integer registers
578 var_types argType = mangleVarArgsType(varDsc->TypeGet());
579 var_types origArgType = argType;
580 // ARM softfp calling convention should affect only the floating point arguments.
581 // Otherwise there appear too many surplus pre-spills and other memory operations
582 // with the associated locations .
583 bool isSoftFPPreSpill = opts.compUseSoftFP && varTypeIsFloating(varDsc->TypeGet());
584 unsigned argSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
585 unsigned cSlots = argSize / TARGET_POINTER_SIZE; // the total number of slots of this argument
586 bool isHfaArg = false;
587 var_types hfaType = TYP_UNDEF;
589 // Methods that use VarArg or SoftFP cannot have HFA arguments
590 if (!info.compIsVarArgs && !opts.compUseSoftFP)
592 // If the argType is a struct, then check if it is an HFA
593 if (varTypeIsStruct(argType))
595 hfaType = GetHfaType(typeHnd); // set to float or double if it is an HFA, otherwise TYP_UNDEF
596 isHfaArg = varTypeIsFloating(hfaType);
601 // We have an HFA argument, so from here on out treat the type as a float or double.
602 // The orginal struct type is available by using origArgType
603 // We also update the cSlots to be the number of float/double fields in the HFA
605 cSlots = varDsc->lvHfaSlots();
607 // The number of slots that must be enregistered if we are to consider this argument enregistered.
608 // This is normally the same as cSlots, since we normally either enregister the entire object,
609 // or none of it. For structs on ARM, however, we only need to enregister a single slot to consider
610 // it enregistered, as long as we can split the rest onto the stack.
611 unsigned cSlotsToEnregister = cSlots;
614 // On ARM we pass the first 4 words of integer arguments and non-HFA structs in registers.
615 // But we pre-spill user arguments in varargs methods and structs.
618 bool preSpill = info.compIsVarArgs || isSoftFPPreSpill;
623 assert(varDsc->lvSize() == argSize);
624 cAlign = varDsc->lvStructDoubleAlign ? 2 : 1;
626 // HFA arguments go on the stack frame. They don't get spilled in the prolog like struct
627 // arguments passed in the integer registers but get homed immediately after the prolog.
630 cSlotsToEnregister = 1; // HFAs must be totally enregistered or not, but other structs can be split.
645 if (isRegParamType(argType))
647 compArgSize += varDscInfo->alignReg(argType, cAlign) * REGSIZE_BYTES;
650 if (argType == TYP_STRUCT)
652 // Are we going to split the struct between registers and stack? We can do that as long as
653 // no floating-point arguments have been put on the stack.
655 // From the ARM Procedure Call Standard:
656 // Rule C.5: "If the NCRN is less than r4 **and** the NSAA is equal to the SP,"
657 // then split the argument between registers and stack. Implication: if something
658 // has already been spilled to the stack, then anything that would normally be
659 // split between the core registers and the stack will be put on the stack.
660 // Anything that follows will also be on the stack. However, if something from
661 // floating point regs has been spilled to the stack, we can still use r0-r3 until they are full.
663 if (varDscInfo->canEnreg(TYP_INT, 1) && // The beginning of the struct can go in a register
664 !varDscInfo->canEnreg(TYP_INT, cSlots) && // The end of the struct can't fit in a register
665 varDscInfo->existAnyFloatStackArgs()) // There's at least one stack-based FP arg already
667 varDscInfo->setAllRegArgUsed(TYP_INT); // Prevent all future use of integer registers
668 preSpill = false; // This struct won't be prespilled, since it will go on the stack
674 for (unsigned ix = 0; ix < cSlots; ix++)
676 if (!varDscInfo->canEnreg(TYP_INT, ix + 1))
680 regMaskTP regMask = genMapArgNumToRegMask(varDscInfo->regArgNum(TYP_INT) + ix, TYP_INT);
683 doubleAlignMask |= regMask;
685 codeGen->regSet.rsMaskPreSpillRegArg |= regMask;
688 #else // !_TARGET_ARM_
689 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
690 SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
691 if (varTypeIsStruct(argType))
693 assert(typeHnd != nullptr);
694 eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc);
695 if (structDesc.passedInRegisters)
697 unsigned intRegCount = 0;
698 unsigned floatRegCount = 0;
700 for (unsigned int i = 0; i < structDesc.eightByteCount; i++)
702 if (structDesc.IsIntegralSlot(i))
706 else if (structDesc.IsSseSlot(i))
712 assert(false && "Invalid eightbyte classification type.");
717 if (intRegCount != 0 && !varDscInfo->canEnreg(TYP_INT, intRegCount))
719 structDesc.passedInRegisters = false; // No register to enregister the eightbytes.
722 if (floatRegCount != 0 && !varDscInfo->canEnreg(TYP_FLOAT, floatRegCount))
724 structDesc.passedInRegisters = false; // No register to enregister the eightbytes.
728 #endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
729 #endif // !_TARGET_ARM_
731 // The final home for this incoming register might be our local stack frame.
732 // For System V platforms the final home will always be on the local stack frame.
733 varDsc->lvOnFrame = true;
735 bool canPassArgInRegisters = false;
737 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
738 if (varTypeIsStruct(argType))
740 canPassArgInRegisters = structDesc.passedInRegisters;
743 #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
745 canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister);
748 if (canPassArgInRegisters)
750 /* Another register argument */
752 // Allocate the registers we need. allocRegArg() returns the first argument register number of the set.
753 // For non-HFA structs, we still "try" to enregister the whole thing; it will just max out if splitting
754 // to the stack happens.
755 unsigned firstAllocatedRegArgNum = 0;
757 #if FEATURE_MULTIREG_ARGS
758 varDsc->lvOtherArgReg = REG_NA;
759 #endif // FEATURE_MULTIREG_ARGS
761 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
762 unsigned secondAllocatedRegArgNum = 0;
763 var_types firstEightByteType = TYP_UNDEF;
764 var_types secondEightByteType = TYP_UNDEF;
766 if (varTypeIsStruct(argType))
768 if (structDesc.eightByteCount >= 1)
770 firstEightByteType = GetEightByteType(structDesc, 0);
771 firstAllocatedRegArgNum = varDscInfo->allocRegArg(firstEightByteType, 1);
775 #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
777 firstAllocatedRegArgNum = varDscInfo->allocRegArg(argType, cSlots);
782 // We need to save the fact that this HFA is enregistered
783 varDsc->lvSetIsHfa();
784 varDsc->lvSetIsHfaRegArg();
785 varDsc->SetHfaType(hfaType);
786 varDsc->lvIsMultiRegArg = (varDsc->lvHfaSlots() > 1);
789 varDsc->lvIsRegArg = 1;
791 #if FEATURE_MULTIREG_ARGS
792 if (varTypeIsStruct(argType))
794 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
795 varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, firstEightByteType);
797 // If there is a second eightbyte, get a register for it too and map the arg to the reg number.
798 if (structDesc.eightByteCount >= 2)
800 secondEightByteType = GetEightByteType(structDesc, 1);
801 secondAllocatedRegArgNum = varDscInfo->allocRegArg(secondEightByteType, 1);
804 if (secondEightByteType != TYP_UNDEF)
806 varDsc->lvOtherArgReg = genMapRegArgNumToRegNum(secondAllocatedRegArgNum, secondEightByteType);
807 varDsc->addPrefReg(genRegMask(varDsc->lvOtherArgReg), this);
809 #else // ARM32 or ARM64
810 varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, TYP_I_IMPL);
811 #ifdef _TARGET_ARM64_
814 varDsc->lvOtherArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum + 1, TYP_I_IMPL);
815 varDsc->addPrefReg(genRegMask(varDsc->lvOtherArgReg), this);
817 #endif // _TARGET_ARM64_
818 #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
821 #endif // FEATURE_MULTIREG_ARGS
823 varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argType);
826 varDsc->setPrefReg(varDsc->lvArgReg, this);
829 if (varDsc->TypeGet() == TYP_LONG)
831 varDsc->lvOtherReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum + 1, TYP_INT);
832 varDsc->addPrefReg(genRegMask(varDsc->lvOtherReg), this);
834 #endif // _TARGET_ARM_
839 printf("Arg #%u passed in register(s) ", varDscInfo->varNum);
840 bool isFloat = false;
841 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
842 // In case of one eightbyte struct the type is already normalized earlier.
843 // The varTypeIsFloating(argType) is good for this case.
844 if (varTypeIsStruct(argType) && (structDesc.eightByteCount >= 1))
846 isFloat = varTypeIsFloating(firstEightByteType);
849 #else // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
851 isFloat = varTypeIsFloating(argType);
853 #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
855 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
856 if (varTypeIsStruct(argType))
858 // Print both registers, just to be clear
859 if (firstEightByteType == TYP_UNDEF)
861 printf("firstEightByte: <not used>");
865 printf("firstEightByte: %s",
866 getRegName(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, firstEightByteType),
870 if (secondEightByteType == TYP_UNDEF)
872 printf(", secondEightByte: <not used>");
876 printf(", secondEightByte: %s",
877 getRegName(genMapRegArgNumToRegNum(secondAllocatedRegArgNum, secondEightByteType),
878 varTypeIsFloating(secondEightByteType)));
882 #endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
884 unsigned regArgNum = genMapRegNumToRegArgNum(varDsc->lvArgReg, argType);
886 for (unsigned ix = 0; ix < cSlots; ix++, regArgNum++)
893 if (!isFloat && (regArgNum >= varDscInfo->maxIntRegArgNum)) // a struct has been split between
894 // registers and stack
896 printf(" stack slots:%d", cSlots - ix);
903 // Print register size prefix
904 if (argType == TYP_DOUBLE)
906 // Print both registers, just to be clear
907 printf("%s/%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat),
908 getRegName(genMapRegArgNumToRegNum(regArgNum + 1, argType), isFloat));
910 // doubles take 2 slots
911 assert(ix + 1 < cSlots);
917 printf("%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat));
921 #endif // _TARGET_ARM_
923 printf("%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat));
930 } // end if (canPassArgInRegisters)
933 #if defined(_TARGET_ARM_)
934 varDscInfo->setAllRegArgUsed(argType);
935 if (varTypeIsFloating(argType))
937 varDscInfo->setAnyFloatStackArgs();
940 #elif defined(_TARGET_ARM64_)
942 // If we needed to use the stack in order to pass this argument then
943 // record the fact that we have used up any remaining registers of this 'type'
944 // This prevents any 'backfilling' from occuring on ARM64
946 varDscInfo->setAllRegArgUsed(argType);
948 #endif // _TARGET_XXX_
950 #if FEATURE_FASTTAILCALL
951 varDscInfo->stackArgSize += (unsigned)roundUp(argSize, TARGET_POINTER_SIZE);
952 #endif // FEATURE_FASTTAILCALL
955 #ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
956 // The arg size is returning the number of bytes of the argument. For a struct it could return a size not a
957 // multiple of TARGET_POINTER_SIZE. The stack allocated space should always be multiple of TARGET_POINTER_SIZE,
959 compArgSize += (unsigned)roundUp(argSize, TARGET_POINTER_SIZE);
960 #else // !FEATURE_UNIX_AMD64_STRUCT_PASSING
961 compArgSize += argSize;
962 #endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING
963 if (info.compIsVarArgs || isHfaArg || isSoftFPPreSpill)
965 #if defined(_TARGET_X86_)
966 varDsc->lvStkOffs = compArgSize;
967 #else // !_TARGET_X86_
968 // TODO-CQ: We shouldn't have to go as far as to declare these
969 // address-exposed -- DoNotEnregister should suffice.
970 lvaSetVarAddrExposed(varDscInfo->varNum);
971 #endif // !_TARGET_X86_
973 } // for each user arg
976 if (doubleAlignMask != RBM_NONE)
978 assert(RBM_ARG_REGS == 0xF);
979 assert((doubleAlignMask & RBM_ARG_REGS) == doubleAlignMask);
980 if (doubleAlignMask != RBM_NONE && doubleAlignMask != RBM_ARG_REGS)
982 // doubleAlignMask can only be 0011 and/or 1100 as 'double aligned types' can
983 // begin at r0 or r2.
984 assert(doubleAlignMask == 0x3 || doubleAlignMask == 0xC /* || 0xF is if'ed out */);
986 // Now if doubleAlignMask is 0011 i.e., {r0,r1} and we prespill r2 or r3
987 // but not both, then the stack would be misaligned for r0. So spill both
990 // ; +0 --- caller SP double aligned ----
993 // ; -c r0 r0 <-- misaligned.
994 // ; callee saved regs
995 if (doubleAlignMask == 0x3 && doubleAlignMask != codeGen->regSet.rsMaskPreSpillRegArg)
997 codeGen->regSet.rsMaskPreSpillAlign =
998 (~codeGen->regSet.rsMaskPreSpillRegArg & ~doubleAlignMask) & RBM_ARG_REGS;
1002 #endif // _TARGET_ARM_
1005 /*****************************************************************************/
1006 void Compiler::lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo)
1008 //@GENERICS: final instantiation-info argument for shared generic methods
1009 // and shared generic struct instance methods
1010 if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
1012 info.compTypeCtxtArg = varDscInfo->varNum;
1014 LclVarDsc* varDsc = varDscInfo->varDsc;
1015 varDsc->lvIsParam = 1;
1017 varDsc->lvSingleDef = 1;
1020 varDsc->lvType = TYP_I_IMPL;
1022 if (varDscInfo->canEnreg(TYP_I_IMPL))
1024 /* Another register argument */
1026 varDsc->lvIsRegArg = 1;
1027 varDsc->lvArgReg = genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet());
1028 #if FEATURE_MULTIREG_ARGS
1029 varDsc->lvOtherArgReg = REG_NA;
1031 varDsc->setPrefReg(varDsc->lvArgReg, this);
1032 varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
1034 varDscInfo->intRegArgNum++;
1039 printf("'GenCtxt' passed in register %s\n", getRegName(varDsc->lvArgReg));
1043 #ifndef LEGACY_BACKEND
1046 // For the RyuJIT backend, we need to mark these as being on the stack,
1047 // as this is not done elsewhere in the case that canEnreg returns false.
1048 varDsc->lvOnFrame = true;
1049 #if FEATURE_FASTTAILCALL
1050 varDscInfo->stackArgSize += TARGET_POINTER_SIZE;
1051 #endif // FEATURE_FASTTAILCALL
1053 #endif // !LEGACY_BACKEND
1055 compArgSize += TARGET_POINTER_SIZE;
1057 #if defined(_TARGET_X86_)
1058 if (info.compIsVarArgs)
1059 varDsc->lvStkOffs = compArgSize;
1060 #endif // _TARGET_X86_
1062 varDscInfo->varNum++;
1063 varDscInfo->varDsc++;
1067 /*****************************************************************************/
1068 void Compiler::lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo)
1070 if (info.compIsVarArgs)
1072 lvaVarargsHandleArg = varDscInfo->varNum;
1074 LclVarDsc* varDsc = varDscInfo->varDsc;
1075 varDsc->lvType = TYP_I_IMPL;
1076 varDsc->lvIsParam = 1;
1077 // Make sure this lives in the stack -- address may be reported to the VM.
1078 // TODO-CQ: This should probably be:
1079 // lvaSetVarDoNotEnregister(varDscInfo->varNum DEBUGARG(DNER_VMNeedsStackAddr));
1080 // But that causes problems, so, for expedience, I switched back to this heavyweight
1081 // hammer. But I think it should be possible to switch; it may just work now
1082 // that other problems are fixed.
1083 lvaSetVarAddrExposed(varDscInfo->varNum);
1086 varDsc->lvSingleDef = 1;
1089 if (varDscInfo->canEnreg(TYP_I_IMPL))
1091 /* Another register argument */
1093 unsigned varArgHndArgNum = varDscInfo->allocRegArg(TYP_I_IMPL);
1095 varDsc->lvIsRegArg = 1;
1096 varDsc->lvArgReg = genMapRegArgNumToRegNum(varArgHndArgNum, TYP_I_IMPL);
1097 #if FEATURE_MULTIREG_ARGS
1098 varDsc->lvOtherArgReg = REG_NA;
1100 varDsc->setPrefReg(varDsc->lvArgReg, this);
1101 varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
1103 // This has to be spilled right in front of the real arguments and we have
1104 // to pre-spill all the argument registers explicitly because we only have
1105 // have symbols for the declared ones, not any potential variadic ones.
1106 for (unsigned ix = varArgHndArgNum; ix < ArrLen(intArgMasks); ix++)
1108 codeGen->regSet.rsMaskPreSpillRegArg |= intArgMasks[ix];
1110 #endif // _TARGET_ARM_
1115 printf("'VarArgHnd' passed in register %s\n", getRegName(varDsc->lvArgReg));
1119 #ifndef LEGACY_BACKEND
1122 // For the RyuJIT backend, we need to mark these as being on the stack,
1123 // as this is not done elsewhere in the case that canEnreg returns false.
1124 varDsc->lvOnFrame = true;
1125 #if FEATURE_FASTTAILCALL
1126 varDscInfo->stackArgSize += TARGET_POINTER_SIZE;
1127 #endif // FEATURE_FASTTAILCALL
1129 #endif // !LEGACY_BACKEND
1131 /* Update the total argument size, count and varDsc */
1133 compArgSize += TARGET_POINTER_SIZE;
1135 varDscInfo->varNum++;
1136 varDscInfo->varDsc++;
1138 #if defined(_TARGET_X86_)
1139 varDsc->lvStkOffs = compArgSize;
1141 // Allocate a temp to point at the beginning of the args
1143 lvaVarargsBaseOfStkArgs = lvaGrabTemp(false DEBUGARG("Varargs BaseOfStkArgs"));
1144 lvaTable[lvaVarargsBaseOfStkArgs].lvType = TYP_I_IMPL;
1146 #endif // _TARGET_X86_
1150 /*****************************************************************************/
1151 void Compiler::lvaInitVarDsc(LclVarDsc* varDsc,
1153 CorInfoType corInfoType,
1154 CORINFO_CLASS_HANDLE typeHnd,
1155 CORINFO_ARG_LIST_HANDLE varList,
1156 CORINFO_SIG_INFO* varSig)
1158 noway_assert(varDsc == &lvaTable[varNum]);
1160 switch (corInfoType)
1162 // Mark types that looks like a pointer for doing shadow-copying of
1163 // parameters if we have an unsafe buffer.
1164 // Note that this does not handle structs with pointer fields. Instead,
1165 // we rely on using the assign-groups/equivalence-groups in
1166 // gsFindVulnerableParams() to determine if a buffer-struct contains a
1167 // pointer. We could do better by having the EE determine this for us.
1168 // Note that we want to keep buffers without pointers at lower memory
1169 // addresses than buffers with pointers.
1170 case CORINFO_TYPE_PTR:
1171 case CORINFO_TYPE_BYREF:
1172 case CORINFO_TYPE_CLASS:
1173 case CORINFO_TYPE_STRING:
1174 case CORINFO_TYPE_VAR:
1175 case CORINFO_TYPE_REFANY:
1176 varDsc->lvIsPtr = 1;
1182 var_types type = JITtype2varType(corInfoType);
1183 if (varTypeIsFloating(type))
1185 compFloatingPointUsed = true;
1188 if (tiVerificationNeeded)
1190 varDsc->lvVerTypeInfo = verParseArgSigToTypeInfo(varSig, varList);
1193 if (tiVerificationNeeded)
1195 if (varDsc->lvIsParam)
1197 // For an incoming ValueType we better be able to have the full type information
1198 // so that we can layout the parameter offsets correctly
1200 if (varTypeIsStruct(type) && varDsc->lvVerTypeInfo.IsDead())
1202 BADCODE("invalid ValueType parameter");
1205 // For an incoming reference type we need to verify that the actual type is
1206 // a reference type and not a valuetype.
1208 if (type == TYP_REF &&
1209 !(varDsc->lvVerTypeInfo.IsType(TI_REF) || varDsc->lvVerTypeInfo.IsUnboxedGenericTypeVar()))
1211 BADCODE("parameter type mismatch");
1215 // Disallow byrefs to byref like objects (ArgTypeHandle)
1216 // techncally we could get away with just not setting them
1217 if (varDsc->lvVerTypeInfo.IsByRef() && verIsByRefLike(DereferenceByRef(varDsc->lvVerTypeInfo)))
1219 varDsc->lvVerTypeInfo = typeInfo();
1222 // we don't want the EE to assert in lvaSetStruct on bad sigs, so change
1223 // the JIT type to avoid even trying to call back
1224 if (varTypeIsStruct(type) && varDsc->lvVerTypeInfo.IsDead())
1232 unsigned cFlags = info.compCompHnd->getClassAttribs(typeHnd);
1234 // We can get typeHnds for primitive types, these are value types which only contain
1235 // a primitive. We will need the typeHnd to distinguish them, so we store it here.
1236 if ((cFlags & CORINFO_FLG_VALUECLASS) && !varTypeIsStruct(type))
1238 if (tiVerificationNeeded == false)
1240 // printf("This is a struct that the JIT will treat as a primitive\n");
1241 varDsc->lvVerTypeInfo = verMakeTypeInfo(typeHnd);
1245 varDsc->lvOverlappingFields = StructHasOverlappingFields(cFlags);
1248 if (varTypeIsGC(type))
1250 varDsc->lvStructGcCount = 1;
1253 // Set the lvType (before this point it is TYP_UNDEF).
1254 if ((varTypeIsStruct(type)))
1256 lvaSetStruct(varNum, typeHnd, typeHnd != nullptr, !tiVerificationNeeded);
1260 varDsc->lvType = type;
1264 if (type == TYP_BOOL)
1266 varDsc->lvIsBoolean = true;
1271 varDsc->lvStkOffs = BAD_STK_OFFS;
1274 #if FEATURE_MULTIREG_ARGS
1275 varDsc->lvOtherArgReg = REG_NA;
1276 #endif // FEATURE_MULTIREG_ARGS
1279 /*****************************************************************************
1280 * Returns our internal varNum for a given IL variable.
1281 * Asserts assume it is called after lvaTable[] has been set up.
1284 unsigned Compiler::compMapILvarNum(unsigned ILvarNum)
1286 noway_assert(ILvarNum < info.compILlocalsCount || ILvarNum > unsigned(ICorDebugInfo::UNKNOWN_ILNUM));
1290 if (ILvarNum == (unsigned)ICorDebugInfo::VARARGS_HND_ILNUM)
1292 // The varargs cookie is the last argument in lvaTable[]
1293 noway_assert(info.compIsVarArgs);
1295 varNum = lvaVarargsHandleArg;
1296 noway_assert(lvaTable[varNum].lvIsParam);
1298 else if (ILvarNum == (unsigned)ICorDebugInfo::RETBUF_ILNUM)
1300 noway_assert(info.compRetBuffArg != BAD_VAR_NUM);
1301 varNum = info.compRetBuffArg;
1303 else if (ILvarNum == (unsigned)ICorDebugInfo::TYPECTXT_ILNUM)
1305 noway_assert(info.compTypeCtxtArg >= 0);
1306 varNum = unsigned(info.compTypeCtxtArg);
1308 else if (ILvarNum < info.compILargsCount)
1311 varNum = compMapILargNum(ILvarNum);
1312 noway_assert(lvaTable[varNum].lvIsParam);
1314 else if (ILvarNum < info.compILlocalsCount)
1317 unsigned lclNum = ILvarNum - info.compILargsCount;
1318 varNum = info.compArgsCount + lclNum;
1319 noway_assert(!lvaTable[varNum].lvIsParam);
1326 noway_assert(varNum < info.compLocalsCount);
1330 /*****************************************************************************
1331 * Returns the IL variable number given our internal varNum.
1332 * Special return values are VARG_ILNUM, RETBUF_ILNUM, TYPECTXT_ILNUM.
1334 * Returns UNKNOWN_ILNUM if it can't be mapped.
1337 unsigned Compiler::compMap2ILvarNum(unsigned varNum)
1339 if (compIsForInlining())
1341 return impInlineInfo->InlinerCompiler->compMap2ILvarNum(varNum);
1344 noway_assert(varNum < lvaCount);
1346 if (varNum == info.compRetBuffArg)
1348 return (unsigned)ICorDebugInfo::RETBUF_ILNUM;
1351 // Is this a varargs function?
1352 if (info.compIsVarArgs && varNum == lvaVarargsHandleArg)
1354 return (unsigned)ICorDebugInfo::VARARGS_HND_ILNUM;
1357 // We create an extra argument for the type context parameter
1358 // needed for shared generic code.
1359 if ((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) && varNum == (unsigned)info.compTypeCtxtArg)
1361 return (unsigned)ICorDebugInfo::TYPECTXT_ILNUM;
1364 // Now mutate varNum to remove extra parameters from the count.
1365 if ((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) && varNum > (unsigned)info.compTypeCtxtArg)
1370 if (info.compIsVarArgs && varNum > lvaVarargsHandleArg)
1375 /* Is there a hidden argument for the return buffer.
1376 Note that this code works because if the RetBuffArg is not present,
1377 compRetBuffArg will be BAD_VAR_NUM */
1378 if (info.compRetBuffArg != BAD_VAR_NUM && varNum > info.compRetBuffArg)
1383 if (varNum >= info.compLocalsCount)
1385 return (unsigned)ICorDebugInfo::UNKNOWN_ILNUM; // Cannot be mapped
1391 /*****************************************************************************
1392 * Returns true if variable "varNum" may be address-exposed.
1395 bool Compiler::lvaVarAddrExposed(unsigned varNum)
1397 noway_assert(varNum < lvaCount);
1398 LclVarDsc* varDsc = &lvaTable[varNum];
1400 return varDsc->lvAddrExposed;
1403 /*****************************************************************************
1404 * Returns true iff variable "varNum" should not be enregistered (or one of several reasons).
1407 bool Compiler::lvaVarDoNotEnregister(unsigned varNum)
1409 noway_assert(varNum < lvaCount);
1410 LclVarDsc* varDsc = &lvaTable[varNum];
1412 return varDsc->lvDoNotEnregister;
1415 /*****************************************************************************
1416 * Returns the handle to the class of the local variable varNum
1419 CORINFO_CLASS_HANDLE Compiler::lvaGetStruct(unsigned varNum)
1421 noway_assert(varNum < lvaCount);
1422 LclVarDsc* varDsc = &lvaTable[varNum];
1424 return varDsc->lvVerTypeInfo.GetClassHandleForValueClass();
1427 /*****************************************************************************
1429 * Compare function passed to qsort() by Compiler::lvaCanPromoteStructVar().
1433 int __cdecl Compiler::lvaFieldOffsetCmp(const void* field1, const void* field2)
1435 lvaStructFieldInfo* pFieldInfo1 = (lvaStructFieldInfo*)field1;
1436 lvaStructFieldInfo* pFieldInfo2 = (lvaStructFieldInfo*)field2;
1438 if (pFieldInfo1->fldOffset == pFieldInfo2->fldOffset)
1444 return (pFieldInfo1->fldOffset > pFieldInfo2->fldOffset) ? +1 : -1;
1448 /*****************************************************************************
1449 * Is this type promotable? */
1451 void Compiler::lvaCanPromoteStructType(CORINFO_CLASS_HANDLE typeHnd,
1452 lvaStructPromotionInfo* StructPromotionInfo,
1455 assert(eeIsValueClass(typeHnd));
1457 if (typeHnd != StructPromotionInfo->typeHnd)
1459 // sizeof(double) represents the size of the largest primitive type that we can struct promote.
1460 // In the future this may be changing to XMM_REGSIZE_BYTES.
1461 // Note: MaxOffset is used below to declare a local array, and therefore must be a compile-time constant.
1462 CLANG_FORMAT_COMMENT_ANCHOR;
1463 #if defined(FEATURE_SIMD)
1464 #if defined(_TARGET_XARCH_)
1465 // This will allow promotion of 2 Vector<T> fields on AVX2, or 4 Vector<T> fields on SSE2.
1466 const int MaxOffset = MAX_NumOfFieldsInPromotableStruct * XMM_REGSIZE_BYTES;
1467 #elif defined(_TARGET_ARM64_)
1468 const int MaxOffset = MAX_NumOfFieldsInPromotableStruct * FP_REGSIZE_BYTES;
1469 #endif // defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)
1470 #else // !FEATURE_SIMD
1471 const int MaxOffset = MAX_NumOfFieldsInPromotableStruct * sizeof(double);
1472 #endif // !FEATURE_SIMD
1474 assert((BYTE)MaxOffset == MaxOffset); // because lvaStructFieldInfo.fldOffset is byte-sized
1475 assert((BYTE)MAX_NumOfFieldsInPromotableStruct ==
1476 MAX_NumOfFieldsInPromotableStruct); // because lvaStructFieldInfo.fieldCnt is byte-sized
1478 bool requiresScratchVar = false;
1479 bool containsHoles = false;
1480 bool customLayout = false;
1481 bool containsGCpointers = false;
1483 StructPromotionInfo->typeHnd = typeHnd;
1484 StructPromotionInfo->canPromote = false;
1486 unsigned structSize = info.compCompHnd->getClassSize(typeHnd);
1487 if (structSize > MaxOffset)
1489 return; // struct is too large
1492 unsigned fieldCnt = info.compCompHnd->getClassNumInstanceFields(typeHnd);
1493 if (fieldCnt == 0 || fieldCnt > MAX_NumOfFieldsInPromotableStruct)
1495 return; // struct must have between 1 and MAX_NumOfFieldsInPromotableStruct fields
1498 StructPromotionInfo->fieldCnt = (BYTE)fieldCnt;
1499 DWORD typeFlags = info.compCompHnd->getClassAttribs(typeHnd);
1501 bool treatAsOverlapping = StructHasOverlappingFields(typeFlags);
1503 #if 1 // TODO-Cleanup: Consider removing this entire #if block in the future
1505 // This method has two callers. The one in Importer.cpp passes `sortFields == false` and the other passes
1506 // `sortFields == true`. This is a workaround that leaves the inlining behavior the same as before while still
1507 // performing extra struct promotion when compiling the method.
1509 // The legacy back-end can't handle this more general struct promotion (notably structs with holes) in
1510 // morph/genPushArgList()/SetupLateArgs, so in that case always check for custom layout.
1511 #if !defined(LEGACY_BACKEND)
1512 if (!sortFields) // the condition "!sortFields" really means "we are inlining"
1515 treatAsOverlapping = StructHasCustomLayout(typeFlags);
1519 if (treatAsOverlapping)
1524 // Don't struct promote if we have an CUSTOMLAYOUT flag on an HFA type
1525 if (StructHasCustomLayout(typeFlags) && IsHfa(typeHnd))
1531 // On ARM, we have a requirement on the struct alignment; see below.
1532 unsigned structAlignment =
1533 roundUp(info.compCompHnd->getClassAlignmentRequirement(typeHnd), TARGET_POINTER_SIZE);
1534 #endif // _TARGET_ARM_
1536 bool isHole[MaxOffset]; // isHole[] is initialized to true for every valid offset in the struct and false for
1538 unsigned i; // then as we process the fields we clear the isHole[] values that the field spans.
1539 for (i = 0; i < MaxOffset; i++)
1541 isHole[i] = (i < structSize) ? true : false;
1544 for (BYTE ordinal = 0; ordinal < fieldCnt; ++ordinal)
1546 lvaStructFieldInfo* pFieldInfo = &StructPromotionInfo->fields[ordinal];
1547 pFieldInfo->fldHnd = info.compCompHnd->getFieldInClass(typeHnd, ordinal);
1548 unsigned fldOffset = info.compCompHnd->getFieldOffset(pFieldInfo->fldHnd);
1550 // The fldOffset value should never be larger than our structSize.
1551 if (fldOffset >= structSize)
1553 noway_assert(false);
1557 pFieldInfo->fldOffset = (BYTE)fldOffset;
1558 pFieldInfo->fldOrdinal = ordinal;
1559 CorInfoType corType = info.compCompHnd->getFieldType(pFieldInfo->fldHnd, &pFieldInfo->fldTypeHnd);
1560 pFieldInfo->fldType = JITtype2varType(corType);
1561 pFieldInfo->fldSize = genTypeSize(pFieldInfo->fldType);
1564 // Check to see if this is a SIMD type.
1565 // We will only check this if we have already found a SIMD type, which will be true if
1566 // we have encountered any SIMD intrinsics.
1567 if (usesSIMDTypes() && (pFieldInfo->fldSize == 0) && isSIMDorHWSIMDClass(pFieldInfo->fldTypeHnd))
1570 var_types simdBaseType = getBaseTypeAndSizeOfSIMDType(pFieldInfo->fldTypeHnd, &simdSize);
1571 if (simdBaseType != TYP_UNKNOWN)
1573 pFieldInfo->fldType = getSIMDTypeForSize(simdSize);
1574 pFieldInfo->fldSize = simdSize;
1577 #endif // FEATURE_SIMD
1579 if (pFieldInfo->fldSize == 0)
1581 // Size of TYP_BLK, TYP_FUNC, TYP_VOID and TYP_STRUCT is zero.
1582 // Early out if field type is other than TYP_STRUCT.
1583 // This is a defensive check as we don't expect a struct to have
1584 // fields of TYP_BLK, TYP_FUNC or TYP_VOID.
1585 if (pFieldInfo->fldType != TYP_STRUCT)
1590 // Non-primitive struct field.
1591 // Try to promote structs of single field of scalar types aligned at their
1592 // natural boundary.
1594 // Do Not promote if the struct field in turn has more than one field.
1595 if (info.compCompHnd->getClassNumInstanceFields(pFieldInfo->fldTypeHnd) != 1)
1600 // Do not promote if the single field is not aligned at its natural boundary within
1601 // the struct field.
1602 CORINFO_FIELD_HANDLE fHnd = info.compCompHnd->getFieldInClass(pFieldInfo->fldTypeHnd, 0);
1603 unsigned fOffset = info.compCompHnd->getFieldOffset(fHnd);
1609 CORINFO_CLASS_HANDLE cHnd;
1610 CorInfoType fieldCorType = info.compCompHnd->getFieldType(fHnd, &cHnd);
1611 var_types fieldVarType = JITtype2varType(fieldCorType);
1612 unsigned fieldSize = genTypeSize(fieldVarType);
1614 // Do not promote if either not a primitive type or size equal to ptr size on
1615 // target or a struct containing a single floating-point field.
1617 // TODO-PERF: Structs containing a single floating-point field on Amd64
1618 // needs to be passed in integer registers. Right now LSRA doesn't support
1619 // passing of floating-point LCL_VARS in integer registers. Enabling promotion
1620 // of such structs results in an assert in lsra right now.
1622 // TODO-PERF: Right now promotion is confined to struct containing a ptr sized
1623 // field (int/uint/ref/byref on 32-bits and long/ulong/ref/byref on 64-bits).
1624 // Though this would serve the purpose of promoting Span<T> containing ByReference<T>,
1625 // this can be extended to other primitive types as long as they are aligned at their
1626 // natural boundary.
1627 if (fieldSize == 0 || fieldSize != TARGET_POINTER_SIZE || varTypeIsFloating(fieldVarType))
1632 // Retype the field as the type of the single field of the struct
1633 pFieldInfo->fldType = fieldVarType;
1634 pFieldInfo->fldSize = fieldSize;
1637 if ((pFieldInfo->fldOffset % pFieldInfo->fldSize) != 0)
1639 // The code in Compiler::genPushArgList that reconstitutes
1640 // struct values on the stack from promoted fields expects
1641 // those fields to be at their natural alignment.
1645 if (varTypeIsGC(pFieldInfo->fldType))
1647 containsGCpointers = true;
1650 // The end offset for this field should never be larger than our structSize.
1651 noway_assert(fldOffset + pFieldInfo->fldSize <= structSize);
1653 for (i = 0; i < pFieldInfo->fldSize; i++)
1655 isHole[fldOffset + i] = false;
1659 // On ARM, for struct types that don't use explicit layout, the alignment of the struct is
1660 // at least the max alignment of its fields. We take advantage of this invariant in struct promotion,
1661 // so verify it here.
1662 if (pFieldInfo->fldSize > structAlignment)
1664 // Don't promote vars whose struct types violates the invariant. (Alignment == size for primitives.)
1667 // If we have any small fields we will allocate a single PromotedStructScratch local var for the method.
1668 // This is a stack area that we use to assemble the small fields in order to place them in a register
1671 if (pFieldInfo->fldSize < TARGET_POINTER_SIZE)
1673 requiresScratchVar = true;
1675 #endif // _TARGET_ARM_
1678 // If we saw any GC pointer or by-ref fields above then CORINFO_FLG_CONTAINS_GC_PTR or
1679 // CORINFO_FLG_CONTAINS_STACK_PTR has to be set!
1680 noway_assert((containsGCpointers == false) ||
1681 ((typeFlags & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_CONTAINS_STACK_PTR)) != 0));
1683 // If we have "Custom Layout" then we might have an explicit Size attribute
1684 // Managed C++ uses this for its structs, such C++ types will not contain GC pointers.
1686 // The current VM implementation also incorrectly sets the CORINFO_FLG_CUSTOMLAYOUT
1687 // whenever a managed value class contains any GC pointers.
1688 // (See the comment for VMFLAG_NOT_TIGHTLY_PACKED in class.h)
1690 // It is important to struct promote managed value classes that have GC pointers
1691 // So we compute the correct value for "CustomLayout" here
1693 if (StructHasCustomLayout(typeFlags) && ((typeFlags & CORINFO_FLG_CONTAINS_GC_PTR) == 0))
1695 customLayout = true;
1698 // Check if this promoted struct contains any holes
1700 for (i = 0; i < structSize; i++)
1704 containsHoles = true;
1709 // Cool, this struct is promotable.
1710 StructPromotionInfo->canPromote = true;
1711 StructPromotionInfo->requiresScratchVar = requiresScratchVar;
1712 StructPromotionInfo->containsHoles = containsHoles;
1713 StructPromotionInfo->customLayout = customLayout;
1717 // Sort the fields according to the increasing order of the field offset.
1718 // This is needed because the fields need to be pushed on stack (when referenced
1719 // as a struct) in order.
1720 qsort(StructPromotionInfo->fields, StructPromotionInfo->fieldCnt, sizeof(*StructPromotionInfo->fields),
1726 // Asking for the same type of struct as the last time.
1727 // Nothing need to be done.
1732 /*****************************************************************************
1733 * Is this struct type local variable promotable? */
1735 void Compiler::lvaCanPromoteStructVar(unsigned lclNum, lvaStructPromotionInfo* StructPromotionInfo)
1737 noway_assert(lclNum < lvaCount);
1739 LclVarDsc* varDsc = &lvaTable[lclNum];
1741 noway_assert(varTypeIsStruct(varDsc));
1742 noway_assert(!varDsc->lvPromoted); // Don't ask again :)
1744 // If this lclVar is used in a SIMD intrinsic, then we don't want to struct promote it.
1745 // Note, however, that SIMD lclVars that are NOT used in a SIMD intrinsic may be
1746 // profitably promoted.
1747 if (varDsc->lvIsUsedInSIMDIntrinsic())
1749 JITDUMP(" struct promotion of V%02u is disabled because lvIsUsedInSIMDIntrinsic()\n", lclNum);
1750 StructPromotionInfo->canPromote = false;
1754 // Reject struct promotion of parameters when -GS stack reordering is enabled
1755 // as we could introduce shadow copies of them.
1756 if (varDsc->lvIsParam && compGSReorderStackLayout)
1758 JITDUMP(" struct promotion of V%02u is disabled because lvIsParam and compGSReorderStackLayout\n", lclNum);
1759 StructPromotionInfo->canPromote = false;
1763 // Explicitly check for HFA reg args and reject them for promotion here.
1764 // Promoting HFA args will fire an assert in lvaAssignFrameOffsets
1765 // when the HFA reg arg is struct promoted.
1767 // TODO-PERF - Allow struct promotion for HFA register arguments
1768 if (varDsc->lvIsHfaRegArg())
1770 JITDUMP(" struct promotion of V%02u is disabled because lvIsHfaRegArg()\n", lclNum);
1771 StructPromotionInfo->canPromote = false;
1775 #if !FEATURE_MULTIREG_STRUCT_PROMOTE
1776 if (varDsc->lvIsMultiRegArg)
1778 JITDUMP(" struct promotion of V%02u is disabled because lvIsMultiRegArg\n", lclNum);
1779 StructPromotionInfo->canPromote = false;
1784 if (varDsc->lvIsMultiRegRet)
1786 JITDUMP(" struct promotion of V%02u is disabled because lvIsMultiRegRet\n", lclNum);
1787 StructPromotionInfo->canPromote = false;
1791 CORINFO_CLASS_HANDLE typeHnd = varDsc->lvVerTypeInfo.GetClassHandle();
1792 lvaCanPromoteStructType(typeHnd, StructPromotionInfo, true);
1795 //--------------------------------------------------------------------------------------------
1796 // lvaShouldPromoteStructVar - Should a struct var be promoted if it can be promoted?
1797 // This routine mainly performs profitability checks. Right now it also has
1798 // some correctness checks due to limitations of down-stream phases.
1801 // lclNum - Struct local number
1802 // structPromotionInfo - In Parameter; struct promotion information
1805 // true if the struct should be promoted
1806 bool Compiler::lvaShouldPromoteStructVar(unsigned lclNum, lvaStructPromotionInfo* structPromotionInfo)
1808 assert(lclNum < lvaCount);
1809 assert(structPromotionInfo->canPromote);
1811 LclVarDsc* varDsc = &lvaTable[lclNum];
1812 assert(varTypeIsStruct(varDsc));
1814 bool shouldPromote = true;
1816 // We *can* promote; *should* we promote?
1817 // We should only do so if promotion has potential savings. One source of savings
1818 // is if a field of the struct is accessed, since this access will be turned into
1819 // an access of the corresponding promoted field variable. Even if there are no
1820 // field accesses, but only block-level operations on the whole struct, if the struct
1821 // has only one or two fields, then doing those block operations field-wise is probably faster
1822 // than doing a whole-variable block operation (e.g., a hardware "copy loop" on x86).
1823 // Struct promotion also provides the following benefits: reduce stack frame size,
1824 // reduce the need for zero init of stack frame and fine grained constant/copy prop.
1825 // Asm diffs indicate that promoting structs up to 3 fields is a net size win.
1826 // So if no fields are accessed independently, and there are four or more fields,
1827 // then do not promote.
1829 // TODO: Ideally we would want to consider the impact of whether the struct is
1830 // passed as a parameter or assigned the return value of a call. Because once promoted,
1831 // struct copying is done by field by field assignment instead of a more efficient
1832 // rep.stos or xmm reg based copy.
1833 if (structPromotionInfo->fieldCnt > 3 && !varDsc->lvFieldAccessed)
1835 JITDUMP("Not promoting promotable struct local V%02u: #fields = %d, fieldAccessed = %d.\n", lclNum,
1836 structPromotionInfo->fieldCnt, varDsc->lvFieldAccessed);
1837 shouldPromote = false;
1839 #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || defined(_TARGET_ARM_)
1840 // TODO-PERF - Only do this when the LclVar is used in an argument context
1841 // TODO-ARM64 - HFA support should also eliminate the need for this.
1842 // TODO-ARM32 - HFA support should also eliminate the need for this.
1843 // TODO-LSRA - Currently doesn't support the passing of floating point LCL_VARS in the integer registers
1845 // For now we currently don't promote structs with a single float field
1846 // Promoting it can cause us to shuffle it back and forth between the int and
1847 // the float regs when it is used as a argument, which is very expensive for XARCH
1849 else if ((structPromotionInfo->fieldCnt == 1) && varTypeIsFloating(structPromotionInfo->fields[0].fldType))
1851 JITDUMP("Not promoting promotable struct local V%02u: #fields = %d because it is a struct with "
1852 "single float field.\n",
1853 lclNum, structPromotionInfo->fieldCnt);
1854 shouldPromote = false;
1856 #endif // _TARGET_AMD64_ || _TARGET_ARM64_ || _TARGET_ARM_
1857 else if (varDsc->lvIsParam && !lvaIsImplicitByRefLocal(lclNum))
1859 #if FEATURE_MULTIREG_STRUCT_PROMOTE
1860 // Is this a variable holding a value with exactly two fields passed in
1861 // multiple registers?
1862 if ((structPromotionInfo->fieldCnt != 2) && lvaIsMultiregStruct(varDsc))
1864 JITDUMP("Not promoting multireg struct local V%02u, because lvIsParam is true and #fields != 2\n", lclNum);
1865 shouldPromote = false;
1868 #endif // !FEATURE_MULTIREG_STRUCT_PROMOTE
1870 // TODO-PERF - Implement struct promotion for incoming multireg structs
1871 // Currently it hits assert(lvFieldCnt==1) in lclvar.cpp line 4417
1872 // Also the implementation of jmp uses the 4 byte move to store
1873 // byte parameters to the stack, so that if we have a byte field
1874 // with something else occupying the same 4-byte slot, it will
1875 // overwrite other fields.
1876 if (structPromotionInfo->fieldCnt != 1)
1878 JITDUMP("Not promoting promotable struct local V%02u, because lvIsParam is true and #fields = "
1880 lclNum, structPromotionInfo->fieldCnt);
1881 shouldPromote = false;
1886 // If the lvRefCnt is zero and we have a struct promoted parameter we can end up with an extra store of
1887 // the the incoming register into the stack frame slot.
1888 // In that case, we would like to avoid promortion.
1889 // However we haven't yet computed the lvRefCnt values so we can't do that.
1891 CLANG_FORMAT_COMMENT_ANCHOR;
1893 return shouldPromote;
1896 /*****************************************************************************
1897 * Promote a struct type local */
1899 void Compiler::lvaPromoteStructVar(unsigned lclNum, lvaStructPromotionInfo* StructPromotionInfo)
1901 LclVarDsc* varDsc = &lvaTable[lclNum];
1903 // We should never see a reg-sized non-field-addressed struct here.
1904 noway_assert(!varDsc->lvRegStruct);
1906 noway_assert(StructPromotionInfo->canPromote);
1907 noway_assert(StructPromotionInfo->typeHnd == varDsc->lvVerTypeInfo.GetClassHandle());
1909 varDsc->lvFieldCnt = StructPromotionInfo->fieldCnt;
1910 varDsc->lvFieldLclStart = lvaCount;
1911 varDsc->lvPromoted = true;
1912 varDsc->lvContainsHoles = StructPromotionInfo->containsHoles;
1913 varDsc->lvCustomLayout = StructPromotionInfo->customLayout;
1916 // Don't change the source to a TYP_BLK either.
1917 varDsc->lvKeepType = 1;
1923 printf("\nPromoting struct local V%02u (%s):", lclNum, eeGetClassName(StructPromotionInfo->typeHnd));
1927 for (unsigned index = 0; index < StructPromotionInfo->fieldCnt; ++index)
1929 lvaStructFieldInfo* pFieldInfo = &StructPromotionInfo->fields[index];
1931 if (varTypeIsFloating(pFieldInfo->fldType) || varTypeIsSIMD(pFieldInfo->fldType))
1933 // Whenever we promote a struct that contains a floating point field
1934 // it's possible we transition from a method that originally only had integer
1935 // local vars to start having FP. We have to communicate this through this flag
1936 // since LSRA later on will use this flag to determine whether or not to track FP register sets.
1937 compFloatingPointUsed = true;
1940 // Now grab the temp for the field local.
1944 char* bufp = &buf[0];
1946 sprintf_s(bufp, sizeof(buf), "%s V%02u.%s (fldOffset=0x%x)", "field", lclNum,
1947 eeGetFieldName(pFieldInfo->fldHnd), pFieldInfo->fldOffset);
1951 noway_assert(pFieldInfo->fldOffset > (pFieldInfo - 1)->fldOffset);
1955 unsigned varNum = lvaGrabTemp(false DEBUGARG(bufp)); // Lifetime of field locals might span multiple BBs,
1956 // so they must be long lifetime temps.
1957 varDsc = &lvaTable[lclNum]; // lvaGrabTemp can reallocate the lvaTable
1959 LclVarDsc* fieldVarDsc = &lvaTable[varNum];
1960 fieldVarDsc->lvType = pFieldInfo->fldType;
1961 fieldVarDsc->lvExactSize = pFieldInfo->fldSize;
1962 fieldVarDsc->lvIsStructField = true;
1963 fieldVarDsc->lvFieldHnd = pFieldInfo->fldHnd;
1964 fieldVarDsc->lvFldOffset = pFieldInfo->fldOffset;
1965 fieldVarDsc->lvFldOrdinal = pFieldInfo->fldOrdinal;
1966 fieldVarDsc->lvParentLcl = lclNum;
1967 fieldVarDsc->lvIsParam = varDsc->lvIsParam;
1968 #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
1969 // Do we have a parameter that can be enregistered?
1971 if (varDsc->lvIsRegArg)
1973 fieldVarDsc->lvIsRegArg = true;
1974 fieldVarDsc->lvArgReg = varDsc->lvArgReg;
1975 fieldVarDsc->setPrefReg(varDsc->lvArgReg, this); // Set the preferred register
1976 #if FEATURE_MULTIREG_ARGS && defined(FEATURE_SIMD)
1977 if (varTypeIsSIMD(fieldVarDsc))
1979 // This field is a SIMD type, and will be considered to be passed in multiple registers
1980 // if the parent struct was. Note that this code relies on the fact that if there is
1981 // a SIMD field of an enregisterable struct, it is the only field.
1982 // We will assert that, in case future changes are made to the ABI.
1983 assert(varDsc->lvFieldCnt == 1);
1984 fieldVarDsc->lvOtherArgReg = varDsc->lvOtherArgReg;
1986 #endif // FEATURE_MULTIREG_ARGS && defined(FEATURE_SIMD)
1988 lvaMarkRefsWeight = BB_UNITY_WEIGHT; // incRefCnts can use this compiler global variable
1989 fieldVarDsc->incRefCnts(BB_UNITY_WEIGHT, this); // increment the ref count for prolog initialization
1994 if (varTypeIsSIMD(pFieldInfo->fldType))
1996 // Set size to zero so that lvaSetStruct will appropriately set the SIMD-relevant fields.
1997 fieldVarDsc->lvExactSize = 0;
1998 lvaSetStruct(varNum, pFieldInfo->fldTypeHnd, false, true);
2000 #endif // FEATURE_SIMD
2003 // This temporary should not be converted to a double in stress mode,
2004 // because we introduce assigns to it after the stress conversion
2005 fieldVarDsc->lvKeepType = 1;
2010 #if !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_)
2011 //------------------------------------------------------------------------
2012 // lvaPromoteLongVars: "Struct promote" all register candidate longs as if they are structs of two ints.
2020 void Compiler::lvaPromoteLongVars()
2022 if ((opts.compFlags & CLFLG_REGVAR) == 0)
2027 // The lvaTable might grow as we grab temps. Make a local copy here.
2028 unsigned startLvaCount = lvaCount;
2029 for (unsigned lclNum = 0; lclNum < startLvaCount; lclNum++)
2031 LclVarDsc* varDsc = &lvaTable[lclNum];
2032 if (!varTypeIsLong(varDsc) || varDsc->lvDoNotEnregister || varDsc->lvIsMultiRegArgOrRet() ||
2033 (varDsc->lvRefCnt == 0) || varDsc->lvIsStructField || (fgNoStructPromotion && varDsc->lvIsParam))
2038 varDsc->lvFieldCnt = 2;
2039 varDsc->lvFieldLclStart = lvaCount;
2040 varDsc->lvPromoted = true;
2041 varDsc->lvContainsHoles = false;
2046 printf("\nPromoting long local V%02u:", lclNum);
2050 bool isParam = varDsc->lvIsParam;
2052 for (unsigned index = 0; index < 2; ++index)
2054 // Grab the temp for the field local.
2055 CLANG_FORMAT_COMMENT_ANCHOR;
2059 char* bufp = &buf[0];
2061 sprintf_s(bufp, sizeof(buf), "%s V%02u.%s (fldOffset=0x%x)", "field", lclNum, index == 0 ? "lo" : "hi",
2064 unsigned varNum = lvaGrabTemp(false DEBUGARG(bufp)); // Lifetime of field locals might span multiple BBs, so
2065 // they are long lifetime temps.
2067 LclVarDsc* fieldVarDsc = &lvaTable[varNum];
2068 fieldVarDsc->lvType = TYP_INT;
2069 fieldVarDsc->lvExactSize = genTypeSize(TYP_INT);
2070 fieldVarDsc->lvIsStructField = true;
2071 fieldVarDsc->lvFldOffset = (unsigned char)(index * genTypeSize(TYP_INT));
2072 fieldVarDsc->lvFldOrdinal = (unsigned char)index;
2073 fieldVarDsc->lvParentLcl = lclNum;
2074 // Currently we do not support enregistering incoming promoted aggregates with more than one field.
2077 fieldVarDsc->lvIsParam = true;
2078 lvaSetVarDoNotEnregister(varNum DEBUGARG(DNER_LongParamField));
2086 printf("\nlvaTable after lvaPromoteLongVars\n");
2091 #endif // !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_)
2093 /*****************************************************************************
2094 * Given a fldOffset in a promoted struct var, return the index of the local
2095 that represents this field.
2098 unsigned Compiler::lvaGetFieldLocal(LclVarDsc* varDsc, unsigned int fldOffset)
2100 noway_assert(varTypeIsStruct(varDsc));
2101 noway_assert(varDsc->lvPromoted);
2103 for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
2105 noway_assert(lvaTable[i].lvIsStructField);
2106 noway_assert(lvaTable[i].lvParentLcl == (unsigned)(varDsc - lvaTable));
2107 if (lvaTable[i].lvFldOffset == fldOffset)
2113 // This is the not-found error return path, the caller should check for BAD_VAR_NUM
2117 /*****************************************************************************
2119 * Set the local var "varNum" as address-exposed.
2120 * If this is a promoted struct, label it's fields the same way.
2123 void Compiler::lvaSetVarAddrExposed(unsigned varNum)
2125 noway_assert(varNum < lvaCount);
2127 LclVarDsc* varDsc = &lvaTable[varNum];
2129 varDsc->lvAddrExposed = 1;
2131 if (varDsc->lvPromoted)
2133 noway_assert(varTypeIsStruct(varDsc));
2135 for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
2137 noway_assert(lvaTable[i].lvIsStructField);
2138 lvaTable[i].lvAddrExposed = 1; // Make field local as address-exposed.
2139 lvaSetVarDoNotEnregister(i DEBUGARG(DNER_AddrExposed));
2143 lvaSetVarDoNotEnregister(varNum DEBUGARG(DNER_AddrExposed));
2146 /*****************************************************************************
2148 * Record that the local var "varNum" should not be enregistered (for one of several reasons.)
2151 void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregisterReason reason))
2153 noway_assert(varNum < lvaCount);
2154 LclVarDsc* varDsc = &lvaTable[varNum];
2155 varDsc->lvDoNotEnregister = 1;
2160 printf("\nLocal V%02u should not be enregistered because: ", varNum);
2164 case DNER_AddrExposed:
2165 JITDUMP("it is address exposed\n");
2166 assert(varDsc->lvAddrExposed);
2169 JITDUMP("it is a struct\n");
2170 assert(varTypeIsStruct(varDsc));
2172 case DNER_IsStructArg:
2173 JITDUMP("it is a struct arg\n");
2174 assert(varTypeIsStruct(varDsc));
2177 JITDUMP("written in a block op\n");
2178 varDsc->lvLclBlockOpAddr = 1;
2180 case DNER_LocalField:
2181 JITDUMP("was accessed as a local field\n");
2182 varDsc->lvLclFieldExpr = 1;
2184 case DNER_VMNeedsStackAddr:
2185 JITDUMP("needs stack addr\n");
2186 varDsc->lvVMNeedsStackAddr = 1;
2188 case DNER_LiveInOutOfHandler:
2189 JITDUMP("live in/out of a handler\n");
2190 varDsc->lvLiveInOutOfHndlr = 1;
2192 case DNER_LiveAcrossUnmanagedCall:
2193 JITDUMP("live across unmanaged call\n");
2194 varDsc->lvLiveAcrossUCall = 1;
2197 JITDUMP("field of a dependently promoted struct\n");
2198 assert(varDsc->lvIsStructField && (lvaGetParentPromotionType(varNum) != PROMOTION_TYPE_INDEPENDENT));
2200 case DNER_NoRegVars:
2201 JITDUMP("opts.compFlags & CLFLG_REGVAR is not set\n");
2202 assert((opts.compFlags & CLFLG_REGVAR) == 0);
2204 case DNER_MinOptsGC:
2205 JITDUMP("It is a GC Ref and we are compiling MinOpts\n");
2206 assert(!JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet()));
2208 #ifdef JIT32_GCENCODER
2209 case DNER_PinningRef:
2210 JITDUMP("pinning ref\n");
2211 assert(varDsc->lvPinned);
2214 #if !defined(LEGACY_BACKEND) && !defined(_TARGET_64BIT_)
2215 case DNER_LongParamField:
2216 JITDUMP("it is a decomposed field of a long parameter\n");
2226 // Returns true if this local var is a multireg struct
2227 bool Compiler::lvaIsMultiregStruct(LclVarDsc* varDsc)
2229 if (varTypeIsStruct(varDsc->TypeGet()))
2231 CORINFO_CLASS_HANDLE clsHnd = varDsc->lvVerTypeInfo.GetClassHandleForValueClass();
2232 structPassingKind howToPassStruct;
2234 var_types type = getArgTypeForStruct(clsHnd, &howToPassStruct, varDsc->lvExactSize);
2236 if (howToPassStruct == SPK_ByValueAsHfa)
2238 assert(type == TYP_STRUCT);
2242 #if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING) || defined(_TARGET_ARM64_)
2243 if (howToPassStruct == SPK_ByValue)
2245 assert(type == TYP_STRUCT);
2253 /*****************************************************************************
2254 * Set the lvClass for a local variable of a struct type */
2256 void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool unsafeValueClsCheck, bool setTypeInfo)
2258 noway_assert(varNum < lvaCount);
2260 LclVarDsc* varDsc = &lvaTable[varNum];
2263 varDsc->lvVerTypeInfo = typeInfo(TI_STRUCT, typeHnd);
2266 // Set the type and associated info if we haven't already set it.
2267 var_types structType = varDsc->lvType;
2268 if (varDsc->lvType == TYP_UNDEF)
2270 varDsc->lvType = TYP_STRUCT;
2272 if (varDsc->lvExactSize == 0)
2274 varDsc->lvExactSize = info.compCompHnd->getClassSize(typeHnd);
2276 size_t lvSize = varDsc->lvSize();
2277 assert((lvSize % TARGET_POINTER_SIZE) ==
2278 0); // The struct needs to be a multiple of TARGET_POINTER_SIZE bytes for getClassGClayout() to be valid.
2279 varDsc->lvGcLayout = (BYTE*)compGetMem((lvSize / TARGET_POINTER_SIZE) * sizeof(BYTE), CMK_LvaTable);
2281 var_types simdBaseType = TYP_UNKNOWN;
2282 varDsc->lvType = impNormStructType(typeHnd, varDsc->lvGcLayout, &numGCVars, &simdBaseType);
2284 // We only save the count of GC vars in a struct up to 7.
2289 varDsc->lvStructGcCount = numGCVars;
2291 if (simdBaseType != TYP_UNKNOWN)
2293 assert(varTypeIsSIMD(varDsc));
2294 varDsc->lvSIMDType = true;
2295 varDsc->lvBaseType = simdBaseType;
2297 #endif // FEATURE_SIMD
2299 // for structs that are small enough, we check and set lvIsHfa and lvHfaTypeIsFloat
2300 if (varDsc->lvExactSize <= MAX_PASS_MULTIREG_BYTES)
2302 var_types hfaType = GetHfaType(typeHnd); // set to float or double if it is an HFA, otherwise TYP_UNDEF
2303 if (varTypeIsFloating(hfaType))
2305 varDsc->_lvIsHfa = true;
2306 varDsc->lvSetHfaTypeIsFloat(hfaType == TYP_FLOAT);
2308 // hfa variables can never contain GC pointers
2309 assert(varDsc->lvStructGcCount == 0);
2310 // The size of this struct should be evenly divisible by 4 or 8
2311 assert((varDsc->lvExactSize % genTypeSize(hfaType)) == 0);
2312 // The number of elements in the HFA should fit into our MAX_ARG_REG_COUNT limit
2313 assert((varDsc->lvExactSize / genTypeSize(hfaType)) <= MAX_ARG_REG_COUNT);
2316 #endif // FEATURE_HFA
2321 assert(!varTypeIsSIMD(varDsc) || (varDsc->lvBaseType != TYP_UNKNOWN));
2322 #endif // FEATURE_SIMD
2325 #ifndef _TARGET_64BIT_
2326 BOOL fDoubleAlignHint = FALSE;
2328 fDoubleAlignHint = TRUE;
2331 if (info.compCompHnd->getClassAlignmentRequirement(typeHnd, fDoubleAlignHint) == 8)
2336 printf("Marking struct in V%02i with double align flag\n", varNum);
2339 varDsc->lvStructDoubleAlign = 1;
2341 #endif // not _TARGET_64BIT_
2343 unsigned classAttribs = info.compCompHnd->getClassAttribs(typeHnd);
2345 varDsc->lvOverlappingFields = StructHasOverlappingFields(classAttribs);
2347 // Check whether this local is an unsafe value type and requires GS cookie protection.
2348 // GS checks require the stack to be re-ordered, which can't be done with EnC.
2349 if (unsafeValueClsCheck && (classAttribs & CORINFO_FLG_UNSAFE_VALUECLASS) && !opts.compDbgEnC)
2351 setNeedsGSSecurityCookie();
2352 compGSReorderStackLayout = true;
2353 varDsc->lvIsUnsafeBuffer = true;
2357 //------------------------------------------------------------------------
2358 // lvaSetClass: set class information for a local var.
2361 // varNum -- number of the variable
2362 // clsHnd -- class handle to use in set or update
2363 // isExact -- true if class is known exactly
2366 // varNum must not already have a ref class handle.
2368 void Compiler::lvaSetClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool isExact)
2370 noway_assert(varNum < lvaCount);
2372 // If we are just importing, we cannot reliably track local ref types,
2373 // since the jit maps CORINFO_TYPE_VAR to TYP_REF.
2374 if (compIsForImportOnly())
2379 // Else we should have a type handle.
2380 assert(clsHnd != nullptr);
2382 LclVarDsc* varDsc = &lvaTable[varNum];
2383 assert(varDsc->lvType == TYP_REF);
2385 // We shoud not have any ref type information for this var.
2386 assert(varDsc->lvClassHnd == nullptr);
2387 assert(!varDsc->lvClassIsExact);
2389 JITDUMP("\nlvaSetClass: setting class for V%02i to (%p) %s %s\n", varNum, dspPtr(clsHnd),
2390 info.compCompHnd->getClassName(clsHnd), isExact ? " [exact]" : "");
2392 varDsc->lvClassHnd = clsHnd;
2393 varDsc->lvClassIsExact = isExact;
2396 //------------------------------------------------------------------------
2397 // lvaSetClass: set class information for a local var from a tree or stack type
2400 // varNum -- number of the variable. Must be a single def local
2401 // tree -- tree establishing the variable's value
2402 // stackHnd -- handle for the type from the evaluation stack
2405 // Preferentially uses the tree's type, when available. Since not all
2406 // tree kinds can track ref types, the stack type is used as a
2409 void Compiler::lvaSetClass(unsigned varNum, GenTree* tree, CORINFO_CLASS_HANDLE stackHnd)
2411 bool isExact = false;
2412 bool isNonNull = false;
2413 CORINFO_CLASS_HANDLE clsHnd = gtGetClassHandle(tree, &isExact, &isNonNull);
2415 if (clsHnd != nullptr)
2417 lvaSetClass(varNum, clsHnd, isExact);
2419 else if (stackHnd != nullptr)
2421 lvaSetClass(varNum, stackHnd);
2425 //------------------------------------------------------------------------
2426 // lvaUpdateClass: update class information for a local var.
2429 // varNum -- number of the variable
2430 // clsHnd -- class handle to use in set or update
2431 // isExact -- true if class is known exactly
2435 // This method models the type update rule for an assignment.
2437 // Updates currently should only happen for single-def user args or
2438 // locals, when we are processing the expression actually being
2439 // used to initialize the local (or inlined arg). The update will
2440 // change the local from the declared type to the type of the
2443 // These updates should always *improve* what we know about the
2444 // type, that is making an inexact type exact, or changing a type
2445 // to some subtype. However the jit lacks precise type information
2446 // for shared code, so ensuring this is so is currently not
2449 void Compiler::lvaUpdateClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool isExact)
2451 assert(varNum < lvaCount);
2453 // If we are just importing, we cannot reliably track local ref types,
2454 // since the jit maps CORINFO_TYPE_VAR to TYP_REF.
2455 if (compIsForImportOnly())
2460 // Else we should have a class handle to consider
2461 assert(clsHnd != nullptr);
2463 LclVarDsc* varDsc = &lvaTable[varNum];
2464 assert(varDsc->lvType == TYP_REF);
2466 // We should already have a class
2467 assert(varDsc->lvClassHnd != nullptr);
2471 // In general we only expect one update per local var. However if
2472 // a block is re-imported and that block has the only STLOC for
2473 // the var, we may see multiple updates. All subsequent updates
2474 // should agree on the type, since reimportation is triggered by
2475 // type mismatches for things other than ref types.
2476 if (varDsc->lvClassInfoUpdated)
2478 assert(varDsc->lvClassHnd == clsHnd);
2479 assert(varDsc->lvClassIsExact == isExact);
2482 // This counts as an update, even if nothing changes.
2483 varDsc->lvClassInfoUpdated = true;
2485 #endif // defined(DEBUG)
2487 // If previous type was exact, there is nothing to update. Would
2488 // like to verify new type is compatible but can't do this yet.
2489 if (varDsc->lvClassIsExact)
2494 // Are we updating the type?
2495 if (varDsc->lvClassHnd != clsHnd)
2497 JITDUMP("\nlvaUpdateClass: Updating class for V%02i from (%p) %s to (%p) %s %s\n", varNum,
2498 dspPtr(varDsc->lvClassHnd), info.compCompHnd->getClassName(varDsc->lvClassHnd), dspPtr(clsHnd),
2499 info.compCompHnd->getClassName(clsHnd), isExact ? " [exact]" : "");
2501 varDsc->lvClassHnd = clsHnd;
2502 varDsc->lvClassIsExact = isExact;
2506 // Class info matched. Are we updating exactness?
2509 JITDUMP("\nlvaUpdateClass: Updating class for V%02i (%p) %s to be exact\n", varNum, dspPtr(varDsc->lvClassHnd),
2510 info.compCompHnd->getClassName(varDsc->lvClassHnd));
2512 varDsc->lvClassIsExact = isExact;
2516 // Else we have the same handle and (in)exactness as before. Do nothing.
2520 //------------------------------------------------------------------------
2521 // lvaUpdateClass: Uupdate class information for a local var from a tree
2525 // varNum -- number of the variable. Must be a single def local
2526 // tree -- tree establishing the variable's value
2527 // stackHnd -- handle for the type from the evaluation stack
2530 // Preferentially uses the tree's type, when available. Since not all
2531 // tree kinds can track ref types, the stack type is used as a
2534 void Compiler::lvaUpdateClass(unsigned varNum, GenTree* tree, CORINFO_CLASS_HANDLE stackHnd)
2536 bool isExact = false;
2537 bool isNonNull = false;
2538 CORINFO_CLASS_HANDLE clsHnd = gtGetClassHandle(tree, &isExact, &isNonNull);
2540 if (clsHnd != nullptr)
2542 lvaUpdateClass(varNum, clsHnd, isExact);
2544 else if (stackHnd != nullptr)
2546 lvaUpdateClass(varNum, stackHnd);
2550 /*****************************************************************************
2551 * Returns the array of BYTEs containing the GC layout information
2554 BYTE* Compiler::lvaGetGcLayout(unsigned varNum)
2556 assert(varTypeIsStruct(lvaTable[varNum].lvType) && (lvaTable[varNum].lvExactSize >= TARGET_POINTER_SIZE));
2558 return lvaTable[varNum].lvGcLayout;
2561 //------------------------------------------------------------------------
2562 // lvaLclSize: returns size of a local variable, in bytes
2565 // varNum -- variable to query
2568 // Number of bytes needed on the frame for such a local.
2570 unsigned Compiler::lvaLclSize(unsigned varNum)
2572 assert(varNum < lvaCount);
2574 var_types varType = lvaTable[varNum].TypeGet();
2580 return lvaTable[varNum].lvSize();
2583 #if FEATURE_FIXED_OUT_ARGS
2584 noway_assert(varNum == lvaOutgoingArgSpaceVar);
2585 return lvaOutgoingArgSpaceSize;
2586 #else // FEATURE_FIXED_OUT_ARGS
2587 assert(!"Unknown size");
2588 NO_WAY("Target doesn't support TYP_LCLBLK");
2590 // Keep prefast happy
2593 #endif // FEATURE_FIXED_OUT_ARGS
2595 default: // This must be a primitive var. Fall out of switch statement
2598 #ifdef _TARGET_64BIT_
2599 // We only need this Quirk for _TARGET_64BIT_
2600 if (lvaTable[varNum].lvQuirkToLong)
2602 noway_assert(lvaTable[varNum].lvAddrExposed);
2603 return genTypeStSz(TYP_LONG) * sizeof(int); // return 8 (2 * 4)
2606 return genTypeStSz(varType) * sizeof(int);
2610 // Return the exact width of local variable "varNum" -- the number of bytes
2611 // you'd need to copy in order to overwrite the value.
2613 unsigned Compiler::lvaLclExactSize(unsigned varNum)
2615 assert(varNum < lvaCount);
2617 var_types varType = lvaTable[varNum].TypeGet();
2623 return lvaTable[varNum].lvExactSize;
2626 #if FEATURE_FIXED_OUT_ARGS
2627 noway_assert(lvaOutgoingArgSpaceSize >= 0);
2628 noway_assert(varNum == lvaOutgoingArgSpaceVar);
2629 return lvaOutgoingArgSpaceSize;
2631 #else // FEATURE_FIXED_OUT_ARGS
2632 assert(!"Unknown size");
2633 NO_WAY("Target doesn't support TYP_LCLBLK");
2635 // Keep prefast happy
2638 #endif // FEATURE_FIXED_OUT_ARGS
2640 default: // This must be a primitive var. Fall out of switch statement
2644 return genTypeSize(varType);
2647 // getCalledCount -- get the value used to normalized weights for this method
2648 // if we don't have profile data then getCalledCount will return BB_UNITY_WEIGHT (100)
2649 // otherwise it returns the number of times that profile data says the method was called.
2651 BasicBlock::weight_t BasicBlock::getCalledCount(Compiler* comp)
2653 // when we don't have profile data then fgCalledCount will be BB_UNITY_WEIGHT (100)
2654 BasicBlock::weight_t calledCount = comp->fgCalledCount;
2656 // If we haven't yet reach the place where we setup fgCalledCount it could still be zero
2657 // so return a reasonable value to use until we set it.
2659 if (calledCount == 0)
2661 if (comp->fgIsUsingProfileWeights())
2663 // When we use profile data block counts we have exact counts,
2664 // not multiples of BB_UNITY_WEIGHT (100)
2669 calledCount = comp->fgFirstBB->bbWeight;
2671 if (calledCount == 0)
2673 calledCount = BB_UNITY_WEIGHT;
2680 // getBBWeight -- get the normalized weight of this block
2681 BasicBlock::weight_t BasicBlock::getBBWeight(Compiler* comp)
2683 if (this->bbWeight == 0)
2689 weight_t calledCount = getCalledCount(comp);
2691 // Normalize the bbWeights by multiplying by BB_UNITY_WEIGHT and dividing by the calledCount.
2693 // 1. For methods that do not have IBC data the called weight will always be 100 (BB_UNITY_WEIGHT)
2694 // and the entry point bbWeight value is almost always 100 (BB_UNITY_WEIGHT)
2695 // 2. For methods that do have IBC data the called weight is the actual number of calls
2696 // from the IBC data and the entry point bbWeight value is almost always the actual
2697 // number of calls from the IBC data.
2699 // "almost always" - except for the rare case where a loop backedge jumps to BB01
2701 // We also perform a rounding operation by adding half of the 'calledCount' before performing
2704 // Thus for both cases we will return 100 (BB_UNITY_WEIGHT) for the entry point BasicBlock
2706 // Note that with a 100 (BB_UNITY_WEIGHT) values between 1 and 99 represent decimal fractions.
2707 // (i.e. 33 represents 33% and 75 represents 75%, and values greater than 100 require
2708 // some kind of loop backedge)
2711 if (this->bbWeight < (BB_MAX_WEIGHT / BB_UNITY_WEIGHT))
2713 // Calculate the result using unsigned arithmetic
2714 weight_t result = ((this->bbWeight * BB_UNITY_WEIGHT) + (calledCount / 2)) / calledCount;
2716 // We don't allow a value of zero, as that would imply rarely run
2717 return max(1, result);
2721 // Calculate the full result using floating point
2722 double fullResult = ((double)this->bbWeight * (double)BB_UNITY_WEIGHT) / (double)calledCount;
2724 if (fullResult < (double)BB_MAX_WEIGHT)
2726 // Add 0.5 and truncate to unsigned
2727 return (weight_t)(fullResult + 0.5);
2731 return BB_MAX_WEIGHT;
2737 // Decrement the ref counts for all locals contained in the tree and its children.
2738 void Compiler::lvaRecursiveDecRefCounts(GenTree* tree)
2740 assert(lvaLocalVarRefCounted);
2742 // We could just use the recursive walker for all cases but that is a
2743 // fairly heavyweight thing to spin up when we're usually just handling a leaf.
2744 if (tree->OperIsLeaf())
2746 if (tree->OperIsLocal())
2748 lvaDecRefCnts(tree);
2753 DecLclVarRefCountsVisitor::WalkTree(this, tree);
2757 DecLclVarRefCountsVisitor::DecLclVarRefCountsVisitor(Compiler* compiler)
2758 : GenTreeVisitor<DecLclVarRefCountsVisitor>(compiler)
2762 Compiler::fgWalkResult DecLclVarRefCountsVisitor::PreOrderVisit(GenTree** use, GenTree* user)
2764 m_compiler->lvaDecRefCnts(*use);
2765 return fgWalkResult::WALK_CONTINUE;
2768 Compiler::fgWalkResult DecLclVarRefCountsVisitor::WalkTree(Compiler* compiler, GenTree* tree)
2770 DecLclVarRefCountsVisitor visitor(compiler);
2771 return static_cast<GenTreeVisitor<DecLclVarRefCountsVisitor>*>(&visitor)->WalkTree(&tree, nullptr);
2774 /*****************************************************************************
2776 * Helper passed to the tree walker to decrement the refCnts for
2777 * all local variables in an expression
2779 void Compiler::lvaDecRefCnts(GenTree* tree)
2781 assert(compCurBB != nullptr);
2782 lvaDecRefCnts(compCurBB, tree);
2785 void Compiler::lvaDecRefCnts(BasicBlock* block, GenTree* tree)
2787 assert(block != nullptr);
2788 assert(tree != nullptr);
2793 noway_assert(lvaRefCountingStarted || lvaLocalVarRefCounted);
2795 if ((tree->gtOper == GT_CALL) && (tree->gtFlags & GTF_CALL_UNMANAGED))
2797 assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
2798 if (!opts.ShouldUsePInvokeHelpers())
2800 /* Get the special variable descriptor */
2802 lclNum = info.compLvFrameListRoot;
2804 assert(lclNum <= lvaCount);
2805 varDsc = lvaTable + lclNum;
2807 /* Decrement the reference counts twice */
2809 varDsc->decRefCnts(block->getBBWeight(this), this);
2810 varDsc->decRefCnts(block->getBBWeight(this), this);
2815 /* This must be a local variable */
2817 noway_assert(tree->OperIsLocal());
2819 /* Get the variable descriptor */
2821 lclNum = tree->gtLclVarCommon.gtLclNum;
2823 assert(lclNum < lvaCount);
2824 varDsc = lvaTable + lclNum;
2826 /* Decrement its lvRefCnt and lvRefCntWtd */
2828 varDsc->decRefCnts(block->getBBWeight(this), this);
2832 // Increment the ref counts for all locals contained in the tree and its children.
2833 void Compiler::lvaRecursiveIncRefCounts(GenTree* tree)
2835 assert(lvaLocalVarRefCounted);
2837 // We could just use the recursive walker for all cases but that is a
2838 // fairly heavyweight thing to spin up when we're usually just handling a leaf.
2839 if (tree->OperIsLeaf())
2841 if (tree->OperIsLocal())
2843 lvaIncRefCnts(tree);
2848 IncLclVarRefCountsVisitor::WalkTree(this, tree);
2852 IncLclVarRefCountsVisitor::IncLclVarRefCountsVisitor(Compiler* compiler)
2853 : GenTreeVisitor<IncLclVarRefCountsVisitor>(compiler)
2857 Compiler::fgWalkResult IncLclVarRefCountsVisitor::PreOrderVisit(GenTree** use, GenTree* user)
2859 m_compiler->lvaIncRefCnts(*use);
2860 return fgWalkResult::WALK_CONTINUE;
2863 Compiler::fgWalkResult IncLclVarRefCountsVisitor::WalkTree(Compiler* compiler, GenTree* tree)
2865 IncLclVarRefCountsVisitor visitor(compiler);
2866 return static_cast<GenTreeVisitor<IncLclVarRefCountsVisitor>*>(&visitor)->WalkTree(&tree, nullptr);
2869 /*****************************************************************************
2871 * Helper passed to the tree walker to increment the refCnts for
2872 * all local variables in an expression
2874 void Compiler::lvaIncRefCnts(GenTree* tree)
2879 noway_assert(lvaRefCountingStarted || lvaLocalVarRefCounted);
2881 if ((tree->gtOper == GT_CALL) && (tree->gtFlags & GTF_CALL_UNMANAGED))
2883 assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
2884 if (!opts.ShouldUsePInvokeHelpers())
2886 /* Get the special variable descriptor */
2888 lclNum = info.compLvFrameListRoot;
2890 assert(lclNum <= lvaCount);
2891 varDsc = lvaTable + lclNum;
2893 /* Increment the reference counts twice */
2895 varDsc->incRefCnts(compCurBB->getBBWeight(this), this);
2896 varDsc->incRefCnts(compCurBB->getBBWeight(this), this);
2901 /* This must be a local variable */
2903 noway_assert(tree->gtOper == GT_LCL_VAR || tree->gtOper == GT_LCL_FLD || tree->gtOper == GT_STORE_LCL_VAR ||
2904 tree->gtOper == GT_STORE_LCL_FLD);
2906 /* Get the variable descriptor */
2908 lclNum = tree->gtLclVarCommon.gtLclNum;
2910 assert(lclNum < lvaCount);
2911 varDsc = lvaTable + lclNum;
2913 /* Increment its lvRefCnt and lvRefCntWtd */
2915 varDsc->incRefCnts(compCurBB->getBBWeight(this), this);
2919 /*****************************************************************************
2921 * Compare function passed to qsort() by Compiler::lclVars.lvaSortByRefCount().
2922 * when generating SMALL_CODE.
2923 * Return positive if dsc2 has a higher ref count
2924 * Return negative if dsc1 has a higher ref count
2925 * Return zero if the ref counts are the same
2926 * lvPrefReg is only used to break ties
2930 int __cdecl Compiler::RefCntCmp(const void* op1, const void* op2)
2932 LclVarDsc* dsc1 = *(LclVarDsc**)op1;
2933 LclVarDsc* dsc2 = *(LclVarDsc**)op2;
2935 /* Make sure we preference tracked variables over untracked variables */
2937 if (dsc1->lvTracked != dsc2->lvTracked)
2939 return (dsc2->lvTracked) ? +1 : -1;
2942 unsigned weight1 = dsc1->lvRefCnt;
2943 unsigned weight2 = dsc2->lvRefCnt;
2945 #if !FEATURE_FP_REGALLOC
2946 /* Force integer candidates to sort above float candidates */
2948 bool isFloat1 = isFloatRegType(dsc1->lvType);
2949 bool isFloat2 = isFloatRegType(dsc2->lvType);
2951 if (isFloat1 != isFloat2)
2953 if (weight2 && isFloat1)
2957 if (weight1 && isFloat2)
2964 int diff = weight2 - weight1;
2971 /* The unweighted ref counts were the same */
2972 /* If the weighted ref counts are different then use their difference */
2973 diff = dsc2->lvRefCntWtd - dsc1->lvRefCntWtd;
2980 /* We have equal ref counts and weighted ref counts */
2982 /* Break the tie by: */
2983 /* Increasing the weight by 2 if we have exactly one bit set in lvPrefReg */
2984 /* Increasing the weight by 1 if we have more than one bit set in lvPrefReg */
2985 /* Increasing the weight by 0.5 if we are a GC type */
2986 /* Increasing the weight by 0.5 if we were enregistered in the previous pass */
2990 if (dsc1->lvPrefReg)
2992 if ((dsc1->lvPrefReg & ~RBM_BYTE_REG_FLAG) && genMaxOneBit((unsigned)dsc1->lvPrefReg))
2994 weight1 += 2 * BB_UNITY_WEIGHT;
2998 weight1 += 1 * BB_UNITY_WEIGHT;
3001 if (varTypeIsGC(dsc1->TypeGet()))
3003 weight1 += BB_UNITY_WEIGHT / 2;
3006 if (dsc1->lvRegister)
3008 weight1 += BB_UNITY_WEIGHT / 2;
3014 if (dsc2->lvPrefReg)
3016 if ((dsc2->lvPrefReg & ~RBM_BYTE_REG_FLAG) && genMaxOneBit((unsigned)dsc2->lvPrefReg))
3018 weight2 += 2 * BB_UNITY_WEIGHT;
3022 weight2 += 1 * BB_UNITY_WEIGHT;
3025 if (varTypeIsGC(dsc2->TypeGet()))
3027 weight2 += BB_UNITY_WEIGHT / 2;
3030 if (dsc2->lvRegister)
3032 weight2 += BB_UNITY_WEIGHT / 2;
3036 diff = weight2 - weight1;
3043 /* To achieve a Stable Sort we use the LclNum (by way of the pointer address) */
3057 /*****************************************************************************
3059 * Compare function passed to qsort() by Compiler::lclVars.lvaSortByRefCount().
3060 * when not generating SMALL_CODE.
3061 * Return positive if dsc2 has a higher weighted ref count
3062 * Return negative if dsc1 has a higher weighted ref count
3063 * Return zero if the ref counts are the same
3067 int __cdecl Compiler::WtdRefCntCmp(const void* op1, const void* op2)
3069 LclVarDsc* dsc1 = *(LclVarDsc**)op1;
3070 LclVarDsc* dsc2 = *(LclVarDsc**)op2;
3072 /* Make sure we preference tracked variables over untracked variables */
3074 if (dsc1->lvTracked != dsc2->lvTracked)
3076 return (dsc2->lvTracked) ? +1 : -1;
3079 unsigned weight1 = dsc1->lvRefCntWtd;
3080 unsigned weight2 = dsc2->lvRefCntWtd;
3082 #if !FEATURE_FP_REGALLOC
3083 /* Force integer candidates to sort above float candidates */
3085 bool isFloat1 = isFloatRegType(dsc1->lvType);
3086 bool isFloat2 = isFloatRegType(dsc2->lvType);
3088 if (isFloat1 != isFloat2)
3090 if (weight2 && isFloat1)
3094 if (weight1 && isFloat2)
3101 /* Increase the weight by 2 if we have exactly one bit set in lvPrefReg */
3102 /* Increase the weight by 1 if we have more than one bit set in lvPrefReg */
3104 if (weight1 && dsc1->lvPrefReg)
3106 if ((dsc1->lvPrefReg & ~RBM_BYTE_REG_FLAG) && genMaxOneBit((unsigned)dsc1->lvPrefReg))
3108 weight1 += 2 * BB_UNITY_WEIGHT;
3112 weight1 += 1 * BB_UNITY_WEIGHT;
3116 if (weight2 && dsc2->lvPrefReg)
3118 if ((dsc2->lvPrefReg & ~RBM_BYTE_REG_FLAG) && genMaxOneBit((unsigned)dsc2->lvPrefReg))
3120 weight2 += 2 * BB_UNITY_WEIGHT;
3124 weight2 += 1 * BB_UNITY_WEIGHT;
3128 if (weight2 > weight1)
3132 else if (weight2 < weight1)
3137 // Otherwise, we have equal weighted ref counts.
3139 /* If the unweighted ref counts are different then use their difference */
3140 int diff = (int)dsc2->lvRefCnt - (int)dsc1->lvRefCnt;
3147 /* If one is a GC type and the other is not the GC type wins */
3148 if (varTypeIsGC(dsc1->TypeGet()) != varTypeIsGC(dsc2->TypeGet()))
3150 if (varTypeIsGC(dsc1->TypeGet()))
3162 /* If one was enregistered in the previous pass then it wins */
3163 if (dsc1->lvRegister != dsc2->lvRegister)
3165 if (dsc1->lvRegister)
3177 /* We have a tie! */
3179 /* To achieve a Stable Sort we use the LclNum (by way of the pointer address) */
3193 /*****************************************************************************
3195 * Sort the local variable table by refcount and assign tracking indices.
3198 void Compiler::lvaSortOnly()
3200 /* Now sort the variable table by ref-count */
3202 qsort(lvaRefSorted, lvaCount, sizeof(*lvaRefSorted), (compCodeOpt() == SMALL_CODE) ? RefCntCmp : WtdRefCntCmp);
3204 lvaSortAgain = false;
3209 void Compiler::lvaDumpRefCounts()
3213 if (verbose && lvaCount)
3215 printf("refCnt table for '%s':\n", info.compMethodName);
3217 for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++)
3219 unsigned refCnt = lvaRefSorted[lclNum]->lvRefCnt;
3224 unsigned refCntWtd = lvaRefSorted[lclNum]->lvRefCntWtd;
3227 gtDispLclVar((unsigned)(lvaRefSorted[lclNum] - lvaTable));
3228 printf(" [%6s]: refCnt = %4u, refCntWtd = %6s", varTypeName(lvaRefSorted[lclNum]->TypeGet()), refCnt,
3229 refCntWtd2str(refCntWtd));
3231 regMaskSmall pref = lvaRefSorted[lclNum]->lvPrefReg;
3246 /*****************************************************************************
3248 * Sort the local variable table by refcount and assign tracking indices.
3251 void Compiler::lvaSortByRefCount()
3253 lvaTrackedCount = 0;
3254 lvaTrackedCountInSizeTUnits = 0;
3257 VarSetOps::AssignNoCopy(this, lvaTrackedVars, VarSetOps::MakeEmpty(this));
3270 /* We'll sort the variables by ref count - allocate the sorted table */
3272 lvaRefSorted = refTab = new (this, CMK_LvaTable) LclVarDsc*[lvaCount];
3274 /* Fill in the table used for sorting */
3276 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
3278 /* Append this variable to the table for sorting */
3282 /* If we have JMP, all arguments must have a location
3283 * even if we don't use them inside the method */
3285 if (compJmpOpUsed && varDsc->lvIsParam)
3287 /* ...except when we have varargs and the argument is
3288 passed on the stack. In that case, it's important
3289 for the ref count to be zero, so that we don't attempt
3290 to track them for GC info (which is not possible since we
3291 don't know their offset in the stack). See the assert at the
3292 end of raMarkStkVars and bug #28949 for more info. */
3294 if (!raIsVarargsStackArg(lclNum))
3296 varDsc->incRefCnts(1, this);
3300 /* For now assume we'll be able to track all locals */
3302 varDsc->lvTracked = 1;
3304 /* If the ref count is zero */
3305 if (varDsc->lvRefCnt == 0)
3307 /* Zero ref count, make this untracked */
3308 varDsc->lvTracked = 0;
3309 varDsc->lvRefCntWtd = 0;
3312 #if !defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND)
3313 if (varTypeIsLong(varDsc) && varDsc->lvPromoted)
3315 varDsc->lvTracked = 0;
3317 #endif // !defined(_TARGET_64BIT_) && !defined(LEGACY_BACKEND)
3319 // Variables that are address-exposed, and all struct locals, are never enregistered, or tracked.
3320 // (The struct may be promoted, and its field variables enregistered/tracked, or the VM may "normalize"
3321 // its type so that its not seen by the JIT as a struct.)
3322 // Pinned variables may not be tracked (a condition of the GCInfo representation)
3323 // or enregistered, on x86 -- it is believed that we can enregister pinned (more properly, "pinning")
3324 // references when using the general GC encoding.
3325 if (varDsc->lvAddrExposed)
3327 varDsc->lvTracked = 0;
3328 assert(varDsc->lvType != TYP_STRUCT ||
3329 varDsc->lvDoNotEnregister); // For structs, should have set this when we set lvAddrExposed.
3331 else if (varTypeIsStruct(varDsc))
3333 // Promoted structs will never be considered for enregistration anyway,
3334 // and the DoNotEnregister flag was used to indicate whether promotion was
3335 // independent or dependent.
3336 if (varDsc->lvPromoted)
3338 varDsc->lvTracked = 0;
3340 else if ((varDsc->lvType == TYP_STRUCT) && !varDsc->lvRegStruct)
3342 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct));
3345 else if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT))
3347 // SSA must exclude struct fields that are not independently promoted
3348 // as dependent fields could be assigned using a CopyBlock
3349 // resulting in a single node causing multiple SSA definitions
3350 // which isn't currently supported by SSA
3352 // TODO-CQ: Consider using lvLclBlockOpAddr and only marking these LclVars
3353 // untracked when a blockOp is used to assign the struct.
3355 varDsc->lvTracked = 0; // so, don't mark as tracked
3356 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_DepField));
3358 else if (varDsc->lvPinned)
3360 varDsc->lvTracked = 0;
3361 #ifdef JIT32_GCENCODER
3362 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_PinningRef));
3365 #if !defined(JIT32_GCENCODER) || !defined(LEGACY_BACKEND)
3366 else if (opts.MinOpts() && !JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet()))
3368 varDsc->lvTracked = 0;
3369 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_MinOptsGC));
3371 else if ((opts.compFlags & CLFLG_REGVAR) == 0)
3373 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_NoRegVars));
3375 #endif // !defined(JIT32_GCENCODER) || !defined(LEGACY_BACKEND)
3376 #if defined(JIT32_GCENCODER) && defined(WIN64EXCEPTIONS)
3377 else if (lvaIsOriginalThisArg(lclNum) && (info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0)
3379 // For x86/Linux, we need to track "this".
3380 // However we cannot have it in tracked variables, so we set "this" pointer always untracked
3381 varDsc->lvTracked = 0;
3385 // Are we not optimizing and we have exception handlers?
3386 // if so mark all args and locals "do not enregister".
3388 if (opts.MinOpts() && compHndBBtabCount > 0)
3390 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_LiveInOutOfHandler));
3394 var_types type = genActualType(varDsc->TypeGet());
3398 #if CPU_HAS_FP_SUPPORT
3411 #endif // FEATURE_SIMD
3417 noway_assert(!"lvType not set correctly");
3418 varDsc->lvType = TYP_INT;
3423 varDsc->lvTracked = 0;
3427 /* Now sort the variable table by ref-count */
3431 /* Decide which variables will be worth tracking */
3433 if (lvaCount > lclMAX_TRACKED)
3435 /* Mark all variables past the first 'lclMAX_TRACKED' as untracked */
3437 for (lclNum = lclMAX_TRACKED; lclNum < lvaCount; lclNum++)
3439 lvaRefSorted[lclNum]->lvTracked = 0;
3444 // Re-Initialize to -1 for safety in debug build.
3445 memset(lvaTrackedToVarNum, -1, sizeof(lvaTrackedToVarNum));
3448 /* Assign indices to all the variables we've decided to track */
3450 for (lclNum = 0; lclNum < min(lvaCount, lclMAX_TRACKED); lclNum++)
3452 varDsc = lvaRefSorted[lclNum];
3453 if (varDsc->lvTracked)
3455 noway_assert(varDsc->lvRefCnt > 0);
3457 /* This variable will be tracked - assign it an index */
3459 lvaTrackedToVarNum[lvaTrackedCount] = (unsigned)(varDsc - lvaTable); // The type of varDsc and lvaTable
3460 // is LclVarDsc. Subtraction will give us
3462 varDsc->lvVarIndex = lvaTrackedCount++;
3466 // We have a new epoch, and also cache the tracked var count in terms of size_t's sufficient to hold that many bits.
3468 lvaTrackedCountInSizeTUnits = unsigned(roundUp(lvaTrackedCount, sizeof(size_t) * 8)) / unsigned(sizeof(size_t) * 8);
3471 VarSetOps::AssignNoCopy(this, lvaTrackedVars, VarSetOps::MakeFull(this));
3476 /*****************************************************************************
3478 * This is called by lvaMarkLclRefs to disqualify a variable from being
3479 * considered by optAddCopies()
3481 void LclVarDsc::lvaDisqualifyVar()
3483 this->lvDisqualify = true;
3484 this->lvSingleDef = false;
3485 this->lvDefStmt = nullptr;
3487 #endif // ASSERTION_PROP
3489 /**********************************************************************************
3490 * Get stack size of the varDsc.
3492 const size_t LclVarDsc::lvArgStackSize() const
3494 // Make sure this will have a stack size
3495 assert(!this->lvIsRegArg);
3497 size_t stackSize = 0;
3498 if (varTypeIsStruct(this))
3500 #if defined(WINDOWS_AMD64_ABI)
3501 // Structs are either passed by reference or can be passed by value using one pointer
3502 stackSize = TARGET_POINTER_SIZE;
3503 #elif defined(_TARGET_ARM64_) || defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
3504 // lvSize performs a roundup.
3505 stackSize = this->lvSize();
3507 #if defined(_TARGET_ARM64_)
3508 if ((stackSize > TARGET_POINTER_SIZE * 2) && (!this->lvIsHfa()))
3510 // If the size is greater than 16 bytes then it will
3511 // be passed by reference.
3512 stackSize = TARGET_POINTER_SIZE;
3514 #endif // defined(_TARGET_ARM64_)
3516 #else // !_TARGET_ARM64_ !WINDOWS_AMD64_ABI !FEATURE_UNIX_AMD64_STRUCT_PASSING
3518 NYI("Unsupported target.");
3521 #endif // !_TARGET_ARM64_ !WINDOWS_AMD64_ABI !FEATURE_UNIX_AMD64_STRUCT_PASSING
3525 stackSize = TARGET_POINTER_SIZE;
3531 #ifndef LEGACY_BACKEND
3532 /**********************************************************************************
3533 * Get type of a variable when passed as an argument.
3535 var_types LclVarDsc::lvaArgType()
3537 var_types type = TypeGet();
3539 #ifdef _TARGET_AMD64_
3540 #ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
3541 if (type == TYP_STRUCT)
3545 #else //! FEATURE_UNIX_AMD64_STRUCT_PASSING
3546 if (type == TYP_STRUCT)
3548 switch (lvExactSize)
3560 switch (*lvGcLayout)
3584 #endif // !FEATURE_UNIX_AMD64_STRUCT_PASSING
3585 #elif defined(_TARGET_ARM64_)
3586 if (type == TYP_STRUCT)
3590 #elif defined(_TARGET_X86_)
3591 // Nothing to do; use the type as is.
3594 #endif //_TARGET_AMD64_
3598 #endif // !LEGACY_BACKEND
3600 /*****************************************************************************
3602 * This is called by lvaMarkLclRefsCallback() to do variable ref marking
3605 void Compiler::lvaMarkLclRefs(GenTree* tree)
3607 /* Is this a call to unmanaged code ? */
3608 if (tree->gtOper == GT_CALL && tree->gtFlags & GTF_CALL_UNMANAGED)
3610 assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
3611 if (!opts.ShouldUsePInvokeHelpers())
3613 /* Get the special variable descriptor */
3615 unsigned lclNum = info.compLvFrameListRoot;
3617 noway_assert(lclNum <= lvaCount);
3618 LclVarDsc* varDsc = lvaTable + lclNum;
3620 /* Increment the ref counts twice */
3621 varDsc->incRefCnts(lvaMarkRefsWeight, this);
3622 varDsc->incRefCnts(lvaMarkRefsWeight, this);
3626 /* Is this an assigment? */
3628 if (tree->OperIsAssignment())
3630 GenTree* op1 = tree->gtOp.gtOp1;
3631 GenTree* op2 = tree->gtOp.gtOp2;
3633 /* Set target register for RHS local if assignment is of a "small" type */
3635 if (varTypeIsByte(tree->gtType))
3638 LclVarDsc* varDsc = nullptr;
3640 #ifdef LEGACY_BACKEND
3641 /* GT_CHS is special it doesn't have a valid op2 */
3642 if (tree->gtOper == GT_CHS)
3644 if (op1->gtOper == GT_LCL_VAR)
3646 lclNum = op1->gtLclVarCommon.gtLclNum;
3647 noway_assert(lclNum < lvaCount);
3648 varDsc = &lvaTable[lclNum];
3654 if (op2->gtOper == GT_LCL_VAR)
3656 lclNum = op2->gtLclVarCommon.gtLclNum;
3657 noway_assert(lclNum < lvaCount);
3658 varDsc = &lvaTable[lclNum];
3661 #if CPU_HAS_BYTE_REGS
3663 varDsc->addPrefReg(RBM_BYTE_REG_FLAG, this);
3669 /* Is this an assignment to a local variable? */
3671 if (op1->gtOper == GT_LCL_VAR && op2->gtType != TYP_BOOL)
3673 /* Only simple assignments allowed for booleans */
3675 if (tree->gtOper != GT_ASG)
3680 /* Is the RHS clearly a boolean value? */
3682 switch (op2->gtOper)
3688 if (op2->gtIntCon.gtIconVal == 0)
3692 if (op2->gtIntCon.gtIconVal == 1)
3697 // Not 0 or 1, fall through ....
3702 if (op2->OperIsCompare())
3709 lclNum = op1->gtLclVarCommon.gtLclNum;
3710 noway_assert(lclNum < lvaCount);
3712 lvaTable[lclNum].lvIsBoolean = false;
3719 #ifdef _TARGET_XARCH_
3720 /* Special case: integer shift node by a variable amount */
3722 if (tree->OperIsShiftOrRotate())
3724 if (tree->gtType == TYP_INT)
3726 GenTree* op2 = tree->gtOp.gtOp2;
3728 if (op2->gtOper == GT_LCL_VAR)
3730 unsigned lclNum = op2->gtLclVarCommon.gtLclNum;
3731 assert(lclNum < lvaCount);
3732 lvaTable[lclNum].setPrefReg(REG_ECX, this);
3740 if ((tree->gtOper != GT_LCL_VAR) && (tree->gtOper != GT_LCL_FLD))
3745 /* This must be a local variable reference */
3747 assert((tree->gtOper == GT_LCL_VAR) || (tree->gtOper == GT_LCL_FLD));
3748 unsigned lclNum = tree->gtLclVarCommon.gtLclNum;
3750 noway_assert(lclNum < lvaCount);
3751 LclVarDsc* varDsc = lvaTable + lclNum;
3753 /* Increment the reference counts */
3755 varDsc->incRefCnts(lvaMarkRefsWeight, this);
3757 if (lvaVarAddrExposed(lclNum))
3759 varDsc->lvIsBoolean = false;
3762 if (tree->gtOper == GT_LCL_FLD)
3765 // variables that have uses inside a GT_LCL_FLD
3766 // cause problems, so we will disqualify them here
3767 varDsc->lvaDisqualifyVar();
3768 #endif // ASSERTION_PROP
3773 if (fgDomsComputed && IsDominatedByExceptionalEntry(lvaMarkRefsCurBlock))
3775 SetVolatileHint(varDsc);
3778 /* Record if the variable has a single def or not */
3780 if (!varDsc->lvDisqualify) // If this variable is already disqualified we can skip this
3782 if (tree->gtFlags & GTF_VAR_DEF) // Is this is a def of our variable
3785 If we have one of these cases:
3786 1. We have already seen a definition (i.e lvSingleDef is true)
3787 2. or info.CompInitMem is true (thus this would be the second definition)
3788 3. or we have an assignment inside QMARK-COLON trees
3789 4. or we have an update form of assignment (i.e. +=, -=, *=)
3790 Then we must disqualify this variable for use in optAddCopies()
3792 Note that all parameters start out with lvSingleDef set to true
3794 if ((varDsc->lvSingleDef == true) || (info.compInitMem == true) || (tree->gtFlags & GTF_COLON_COND) ||
3795 (tree->gtFlags & GTF_VAR_USEASG))
3797 varDsc->lvaDisqualifyVar();
3801 varDsc->lvSingleDef = true;
3802 varDsc->lvDefStmt = lvaMarkRefsCurStmt;
3805 else // otherwise this is a ref of our variable
3807 if (BlockSetOps::MayBeUninit(varDsc->lvRefBlks))
3809 // Lazy initialization
3810 BlockSetOps::AssignNoCopy(this, varDsc->lvRefBlks, BlockSetOps::MakeEmpty(this));
3812 BlockSetOps::AddElemD(this, varDsc->lvRefBlks, lvaMarkRefsCurBlock->bbNum);
3815 #endif // ASSERTION_PROP
3817 bool allowStructs = false;
3818 #ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
3819 // On System V the type of the var could be a struct type.
3820 allowStructs = varTypeIsStruct(varDsc);
3821 #endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
3823 /* Variables must be used as the same type throughout the method */
3824 noway_assert(tiVerificationNeeded || varDsc->lvType == TYP_UNDEF || tree->gtType == TYP_UNKNOWN || allowStructs ||
3825 genActualType(varDsc->TypeGet()) == genActualType(tree->gtType) ||
3826 (tree->gtType == TYP_BYREF && varDsc->TypeGet() == TYP_I_IMPL) ||
3827 (tree->gtType == TYP_I_IMPL && varDsc->TypeGet() == TYP_BYREF) || (tree->gtFlags & GTF_VAR_CAST) ||
3828 varTypeIsFloating(varDsc->TypeGet()) && varTypeIsFloating(tree->gtType));
3830 /* Remember the type of the reference */
3832 if (tree->gtType == TYP_UNKNOWN || varDsc->lvType == TYP_UNDEF)
3834 varDsc->lvType = tree->gtType;
3835 noway_assert(genActualType(varDsc->TypeGet()) == tree->gtType); // no truncation
3839 if (tree->gtFlags & GTF_VAR_CAST)
3841 // it should never be bigger than the variable slot
3843 // Trees don't store the full information about structs
3844 // so we can't check them.
3845 if (tree->TypeGet() != TYP_STRUCT)
3847 unsigned treeSize = genTypeSize(tree->TypeGet());
3848 unsigned varSize = genTypeSize(varDsc->TypeGet());
3849 if (varDsc->TypeGet() == TYP_STRUCT)
3851 varSize = varDsc->lvSize();
3854 assert(treeSize <= varSize);
3860 //------------------------------------------------------------------------
3861 // IsDominatedByExceptionalEntry: Check is the block dominated by an exception entry block.
3864 // block - the checking block.
3866 bool Compiler::IsDominatedByExceptionalEntry(BasicBlock* block)
3868 assert(fgDomsComputed);
3869 return block->IsDominatedByExceptionalEntryFlag();
3872 //------------------------------------------------------------------------
3873 // SetVolatileHint: Set a local var's volatile hint.
3876 // varDsc - the local variable that needs the hint.
3878 void Compiler::SetVolatileHint(LclVarDsc* varDsc)
3880 varDsc->lvVolatileHint = true;
3883 /*****************************************************************************
3885 * Update the local variable reference counts for one basic block
3888 void Compiler::lvaMarkLocalVars(BasicBlock* block)
3890 class MarkLocalVarsVisitor final : public GenTreeVisitor<MarkLocalVarsVisitor>
3898 MarkLocalVarsVisitor(Compiler* compiler) : GenTreeVisitor<MarkLocalVarsVisitor>(compiler)
3902 Compiler::fgWalkResult PreOrderVisit(GenTree** use, GenTree* user)
3904 m_compiler->lvaMarkLclRefs(*use);
3905 return WALK_CONTINUE;
3910 lvaMarkRefsCurBlock = block;
3912 lvaMarkRefsWeight = block->getBBWeight(this);
3917 printf("\n*** marking local variables in block BB%02u (weight=%s)\n", block->bbNum,
3918 refCntWtd2str(lvaMarkRefsWeight));
3922 MarkLocalVarsVisitor visitor(this);
3923 for (GenTree* tree = block->FirstNonPhiDef(); tree; tree = tree->gtNext)
3925 assert(tree->gtOper == GT_STMT);
3928 lvaMarkRefsCurStmt = tree;
3938 visitor.WalkTree(&tree->gtStmt.gtStmtExpr, nullptr);
3942 /*****************************************************************************
3944 * Create the local variable table and compute local variable reference
3948 void Compiler::lvaMarkLocalVars()
3954 printf("\n*************** In lvaMarkLocalVars()");
3958 /* If there is a call to an unmanaged target, we already grabbed a
3959 local slot for the current thread control block.
3962 if (info.compCallUnmanaged != 0)
3964 assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
3965 if (!opts.ShouldUsePInvokeHelpers())
3967 noway_assert(info.compLvFrameListRoot >= info.compLocalsCount && info.compLvFrameListRoot < lvaCount);
3969 lvaTable[info.compLvFrameListRoot].lvType = TYP_I_IMPL;
3971 /* Set the refCnt, it is used in the prolog and return block(s) */
3973 lvaTable[info.compLvFrameListRoot].lvRefCnt = 2;
3974 lvaTable[info.compLvFrameListRoot].lvRefCntWtd = 2 * BB_UNITY_WEIGHT;
3978 lvaAllocOutgoingArgSpaceVar();
3980 #if !FEATURE_EH_FUNCLETS
3982 // Grab space for exception handling
3984 if (ehNeedsShadowSPslots())
3986 // The first slot is reserved for ICodeManager::FixContext(ppEndRegion)
3987 // ie. the offset of the end-of-last-executed-filter
3988 unsigned slotsNeeded = 1;
3990 unsigned handlerNestingLevel = ehMaxHndNestingCount;
3992 if (opts.compDbgEnC && (handlerNestingLevel < (unsigned)MAX_EnC_HANDLER_NESTING_LEVEL))
3993 handlerNestingLevel = (unsigned)MAX_EnC_HANDLER_NESTING_LEVEL;
3995 slotsNeeded += handlerNestingLevel;
3997 // For a filter (which can be active at the same time as a catch/finally handler)
3999 // For zero-termination of the shadow-Stack-pointer chain
4002 lvaShadowSPslotsVar = lvaGrabTempWithImplicitUse(false DEBUGARG("lvaShadowSPslotsVar"));
4003 LclVarDsc* shadowSPslotsVar = &lvaTable[lvaShadowSPslotsVar];
4004 shadowSPslotsVar->lvType = TYP_BLK;
4005 shadowSPslotsVar->lvExactSize = (slotsNeeded * TARGET_POINTER_SIZE);
4008 #endif // !FEATURE_EH_FUNCLETS
4010 // PSPSym and LocAllocSPvar are not used by the CoreRT ABI
4011 if (!IsTargetAbi(CORINFO_CORERT_ABI))
4013 #if FEATURE_EH_FUNCLETS
4014 if (ehNeedsPSPSym())
4016 lvaPSPSym = lvaGrabTempWithImplicitUse(false DEBUGARG("PSPSym"));
4017 LclVarDsc* lclPSPSym = &lvaTable[lvaPSPSym];
4018 lclPSPSym->lvType = TYP_I_IMPL;
4020 #endif // FEATURE_EH_FUNCLETS
4022 // TODO: LocAllocSPvar should be only required by the implicit frame layout expected by the VM on x86.
4023 // It should be removed on other platforms once we check there are no other implicit dependencies.
4024 if (compLocallocUsed)
4026 lvaLocAllocSPvar = lvaGrabTempWithImplicitUse(false DEBUGARG("LocAllocSPvar"));
4027 LclVarDsc* locAllocSPvar = &lvaTable[lvaLocAllocSPvar];
4028 locAllocSPvar->lvType = TYP_I_IMPL;
4035 // Assign slot numbers to all variables.
4036 // If compiler generated local variables, slot numbers will be
4037 // invalid (out of range of info.compVarScopes).
4039 // Also have to check if variable was not reallocated to another
4040 // slot in which case we have to register the original slot #.
4042 // We don't need to do this for IL, but this keeps lvSlotNum consistent.
4044 if (opts.compScopeInfo && (info.compVarScopesCount > 0))
4050 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4052 varDsc->lvSlotNum = lclNum;
4056 /* Mark all local variable references */
4058 lvaRefCountingStarted = true;
4059 for (block = fgFirstBB; block; block = block->bbNext)
4061 lvaMarkLocalVars(block);
4064 /* For incoming register arguments, if there are references in the body
4065 * then we will have to copy them to the final home in the prolog
4066 * This counts as an extra reference with a weight of 2
4072 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4074 if (lclNum >= info.compArgsCount)
4076 break; // early exit for loop
4079 if ((varDsc->lvIsRegArg) && (varDsc->lvRefCnt > 0))
4081 // Fix 388376 ARM JitStress WP7
4082 varDsc->incRefCnts(BB_UNITY_WEIGHT, this);
4083 varDsc->incRefCnts(BB_UNITY_WEIGHT, this);
4088 if (!opts.MinOpts() && !opts.compDbgCode)
4090 // Note: optAddCopies() depends on lvaRefBlks, which is set in lvaMarkLocalVars(BasicBlock*), called above.
4095 if (lvaKeepAliveAndReportThis() && lvaTable[0].lvRefCnt == 0)
4097 lvaTable[0].lvRefCnt = 1;
4098 // This isn't strictly needed as we will make a copy of the param-type-arg
4099 // in the prolog. However, this ensures that the LclVarDsc corresponding to
4100 // info.compTypeCtxtArg is valid.
4102 else if (lvaReportParamTypeArg() && lvaTable[info.compTypeCtxtArg].lvRefCnt == 0)
4104 lvaTable[info.compTypeCtxtArg].lvRefCnt = 1;
4107 lvaLocalVarRefCounted = true;
4108 lvaRefCountingStarted = false;
4110 lvaSortByRefCount();
4113 void Compiler::lvaAllocOutgoingArgSpaceVar()
4115 #if FEATURE_FIXED_OUT_ARGS
4117 // Setup the outgoing argument region, in case we end up using it later
4119 if (lvaOutgoingArgSpaceVar == BAD_VAR_NUM)
4121 lvaOutgoingArgSpaceVar = lvaGrabTemp(false DEBUGARG("OutgoingArgSpace"));
4123 lvaTable[lvaOutgoingArgSpaceVar].lvType = TYP_LCLBLK;
4125 /* Set the refCnts */
4127 lvaTable[lvaOutgoingArgSpaceVar].lvRefCnt = 1;
4128 lvaTable[lvaOutgoingArgSpaceVar].lvRefCntWtd = BB_UNITY_WEIGHT;
4131 noway_assert(lvaOutgoingArgSpaceVar >= info.compLocalsCount && lvaOutgoingArgSpaceVar < lvaCount);
4133 #endif // FEATURE_FIXED_OUT_ARGS
4136 inline void Compiler::lvaIncrementFrameSize(unsigned size)
4138 if (size > MAX_FrameSize || compLclFrameSize + size > MAX_FrameSize)
4140 BADCODE("Frame size overflow");
4143 compLclFrameSize += size;
4146 /****************************************************************************
4148 * Return true if absolute offsets of temps are larger than vars, or in other
4149 * words, did we allocate temps before of after vars. The /GS buffer overrun
4150 * checks want temps to be at low stack addresses than buffers
4152 bool Compiler::lvaTempsHaveLargerOffsetThanVars()
4155 // We never want to place the temps with larger offsets for ARM
4158 if (compGSReorderStackLayout)
4160 return codeGen->isFramePointerUsed();
4169 /****************************************************************************
4171 * Return an upper bound estimate for the size of the compiler spill temps
4174 unsigned Compiler::lvaGetMaxSpillTempSize()
4176 unsigned result = 0;
4178 #ifndef LEGACY_BACKEND
4179 if (lvaDoneFrameLayout >= REGALLOC_FRAME_LAYOUT)
4185 result = MAX_SPILL_TEMP_SIZE;
4187 #else // LEGACY_BACKEND
4188 if (lvaDoneFrameLayout >= FINAL_FRAME_LAYOUT)
4194 if (lvaDoneFrameLayout >= REGALLOC_FRAME_LAYOUT)
4196 unsigned maxTmpSize = sizeof(double) + sizeof(int);
4198 maxTmpSize += (tmpDoubleSpillMax * sizeof(double)) + (tmpIntSpillMax * sizeof(int));
4200 result = maxTmpSize;
4204 result = MAX_SPILL_TEMP_SIZE;
4207 // When StressRegs is >=1, there can be a bunch of spills that are not
4208 // predicted by the predictor (see logic in rsPickReg). It is very hard
4209 // to teach the predictor about the behavior of rsPickReg for StressRegs >= 1,
4210 // so instead let's make MaxTmpSize large enough so that we won't be wrong.
4212 if (codeGen->regSet.rsStressRegs() >= 1)
4214 result += (REG_TMP_ORDER_COUNT * REGSIZE_BYTES);
4218 #endif // LEGACY_BACKEND
4223 /*****************************************************************************
4225 * Compute stack frame offsets for arguments, locals and optionally temps.
4227 * The frame is laid out as follows for x86:
4232 * |-----------------------|
4235 * |-----------------------| <---- Virtual '0'
4236 * | return address |
4237 * +=======================+
4238 * |Callee saved registers |
4239 * |-----------------------|
4241 * |-----------------------|
4243 * |-----------------------| <---- Ambient ESP
4244 * | Arguments for the |
4256 * |-----------------------|
4259 * |-----------------------| <---- Virtual '0'
4260 * | return address |
4261 * +=======================+
4263 * |-----------------------| <---- EBP
4264 * |Callee saved registers |
4265 * |-----------------------|
4266 * | security object |
4267 * |-----------------------|
4269 * |-----------------------|
4270 * | Last-executed-filter |
4271 * |-----------------------|
4275 * |-----------------------|
4279 * ~-----------------------|
4281 * |-----------------------|
4283 * |-----------------------| <---- Ambient ESP
4284 * | Arguments for the |
4293 * The frame is laid out as follows for x64:
4297 * |-----------------------|
4300 * |-----------------------|
4301 * | 4 fixed incoming |
4302 * | argument slots |
4303 * |-----------------------| <---- Caller's SP & Virtual '0'
4304 * | return address |
4305 * +=======================+
4306 * | Callee saved Int regs |
4307 * -------------------------
4308 * | Padding | <---- this padding (0 or 8 bytes) is to ensure flt registers are saved at a mem location aligned at 16-bytes
4309 * | | so that we can save 128-bit callee saved xmm regs using performant "movaps" instruction instead of "movups"
4310 * -------------------------
4311 * | Callee saved Flt regs | <----- entire 128-bits of callee saved xmm registers are stored here
4312 * |-----------------------|
4314 * |-----------------------|
4316 * |-----------------------|
4317 * | Arguments for the |
4320 * |-----------------------|
4321 * | 4 fixed outgoing |
4322 * | argument slots |
4323 * |-----------------------| <---- Ambient RSP
4332 * |-----------------------|
4335 * |-----------------------|
4336 * | 4 fixed incoming |
4337 * | argument slots |
4338 * |-----------------------| <---- Caller's SP & Virtual '0'
4339 * | return address |
4340 * +=======================+
4341 * | Callee saved Int regs |
4342 * -------------------------
4344 * -------------------------
4345 * | Callee saved Flt regs |
4346 * |-----------------------|
4347 * | security object |
4348 * |-----------------------|
4350 * |-----------------------|
4356 * |-----------------------|
4358 * |-----------------------|
4360 * ~ localloc ~ // not in frames with EH
4362 * |-----------------------|
4363 * | PSPSym | // only in frames with EH (thus no localloc)
4365 * |-----------------------| <---- RBP in localloc frames (max 240 bytes from Initial-SP)
4366 * | Arguments for the |
4369 * |-----------------------|
4370 * | 4 fixed outgoing |
4371 * | argument slots |
4372 * |-----------------------| <---- Ambient RSP (before localloc, this is Initial-SP)
4379 * The frame is laid out as follows for ARM (this is a general picture; details may differ for different conditions):
4383 * |-----------------------|
4386 * +=======================+ <---- Caller's SP
4387 * | Pre-spill registers |
4388 * |-----------------------| <---- Virtual '0'
4389 * |Callee saved registers |
4390 * |-----------------------|
4391 * ~ possible double align ~
4392 * |-----------------------|
4393 * | security object |
4394 * |-----------------------|
4396 * |-----------------------|
4397 * | possible GS cookie |
4398 * |-----------------------|
4400 * |-----------------------|
4401 * | possible GS cookie |
4402 * |-----------------------|
4404 * |-----------------------|
4405 * | Stub Argument Var |
4406 * |-----------------------|
4407 * |Inlined PInvoke Frame V|
4408 * |-----------------------|
4409 * ~ possible double align ~
4410 * |-----------------------|
4411 * | Arguments for the |
4414 * |-----------------------| <---- Ambient SP
4423 * |-----------------------|
4426 * +=======================+ <---- Caller's SP
4427 * | Pre-spill registers |
4428 * |-----------------------| <---- Virtual '0'
4429 * |Callee saved registers |
4430 * |-----------------------|
4431 * | PSPSym | // Only for frames with EH, which means FP-based frames
4432 * |-----------------------|
4433 * ~ possible double align ~
4434 * |-----------------------|
4435 * | security object |
4436 * |-----------------------|
4438 * |-----------------------|
4439 * | possible GS cookie |
4440 * |-----------------------|
4442 * |-----------------------|
4443 * | possible GS cookie |
4444 * |-----------------------|
4446 * |-----------------------|
4447 * | Stub Argument Var |
4448 * |-----------------------|
4449 * |Inlined PInvoke Frame V|
4450 * |-----------------------|
4451 * ~ possible double align ~
4452 * |-----------------------|
4454 * |-----------------------|
4455 * | Arguments for the |
4458 * |-----------------------| <---- Ambient SP
4465 * The frame is laid out as follows for ARM64 (this is a general picture; details may differ for different conditions):
4466 * TODO-ARM64-NYI: this is preliminary (copied from ARM and modified), and needs to be reviewed.
4467 * NOTE: SP must be 16-byte aligned, so there may be alignment slots in the frame.
4468 * We will often save and establish a frame pointer to create better ETW stack walks.
4472 * |-----------------------|
4475 * +=======================+ <---- Caller's SP
4476 * | homed | // this is only needed if reg argument need to be homed, e.g., for varargs
4477 * | register arguments |
4478 * |-----------------------| <---- Virtual '0'
4479 * |Callee saved registers |
4481 * |-----------------------|
4482 * | security object |
4483 * |-----------------------|
4485 * |-----------------------|
4486 * | possible GS cookie |
4487 * |-----------------------|
4489 * |-----------------------|
4490 * | possible GS cookie |
4491 * |-----------------------|
4493 * |-----------------------|
4494 * | Stub Argument Var |
4495 * |-----------------------|
4496 * |Inlined PInvoke Frame V|
4497 * |-----------------------|
4499 * |-----------------------|
4500 * | Saved FP | <---- Frame pointer
4501 * |-----------------------|
4502 * | Stack arguments for |
4503 * | the next function |
4504 * |-----------------------| <---- SP
4511 * FP (R29 / x29) frames
4513 * |-----------------------|
4516 * +=======================+ <---- Caller's SP
4517 * | optional homed | // this is only needed if reg argument need to be homed, e.g., for varargs
4518 * | register arguments |
4519 * |-----------------------| <---- Virtual '0'
4520 * |Callee saved registers |
4522 * |-----------------------|
4523 * | PSPSym | // Only for frames with EH, which requires FP-based frames
4524 * |-----------------------|
4525 * | security object |
4526 * |-----------------------|
4528 * |-----------------------|
4529 * | possible GS cookie |
4530 * |-----------------------|
4532 * |-----------------------|
4533 * | possible GS cookie |
4534 * |-----------------------|
4536 * |-----------------------|
4537 * | Stub Argument Var |
4538 * |-----------------------|
4539 * |Inlined PInvoke Frame V|
4540 * |-----------------------|
4542 * |-----------------------|
4543 * | Saved FP | <---- Frame pointer
4544 * |-----------------------|
4546 * |-----------------------|
4547 * | Stack arguments for |
4548 * | the next function |
4549 * |-----------------------| <---- Ambient SP
4556 * Doing this all in one pass is 'hard'. So instead we do it in 2 basic passes:
4557 * 1. Assign all the offsets relative to the Virtual '0'. Offsets above (the
4558 * incoming arguments) are positive. Offsets below (everything else) are
4559 * negative. This pass also calcuates the total frame size (between Caller's
4560 * SP/return address and the Ambient SP).
4561 * 2. Figure out where to place the frame pointer, and then adjust the offsets
4562 * as needed for the final stack size and whether the offset is frame pointer
4563 * relative or stack pointer relative.
4568 void Compiler::lvaAssignFrameOffsets(FrameLayoutState curState)
4570 noway_assert((lvaDoneFrameLayout < curState) || (curState == REGALLOC_FRAME_LAYOUT));
4572 lvaDoneFrameLayout = curState;
4578 printf("*************** In lvaAssignFrameOffsets");
4579 if (curState == INITIAL_FRAME_LAYOUT)
4581 printf("(INITIAL_FRAME_LAYOUT)");
4583 else if (curState == PRE_REGALLOC_FRAME_LAYOUT)
4585 printf("(PRE_REGALLOC_FRAME_LAYOUT)");
4587 else if (curState == REGALLOC_FRAME_LAYOUT)
4589 printf("(REGALLOC_FRAME_LAYOUT)");
4591 else if (curState == TENTATIVE_FRAME_LAYOUT)
4593 printf("(TENTATIVE_FRAME_LAYOUT)");
4595 else if (curState == FINAL_FRAME_LAYOUT)
4597 printf("(FINAL_FRAME_LAYOUT)");
4601 printf("(UNKNOWN)");
4608 #if FEATURE_FIXED_OUT_ARGS
4609 assert(lvaOutgoingArgSpaceVar != BAD_VAR_NUM);
4610 #endif // FEATURE_FIXED_OUT_ARGS
4612 /*-------------------------------------------------------------------------
4614 * First process the arguments.
4616 *-------------------------------------------------------------------------
4619 lvaAssignVirtualFrameOffsetsToArgs();
4621 /*-------------------------------------------------------------------------
4623 * Now compute stack offsets for any variables that don't live in registers
4625 *-------------------------------------------------------------------------
4628 lvaAssignVirtualFrameOffsetsToLocals();
4632 /*-------------------------------------------------------------------------
4634 * Now patch the offsets
4636 *-------------------------------------------------------------------------
4639 lvaFixVirtualFrameOffsets();
4641 // Modify the stack offset for fields of promoted structs.
4642 lvaAssignFrameOffsetsToPromotedStructs();
4644 /*-------------------------------------------------------------------------
4648 *-------------------------------------------------------------------------
4651 // If it's not the final frame layout, then it's just an estimate. This means
4652 // we're allowed to once again write to these variables, even if we've read
4653 // from them to make tentative code generation or frame layout decisions.
4654 if (curState < FINAL_FRAME_LAYOUT)
4656 codeGen->resetFramePointerUsedWritePhase();
4660 /*****************************************************************************
4661 * lvaFixVirtualFrameOffsets() : Now that everything has a virtual offset,
4662 * determine the final value for the frame pointer (if needed) and then
4663 * adjust all the offsets appropriately.
4665 * This routine fixes virtual offset to be relative to frame pointer or SP
4666 * based on whether varDsc->lvFramePointerBased is true or false respectively.
4668 void Compiler::lvaFixVirtualFrameOffsets()
4672 #if FEATURE_EH_FUNCLETS && defined(_TARGET_AMD64_)
4673 if (lvaPSPSym != BAD_VAR_NUM)
4675 // We need to fix the offset of the PSPSym so there is no padding between it and the outgoing argument space.
4676 // Without this code, lvaAlignFrame might have put the padding lower than the PSPSym, which would be between
4677 // the PSPSym and the outgoing argument space.
4678 varDsc = &lvaTable[lvaPSPSym];
4679 assert(varDsc->lvFramePointerBased); // We always access it RBP-relative.
4680 assert(!varDsc->lvMustInit); // It is never "must init".
4681 varDsc->lvStkOffs = codeGen->genCallerSPtoInitialSPdelta() + lvaLclSize(lvaOutgoingArgSpaceVar);
4685 // The delta to be added to virtual offset to adjust it relative to frame pointer or SP
4688 #ifdef _TARGET_XARCH_
4689 delta += REGSIZE_BYTES; // pushed PC (return address) for x86/x64
4691 if (codeGen->doubleAlignOrFramePointerUsed())
4693 delta += REGSIZE_BYTES; // pushed EBP (frame pointer)
4697 if (!codeGen->isFramePointerUsed())
4699 // pushed registers, return address, and padding
4700 delta += codeGen->genTotalFrameSize();
4702 #if defined(_TARGET_ARM_)
4705 // We set FP to be after LR, FP
4706 delta += 2 * REGSIZE_BYTES;
4708 #elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
4712 delta += codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta();
4714 #endif //_TARGET_AMD64_
4717 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4719 bool doAssignStkOffs = true;
4721 // Can't be relative to EBP unless we have an EBP
4722 noway_assert(!varDsc->lvFramePointerBased || codeGen->doubleAlignOrFramePointerUsed());
4724 // Is this a non-param promoted struct field?
4725 // if so then set doAssignStkOffs to false.
4727 if (varDsc->lvIsStructField && !varDsc->lvIsParam)
4729 LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl];
4730 lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);
4732 if (promotionType == PROMOTION_TYPE_DEPENDENT)
4734 doAssignStkOffs = false; // Assigned later in lvaAssignFrameOffsetsToPromotedStructs()
4738 if (!varDsc->lvOnFrame)
4740 if (!varDsc->lvIsParam
4741 #if !defined(_TARGET_AMD64_)
4742 || (varDsc->lvIsRegArg
4743 #if defined(_TARGET_ARM_) && defined(PROFILING_SUPPORTED)
4744 && compIsProfilerHookNeeded() &&
4745 !lvaIsPreSpilled(lclNum, codeGen->regSet.rsMaskPreSpillRegs(false)) // We need assign stack offsets
4746 // for prespilled arguments
4749 #endif // !defined(_TARGET_AMD64_)
4752 doAssignStkOffs = false; // Not on frame or an incomming stack arg
4756 if (doAssignStkOffs)
4758 varDsc->lvStkOffs += delta;
4761 if (genDoubleAlign() && !codeGen->isFramePointerUsed())
4763 if (varDsc->lvFramePointerBased)
4765 varDsc->lvStkOffs -= delta;
4767 // We need to re-adjust the offsets of the parameters so they are EBP
4768 // relative rather than stack/frame pointer relative
4770 varDsc->lvStkOffs += (2 * TARGET_POINTER_SIZE); // return address and pushed EBP
4772 noway_assert(varDsc->lvStkOffs >= FIRST_ARG_STACK_OFFS);
4776 // On System V environments the stkOffs could be 0 for params passed in registers.
4777 assert(codeGen->isFramePointerUsed() ||
4778 varDsc->lvStkOffs >= 0); // Only EBP relative references can have negative offsets
4782 assert(tmpAllFree());
4783 for (TempDsc* temp = tmpListBeg(); temp != nullptr; temp = tmpListNxt(temp))
4785 temp->tdAdjustTempOffs(delta);
4788 lvaCachedGenericContextArgOffs += delta;
4790 #if FEATURE_FIXED_OUT_ARGS
4792 if (lvaOutgoingArgSpaceVar != BAD_VAR_NUM)
4794 varDsc = &lvaTable[lvaOutgoingArgSpaceVar];
4795 varDsc->lvStkOffs = 0;
4796 varDsc->lvFramePointerBased = false;
4797 varDsc->lvMustInit = false;
4800 #endif // FEATURE_FIXED_OUT_ARGS
4804 bool Compiler::lvaIsPreSpilled(unsigned lclNum, regMaskTP preSpillMask)
4806 const LclVarDsc& desc = lvaTable[lclNum];
4807 return desc.lvIsRegArg && (preSpillMask & genRegMask(desc.lvArgReg));
4809 #endif // _TARGET_ARM_
4811 #ifndef LEGACY_BACKEND
4812 /*****************************************************************************
4813 * lvaUpdateArgsWithInitialReg() : For each argument variable descriptor, update
4814 * its current register with the initial register as assigned by LSRA.
4816 void Compiler::lvaUpdateArgsWithInitialReg()
4823 for (unsigned lclNum = 0; lclNum < info.compArgsCount; lclNum++)
4825 LclVarDsc* varDsc = lvaTable + lclNum;
4827 if (varDsc->lvPromotedStruct())
4829 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
4831 unsigned fieldVarNum = varDsc->lvFieldLclStart;
4832 varDsc = lvaTable + fieldVarNum;
4835 noway_assert(varDsc->lvIsParam);
4837 if (varDsc->lvIsRegCandidate())
4839 if (varTypeIsMultiReg(varDsc))
4841 regPairNo initialRegPair = varDsc->lvArgInitRegPair;
4842 varDsc->lvRegNum = genRegPairLo(initialRegPair);
4843 varDsc->lvOtherReg = genRegPairHi(initialRegPair);
4847 varDsc->lvRegNum = varDsc->lvArgInitReg;
4852 #endif // !LEGACY_BACKEND
4854 /*****************************************************************************
4855 * lvaAssignVirtualFrameOffsetsToArgs() : Assign virtual stack offsets to the
4856 * arguments, and implicit arguments (this ptr, return buffer, generics,
4859 void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
4861 unsigned lclNum = 0;
4863 #ifdef UNIX_AMD64_ABI
4864 int callerArgOffset = 0;
4865 #endif // UNIX_AMD64_ABI
4868 Assign stack offsets to arguments (in reverse order of passing).
4870 This means that if we pass arguments left->right, we start at
4871 the end of the list and work backwards, for right->left we start
4872 with the first argument and move forward.
4874 This is all relative to our Virtual '0'
4877 if (Target::g_tgtArgOrder == Target::ARG_ORDER_L2R)
4879 argOffs = compArgSize;
4882 /* Update the argOffs to reflect arguments that are passed in registers */
4884 noway_assert(codeGen->intRegState.rsCalleeRegArgCount <= MAX_REG_ARG);
4885 noway_assert(compArgSize >= codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES);
4888 argOffs -= codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES;
4891 #ifndef LEGACY_BACKEND
4892 // Update the arg initial register locations.
4893 lvaUpdateArgsWithInitialReg();
4894 #endif // !LEGACY_BACKEND
4896 /* Is there a "this" argument? */
4898 if (!info.compIsStatic)
4900 noway_assert(lclNum == info.compThisArg);
4901 #ifndef _TARGET_X86_
4903 lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
4904 #endif // _TARGET_X86_
4908 /* if we have a hidden buffer parameter, that comes here */
4910 if (info.compRetBuffArg != BAD_VAR_NUM)
4912 noway_assert(lclNum == info.compRetBuffArg);
4913 noway_assert(lvaTable[lclNum].lvIsRegArg);
4914 #ifndef _TARGET_X86_
4916 lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
4917 #endif // _TARGET_X86_
4921 #if USER_ARGS_COME_LAST
4923 //@GENERICS: extra argument for instantiation info
4924 if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
4926 noway_assert(lclNum == (unsigned)info.compTypeCtxtArg);
4927 argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
4928 argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
4931 if (info.compIsVarArgs)
4933 argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
4934 argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
4937 #endif // USER_ARGS_COME_LAST
4939 CORINFO_ARG_LIST_HANDLE argLst = info.compMethodInfo->args.args;
4940 unsigned argSigLen = info.compMethodInfo->args.numArgs;
4944 // struct_n { int; int; ... n times };
4946 // Consider signature:
4948 // Foo (float a,double b,float c,double d,float e,double f,float g,double h,
4949 // float i,double j,float k,double l,struct_3 m) { }
4951 // Basically the signature is: (all float regs full, 1 double, struct_3);
4953 // The double argument occurs before pre spill in the argument iteration and
4954 // computes an argOffset of 0. struct_3 offset becomes 8. This is wrong.
4955 // Because struct_3 is prespilled and double occurs after prespill.
4956 // The correct offsets are double = 16 (aligned stk), struct_3 = 0..12,
4957 // Offset 12 will be skipped for double alignment of double.
4959 // Another example is (struct_2, all float regs full, double, struct_2);
4960 // Here, notice the order is similarly messed up because of 2 pre-spilled
4964 // ARG_INDEX(i) > ARG_INDEX(j) DOES NOT IMPLY |ARG_OFFSET(i)| > |ARG_OFFSET(j)|
4966 // Therefore, we'll do a two pass offset calculation, one that considers pre-spill
4967 // and the next, stack args.
4970 unsigned argLcls = 0;
4972 // Take care of pre spill registers first.
4973 regMaskTP preSpillMask = codeGen->regSet.rsMaskPreSpillRegs(false);
4974 regMaskTP tempMask = RBM_NONE;
4975 for (unsigned i = 0, preSpillLclNum = lclNum; i < argSigLen; ++i, ++preSpillLclNum)
4977 if (lvaIsPreSpilled(preSpillLclNum, preSpillMask))
4979 unsigned argSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
4980 argOffs = lvaAssignVirtualFrameOffsetToArg(preSpillLclNum, argSize, argOffs);
4983 // Early out if we can. If size is 8 and base reg is 2, then the mask is 0x1100
4984 tempMask |= ((((1 << (roundUp(argSize, TARGET_POINTER_SIZE) / REGSIZE_BYTES))) - 1)
4985 << lvaTable[preSpillLclNum].lvArgReg);
4986 if (tempMask == preSpillMask)
4988 // We won't encounter more pre-spilled registers,
4989 // so don't bother iterating further.
4993 argLst = info.compCompHnd->getArgNext(argLst);
4996 // Take care of non pre-spilled stack arguments.
4997 argLst = info.compMethodInfo->args.args;
4998 for (unsigned i = 0, stkLclNum = lclNum; i < argSigLen; ++i, ++stkLclNum)
5000 if (!lvaIsPreSpilled(stkLclNum, preSpillMask))
5003 lvaAssignVirtualFrameOffsetToArg(stkLclNum, eeGetArgSize(argLst, &info.compMethodInfo->args), argOffs);
5006 argLst = info.compCompHnd->getArgNext(argLst);
5010 #else // !_TARGET_ARM_
5011 for (unsigned i = 0; i < argSigLen; i++)
5013 unsigned argumentSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
5015 #ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
5016 // On the stack frame the homed arg always takes a full number of slots
5017 // for proper stack alignment. Make sure the real struct size is properly rounded up.
5018 argumentSize = (unsigned)roundUp(argumentSize, TARGET_POINTER_SIZE);
5019 #endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
5022 lvaAssignVirtualFrameOffsetToArg(lclNum++, argumentSize, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5023 argLst = info.compCompHnd->getArgNext(argLst);
5025 #endif // !_TARGET_ARM_
5027 #if !USER_ARGS_COME_LAST
5029 //@GENERICS: extra argument for instantiation info
5030 if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
5032 noway_assert(lclNum == (unsigned)info.compTypeCtxtArg);
5033 argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
5034 argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5037 if (info.compIsVarArgs)
5039 argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
5040 argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5043 #endif // USER_ARGS_COME_LAST
5046 #ifdef UNIX_AMD64_ABI
5048 // lvaAssignVirtualFrameOffsetToArg() : Assign virtual stack offsets to an
5049 // individual argument, and return the offset for the next argument.
5050 // Note: This method only calculates the initial offset of the stack passed/spilled arguments
5051 // (if any - the RA might decide to spill(home on the stack) register passed arguments, if rarely used.)
5052 // The final offset is calculated in lvaFixVirtualFrameOffsets method. It accounts for FP existance,
5053 // ret address slot, stack frame padding, alloca instructions, etc.
5054 // Note: This is the implementation for UNIX_AMD64 System V platforms.
5056 int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum,
5058 int argOffs UNIX_AMD64_ABI_ONLY_ARG(int* callerArgOffset))
5060 noway_assert(lclNum < info.compArgsCount);
5061 noway_assert(argSize);
5063 if (Target::g_tgtArgOrder == Target::ARG_ORDER_L2R)
5068 unsigned fieldVarNum = BAD_VAR_NUM;
5070 noway_assert(lclNum < lvaCount);
5071 LclVarDsc* varDsc = lvaTable + lclNum;
5073 if (varDsc->lvPromotedStruct())
5075 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
5076 fieldVarNum = varDsc->lvFieldLclStart;
5078 lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
5080 if (promotionType == PROMOTION_TYPE_INDEPENDENT)
5082 lclNum = fieldVarNum;
5083 noway_assert(lclNum < lvaCount);
5084 varDsc = lvaTable + lclNum;
5085 assert(varDsc->lvIsStructField);
5089 noway_assert(varDsc->lvIsParam);
5091 if (varDsc->lvIsRegArg)
5093 // Argument is passed in a register, don't count it
5094 // when updating the current offset on the stack.
5096 if (varDsc->lvOnFrame)
5098 // The offset for args needs to be set only for the stack homed arguments for System V.
5099 varDsc->lvStkOffs = argOffs;
5103 varDsc->lvStkOffs = 0;
5108 // For Windows AMD64 there are 4 slots for the register passed arguments on the top of the caller's stack.
5109 // This is where they are always homed. So, they can be accessed with positive offset.
5110 // On System V platforms, if the RA decides to home a register passed arg on the stack, it creates a stack
5111 // location on the callee stack (like any other local var.) In such a case, the register passed, stack homed
5112 // arguments are accessed using negative offsets and the stack passed arguments are accessed using positive
5113 // offset (from the caller's stack.)
5114 // For System V platforms if there is no frame pointer the caller stack parameter offset should include the
5115 // callee allocated space. If frame register is used, the callee allocated space should not be included for
5116 // accessing the caller stack parameters. The last two requirements are met in lvaFixVirtualFrameOffsets
5117 // method, which fixes the offsets, based on frame pointer existence, existence of alloca instructions, ret
5118 // address pushed, ets.
5120 varDsc->lvStkOffs = *callerArgOffset;
5121 // Structs passed on stack could be of size less than TARGET_POINTER_SIZE.
5122 // Make sure they get at least TARGET_POINTER_SIZE on the stack - this is required for alignment.
5123 if (argSize > TARGET_POINTER_SIZE)
5125 *callerArgOffset += (int)roundUp(argSize, TARGET_POINTER_SIZE);
5129 *callerArgOffset += TARGET_POINTER_SIZE;
5133 // For struct promoted parameters we need to set the offsets for both LclVars.
5135 // For a dependent promoted struct we also assign the struct fields stack offset
5136 if (varDsc->lvPromotedStruct())
5138 lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
5140 if (promotionType == PROMOTION_TYPE_DEPENDENT)
5142 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
5144 assert(fieldVarNum == varDsc->lvFieldLclStart);
5145 lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
5148 // For an independent promoted struct field we also assign the parent struct stack offset
5149 else if (varDsc->lvIsStructField)
5151 noway_assert(varDsc->lvParentLcl < lvaCount);
5152 lvaTable[varDsc->lvParentLcl].lvStkOffs = varDsc->lvStkOffs;
5155 if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L && !varDsc->lvIsRegArg)
5163 #else // !UNIX_AMD64_ABI
5166 // lvaAssignVirtualFrameOffsetToArg() : Assign virtual stack offsets to an
5167 // individual argument, and return the offset for the next argument.
5168 // Note: This method only calculates the initial offset of the stack passed/spilled arguments
5169 // (if any - the RA might decide to spill(home on the stack) register passed arguments, if rarely used.)
5170 // The final offset is calculated in lvaFixVirtualFrameOffsets method. It accounts for FP existance,
5171 // ret address slot, stack frame padding, alloca instructions, etc.
5172 // Note: This implementation for all the platforms but UNIX_AMD64 OSs (System V 64 bit.)
5173 int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum,
5175 int argOffs UNIX_AMD64_ABI_ONLY_ARG(int* callerArgOffset))
5177 noway_assert(lclNum < info.compArgsCount);
5178 noway_assert(argSize);
5180 if (Target::g_tgtArgOrder == Target::ARG_ORDER_L2R)
5185 unsigned fieldVarNum = BAD_VAR_NUM;
5187 noway_assert(lclNum < lvaCount);
5188 LclVarDsc* varDsc = lvaTable + lclNum;
5190 if (varDsc->lvPromotedStruct())
5192 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
5193 fieldVarNum = varDsc->lvFieldLclStart;
5195 lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
5197 if (promotionType == PROMOTION_TYPE_INDEPENDENT)
5199 lclNum = fieldVarNum;
5200 noway_assert(lclNum < lvaCount);
5201 varDsc = lvaTable + lclNum;
5202 assert(varDsc->lvIsStructField);
5206 noway_assert(varDsc->lvIsParam);
5208 if (varDsc->lvIsRegArg)
5210 /* Argument is passed in a register, don't count it
5211 * when updating the current offset on the stack */
5212 CLANG_FORMAT_COMMENT_ANCHOR;
5214 #if !defined(_TARGET_ARMARCH_)
5216 // TODO: Remove this noway_assert and replace occurrences of TARGET_POINTER_SIZE with argSize
5217 // Also investigate why we are incrementing argOffs for X86 as this seems incorrect
5219 noway_assert(argSize == TARGET_POINTER_SIZE);
5223 #if defined(_TARGET_X86_)
5224 argOffs += TARGET_POINTER_SIZE;
5225 #elif defined(_TARGET_AMD64_)
5226 // Register arguments on AMD64 also takes stack space. (in the backing store)
5227 varDsc->lvStkOffs = argOffs;
5228 argOffs += TARGET_POINTER_SIZE;
5229 #elif defined(_TARGET_ARM64_)
5230 // Register arguments on ARM64 only take stack space when they have a frame home.
5231 #elif defined(_TARGET_ARM_)
5232 // On ARM we spill the registers in codeGen->regSet.rsMaskPreSpillRegArg
5233 // in the prolog, so we have to fill in lvStkOffs here
5235 regMaskTP regMask = genRegMask(varDsc->lvArgReg);
5236 if (codeGen->regSet.rsMaskPreSpillRegArg & regMask)
5238 // Signature: void foo(struct_8, int, struct_4)
5239 // ------- CALLER SP -------
5241 // r2 int - not prespilled, but added for alignment. argOffs should skip this.
5244 // -------------------------
5245 // If we added alignment we need to fix argOffs for all registers above alignment.
5246 if (codeGen->regSet.rsMaskPreSpillAlign != RBM_NONE)
5248 assert(genCountBits(codeGen->regSet.rsMaskPreSpillAlign) == 1);
5249 // Is register beyond the alignment pos?
5250 if (regMask > codeGen->regSet.rsMaskPreSpillAlign)
5252 // Increment argOffs just once for the _first_ register after alignment pos
5253 // in the prespill mask.
5254 if (!BitsBetween(codeGen->regSet.rsMaskPreSpillRegArg, regMask,
5255 codeGen->regSet.rsMaskPreSpillAlign))
5257 argOffs += TARGET_POINTER_SIZE;
5262 switch (varDsc->lvType)
5265 if (!varDsc->lvStructDoubleAlign)
5275 // Let's assign offsets to arg1, a double in r2. argOffs has to be 4 not 8.
5277 // ------- CALLER SP -------
5279 // r2 double -- argOffs = 4, but it doesn't need to be skipped, because there is no skipping.
5280 // r1 VACookie -- argOffs = 0
5281 // -------------------------
5283 // Consider argOffs as if it accounts for number of prespilled registers before the current
5284 // register. In the above example, for r2, it is r1 that is prespilled, but since r1 is
5285 // accounted for by argOffs being 4, there should have been no skipping. Instead, if we didn't
5286 // assign r1 to any variable, then argOffs would still be 0 which implies it is not accounting
5287 // for r1, equivalently r1 is skipped.
5289 // If prevRegsSize is unaccounted for by a corresponding argOffs, we must have skipped a register.
5291 genCountBits(codeGen->regSet.rsMaskPreSpillRegArg & (regMask - 1)) * TARGET_POINTER_SIZE;
5292 if (argOffs < prevRegsSize)
5294 // We must align up the argOffset to a multiple of 8 to account for skipped registers.
5295 argOffs = roundUp(argOffs, 2 * TARGET_POINTER_SIZE);
5297 // We should've skipped only a single register.
5298 assert(argOffs == prevRegsSize);
5303 // No alignment of argOffs required
5306 varDsc->lvStkOffs = argOffs;
5310 #error Unsupported or unset target architecture
5315 #if defined(_TARGET_ARM_)
5316 // Dev11 Bug 42817: incorrect codegen for DrawFlatCheckBox causes A/V in WinForms
5318 // Here we have method with a signature (int a1, struct a2, struct a3, int a4, int a5).
5319 // Struct parameter 'a2' is 16-bytes with no alignment requirements;
5320 // it uses r1,r2,r3 and [OutArg+0] when passed.
5321 // Struct parameter 'a3' is 16-bytes that is required to be double aligned;
5322 // the caller skips [OutArg+4] and starts the argument at [OutArg+8].
5323 // Thus the caller generates the correct code to pass the arguments.
5324 // When generating code to receive the arguments we set codeGen->regSet.rsMaskPreSpillRegArg to [r1,r2,r3]
5325 // and spill these three registers as the first instruction in the prolog.
5326 // Then when we layout the arguments' stack offsets we have an argOffs 0 which
5327 // points at the location that we spilled r1 into the stack. For this first
5328 // struct we take the lvIsRegArg path above with "codeGen->regSet.rsMaskPreSpillRegArg &" matching.
5329 // Next when we calculate the argOffs for the second 16-byte struct we have an argOffs
5330 // of 16, which appears to be aligned properly so we don't skip a stack slot.
5332 // To fix this we must recover the actual OutArg offset by subtracting off the
5333 // sizeof of the PreSpill register args.
5334 // Then we align this offset to a multiple of 8 and add back the sizeof
5335 // of the PreSpill register args.
5337 // Dev11 Bug 71767: failure of assert(sizeofPreSpillRegArgs <= argOffs)
5339 // We have a method with 'this' passed in r0, RetBuf arg in r1, VarArgs cookie
5340 // in r2. The first user arg is a 144 byte struct with double alignment required,
5341 // r3 is skipped, and the struct is passed on the stack. However, 'r3' is added
5342 // to the codeGen->regSet.rsMaskPreSpillRegArg mask by the VarArgs cookie code, since we need to
5343 // home all the potential varargs arguments in registers, even if we don't have
5344 // signature type information for the variadic arguments. However, due to alignment,
5345 // we have skipped a register that doesn't have a corresponding symbol. Make up
5346 // for that by increasing argOffs here.
5349 int sizeofPreSpillRegArgs = genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true)) * REGSIZE_BYTES;
5351 if (argOffs < sizeofPreSpillRegArgs)
5353 // This can only happen if we skipped the last register spot because current stk arg
5354 // is a struct requiring alignment or a pre-spill alignment was required because the
5355 // first reg arg needed alignment.
5357 // Example 1: First Stk Argument requiring alignment in vararg case (same as above comment.)
5358 // Signature (int a0, int a1, int a2, struct {long} a3, ...)
5360 // stk arg a3 --> argOffs here will be 12 (r0-r2) but pre-spill will be 16.
5361 // ---- Caller SP ----
5362 // r3 --> Stack slot is skipped in this case.
5367 // Example 2: First Reg Argument requiring alignment in no-vararg case.
5368 // Signature (struct {long} a0, struct {int} a1, int a2, int a3)
5370 // stk arg --> argOffs here will be 12 {r0-r2} but pre-spill will be 16.
5371 // ---- Caller SP ----
5372 // r3 int a2 --> pushed (not pre-spilled) for alignment of a0 by lvaInitUserArgs.
5373 // r2 struct { int } a1
5374 // r0-r1 struct { long } a0
5375 CLANG_FORMAT_COMMENT_ANCHOR;
5377 #ifdef PROFILING_SUPPORTED
5378 // On Arm under profiler, r0-r3 are always prespilled on stack.
5379 // It is possible to have methods that accept only HFAs as parameters e.g. Signature(struct hfa1, struct
5380 // hfa2), in which case hfa1 and hfa2 will be en-registered in co-processor registers and will have an
5381 // argument offset less than size of preSpill.
5383 // For this reason the following conditions are asserted when not under profiler.
5384 if (!compIsProfilerHookNeeded())
5387 bool cond = ((info.compIsVarArgs || opts.compUseSoftFP) &&
5388 // Does cur stk arg require double alignment?
5389 ((varDsc->lvType == TYP_STRUCT && varDsc->lvStructDoubleAlign) ||
5390 (varDsc->lvType == TYP_DOUBLE) || (varDsc->lvType == TYP_LONG))) ||
5391 // Did first reg arg require alignment?
5392 (codeGen->regSet.rsMaskPreSpillAlign & genRegMask(REG_ARG_LAST));
5395 noway_assert(sizeofPreSpillRegArgs <=
5396 argOffs + TARGET_POINTER_SIZE); // at most one register of alignment
5398 argOffs = sizeofPreSpillRegArgs;
5401 noway_assert(argOffs >= sizeofPreSpillRegArgs);
5402 int argOffsWithoutPreSpillRegArgs = argOffs - sizeofPreSpillRegArgs;
5404 switch (varDsc->lvType)
5407 if (!varDsc->lvStructDoubleAlign)
5414 // We must align up the argOffset to a multiple of 8
5415 argOffs = roundUp(argOffsWithoutPreSpillRegArgs, 2 * TARGET_POINTER_SIZE) + sizeofPreSpillRegArgs;
5419 // No alignment of argOffs required
5422 #endif // _TARGET_ARM_
5424 varDsc->lvStkOffs = argOffs;
5427 // For struct promoted parameters we need to set the offsets for both LclVars.
5429 // For a dependent promoted struct we also assign the struct fields stack offset
5430 CLANG_FORMAT_COMMENT_ANCHOR;
5432 #if !defined(_TARGET_64BIT_)
5433 if ((varDsc->TypeGet() == TYP_LONG) && varDsc->lvPromoted)
5435 noway_assert(varDsc->lvFieldCnt == 2);
5436 fieldVarNum = varDsc->lvFieldLclStart;
5437 lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
5438 lvaTable[fieldVarNum + 1].lvStkOffs = varDsc->lvStkOffs + genTypeSize(TYP_INT);
5441 #endif // !defined(_TARGET_64BIT_)
5442 if (varDsc->lvPromotedStruct())
5444 lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
5446 if (promotionType == PROMOTION_TYPE_DEPENDENT)
5448 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
5450 assert(fieldVarNum == varDsc->lvFieldLclStart);
5451 lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
5454 // For an independent promoted struct field we also assign the parent struct stack offset
5455 else if (varDsc->lvIsStructField)
5457 noway_assert(varDsc->lvParentLcl < lvaCount);
5458 lvaTable[varDsc->lvParentLcl].lvStkOffs = varDsc->lvStkOffs;
5461 if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L && !varDsc->lvIsRegArg)
5468 #endif // !UNIX_AMD64_ABI
5470 /*****************************************************************************
5471 * lvaAssignVirtualFrameOffsetsToLocals() : Assign virtual stack offsets to
5472 * locals, temps, and anything else. These will all be negative offsets
5473 * (stack grows down) relative to the virtual '0'/return address
5475 void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
5478 // codeGen->isFramePointerUsed is set in regalloc phase. Initialize it to a guess for pre-regalloc layout.
5479 if (lvaDoneFrameLayout <= PRE_REGALLOC_FRAME_LAYOUT)
5481 codeGen->setFramePointerUsed(codeGen->isFramePointerRequired());
5484 #ifdef _TARGET_XARCH_
5485 // On x86/amd64, the return address has already been pushed by the call instruction in the caller.
5486 stkOffs -= TARGET_POINTER_SIZE; // return address;
5488 // TODO-AMD64-CQ: for X64 eventually this should be pushed with all the other
5489 // calleeregs. When you fix this, you'll also need to fix
5490 // the assert at the bottom of this method
5491 if (codeGen->doubleAlignOrFramePointerUsed())
5493 stkOffs -= REGSIZE_BYTES;
5495 #endif //_TARGET_XARCH_
5497 int preSpillSize = 0;
5498 bool mustDoubleAlign = false;
5501 mustDoubleAlign = true;
5502 preSpillSize = genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true)) * REGSIZE_BYTES;
5503 #else // !_TARGET_ARM_
5505 if (genDoubleAlign())
5507 mustDoubleAlign = true; // X86 only
5510 #endif // !_TARGET_ARM_
5512 #ifdef _TARGET_ARM64_
5513 // If the frame pointer is used, then we'll save FP/LR at the bottom of the stack.
5514 // Otherwise, we won't store FP, and we'll store LR at the top, with the other callee-save
5515 // registers (if any).
5517 int initialStkOffs = 0;
5518 if (info.compIsVarArgs)
5520 // For varargs we always save all of the integer register arguments
5521 // so that they are contiguous with the incoming stack arguments.
5522 initialStkOffs = MAX_REG_ARG * REGSIZE_BYTES;
5523 stkOffs -= initialStkOffs;
5526 if (isFramePointerUsed())
5528 // Subtract off FP and LR.
5529 assert(compCalleeRegsPushed >= 2);
5530 stkOffs -= (compCalleeRegsPushed - 2) * REGSIZE_BYTES;
5534 stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES;
5537 #else // !_TARGET_ARM64_
5538 stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES;
5539 #endif // !_TARGET_ARM64_
5541 compLclFrameSize = 0;
5543 #ifdef _TARGET_AMD64_
5544 // In case of Amd64 compCalleeRegsPushed includes float regs (Xmm6-xmm15) that
5545 // need to be pushed. But Amd64 doesn't support push/pop of xmm registers.
5546 // Instead we need to allocate space for them on the stack and save them in prolog.
5547 // Therefore, we consider xmm registers being saved while computing stack offsets
5548 // but space for xmm registers is considered part of compLclFrameSize.
5550 // 1) We need to save the entire 128-bits of xmm register to stack, since amd64
5551 // prolog unwind codes allow encoding of an instruction that stores the entire xmm reg
5552 // at an offset relative to SP
5553 // 2) We adjust frame size so that SP is aligned at 16-bytes after pushing integer registers.
5554 // This means while saving the first xmm register to its allocated stack location we might
5555 // have to skip 8-bytes. The reason for padding is to use efficient "movaps" to save/restore
5556 // xmm registers to/from stack to match Jit64 codegen. Without the aligning on 16-byte
5557 // boundary we would have to use movups when offset turns out unaligned. Movaps is more
5558 // performant than movups.
5559 unsigned calleeFPRegsSavedSize = genCountBits(compCalleeFPRegsSavedMask) * XMM_REGSIZE_BYTES;
5560 if (calleeFPRegsSavedSize > 0 && ((stkOffs % XMM_REGSIZE_BYTES) != 0))
5562 // Take care of alignment
5563 int alignPad = (int)AlignmentPad((unsigned)-stkOffs, XMM_REGSIZE_BYTES);
5564 stkOffs -= alignPad;
5565 lvaIncrementFrameSize(alignPad);
5568 stkOffs -= calleeFPRegsSavedSize;
5569 lvaIncrementFrameSize(calleeFPRegsSavedSize);
5571 // Quirk for VS debug-launch scenario to work
5572 if (compVSQuirkStackPaddingNeeded > 0)
5577 printf("\nAdding VS quirk stack padding of %d bytes between save-reg area and locals\n",
5578 compVSQuirkStackPaddingNeeded);
5582 stkOffs -= compVSQuirkStackPaddingNeeded;
5583 lvaIncrementFrameSize(compVSQuirkStackPaddingNeeded);
5585 #endif //_TARGET_AMD64_
5587 #if FEATURE_EH_FUNCLETS && defined(_TARGET_ARMARCH_)
5588 if (lvaPSPSym != BAD_VAR_NUM)
5590 // On ARM/ARM64, if we need a PSPSym, allocate it first, before anything else, including
5591 // padding (so we can avoid computing the same padding in the funclet
5592 // frame). Note that there is no special padding requirement for the PSPSym.
5593 noway_assert(codeGen->isFramePointerUsed()); // We need an explicit frame pointer
5594 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaPSPSym, TARGET_POINTER_SIZE, stkOffs);
5596 #endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARMARCH_)
5598 if (mustDoubleAlign)
5600 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
5602 // Allocate a pointer sized stack slot, since we may need to double align here
5603 // when lvaDoneFrameLayout == FINAL_FRAME_LAYOUT
5605 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5606 stkOffs -= TARGET_POINTER_SIZE;
5608 // If we have any TYP_LONG, TYP_DOUBLE or double aligned structs
5609 // then we need to allocate a second pointer sized stack slot,
5610 // since we may need to double align that LclVar when we see it
5611 // in the loop below. We will just always do this so that the
5612 // offsets that we calculate for the stack frame will always
5613 // be greater (or equal) to what they can be in the final layout.
5615 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5616 stkOffs -= TARGET_POINTER_SIZE;
5618 else // FINAL_FRAME_LAYOUT
5620 if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
5622 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5623 stkOffs -= TARGET_POINTER_SIZE;
5625 // We should now have a double-aligned (stkOffs+preSpillSize)
5626 noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
5630 if (lvaMonAcquired != BAD_VAR_NUM)
5632 // This var must go first, in what is called the 'frame header' for EnC so that it is
5633 // preserved when remapping occurs. See vm\eetwain.cpp for detailed comment specifying frame
5634 // layout requirements for EnC to work.
5635 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaMonAcquired, lvaLclSize(lvaMonAcquired), stkOffs);
5638 if (opts.compNeedSecurityCheck)
5640 #ifdef JIT32_GCENCODER
5641 /* This can't work without an explicit frame, so make sure */
5642 noway_assert(codeGen->isFramePointerUsed());
5644 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaSecurityObject, TARGET_POINTER_SIZE, stkOffs);
5647 if (lvaLocAllocSPvar != BAD_VAR_NUM)
5649 #ifdef JIT32_GCENCODER
5650 noway_assert(codeGen->isFramePointerUsed()); // else offsets of locals of frameless methods will be incorrect
5652 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaLocAllocSPvar, TARGET_POINTER_SIZE, stkOffs);
5655 if (lvaReportParamTypeArg())
5657 #ifdef JIT32_GCENCODER
5658 noway_assert(codeGen->isFramePointerUsed());
5660 // For CORINFO_CALLCONV_PARAMTYPE (if needed)
5661 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5662 stkOffs -= TARGET_POINTER_SIZE;
5663 lvaCachedGenericContextArgOffs = stkOffs;
5665 #ifndef JIT32_GCENCODER
5666 else if (lvaKeepAliveAndReportThis())
5668 // When "this" is also used as generic context arg.
5669 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5670 stkOffs -= TARGET_POINTER_SIZE;
5671 lvaCachedGenericContextArgOffs = stkOffs;
5675 #if !FEATURE_EH_FUNCLETS
5676 /* If we need space for slots for shadow SP, reserve it now */
5677 if (ehNeedsShadowSPslots())
5679 noway_assert(codeGen->isFramePointerUsed()); // else offsets of locals of frameless methods will be incorrect
5680 if (!lvaReportParamTypeArg())
5682 #ifndef JIT32_GCENCODER
5683 if (!lvaKeepAliveAndReportThis())
5686 // In order to keep the gc info encoding smaller, the VM assumes that all methods with EH
5687 // have also saved space for a ParamTypeArg, so we need to do that here
5688 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5689 stkOffs -= TARGET_POINTER_SIZE;
5692 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaShadowSPslotsVar, lvaLclSize(lvaShadowSPslotsVar), stkOffs);
5694 #endif // !FEATURE_EH_FUNCLETS
5696 if (compGSReorderStackLayout)
5698 assert(getNeedsGSSecurityCookie());
5699 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaGSSecurityCookie, lvaLclSize(lvaGSSecurityCookie), stkOffs);
5703 If we're supposed to track lifetimes of pointer temps, we'll
5704 assign frame offsets in the following order:
5706 non-pointer local variables (also untracked pointer variables)
5707 pointer local variables
5714 ALLOC_NON_PTRS = 0x1, // assign offsets to non-ptr
5715 ALLOC_PTRS = 0x2, // Second pass, assign offsets to tracked ptrs
5716 ALLOC_UNSAFE_BUFFERS = 0x4,
5717 ALLOC_UNSAFE_BUFFERS_WITH_PTRS = 0x8
5719 UINT alloc_order[5];
5721 unsigned int cur = 0;
5723 if (compGSReorderStackLayout)
5725 noway_assert(getNeedsGSSecurityCookie());
5727 if (codeGen->isFramePointerUsed())
5729 alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS;
5730 alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS_WITH_PTRS;
5734 bool tempsAllocated = false;
5736 if (lvaTempsHaveLargerOffsetThanVars() && !codeGen->isFramePointerUsed())
5738 // Because we want the temps to have a larger offset than locals
5739 // and we're not using a frame pointer, we have to place the temps
5740 // above the vars. Otherwise we place them after the vars (at the
5741 // bottom of the frame).
5742 noway_assert(!tempsAllocated);
5743 stkOffs = lvaAllocateTemps(stkOffs, mustDoubleAlign);
5744 tempsAllocated = true;
5747 alloc_order[cur++] = ALLOC_NON_PTRS;
5749 if (opts.compDbgEnC)
5751 /* We will use just one pass, and assign offsets to all variables */
5752 alloc_order[cur - 1] |= ALLOC_PTRS;
5753 noway_assert(compGSReorderStackLayout == false);
5757 alloc_order[cur++] = ALLOC_PTRS;
5760 if (!codeGen->isFramePointerUsed() && compGSReorderStackLayout)
5762 alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS_WITH_PTRS;
5763 alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS;
5766 alloc_order[cur] = 0;
5768 noway_assert(cur < _countof(alloc_order));
5770 // Force first pass to happen
5771 UINT assignMore = 0xFFFFFFFF;
5772 bool have_LclVarDoubleAlign = false;
5774 for (cur = 0; alloc_order[cur]; cur++)
5776 if ((assignMore & alloc_order[cur]) == 0)
5786 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
5788 /* Ignore field locals of the promotion type PROMOTION_TYPE_FIELD_DEPENDENT.
5789 In other words, we will not calculate the "base" address of the struct local if
5790 the promotion type is PROMOTION_TYPE_FIELD_DEPENDENT.
5792 if (lvaIsFieldOfDependentlyPromotedStruct(varDsc))
5797 #if FEATURE_FIXED_OUT_ARGS
5798 // The scratch mem is used for the outgoing arguments, and it must be absolutely last
5799 if (lclNum == lvaOutgoingArgSpaceVar)
5805 bool allocateOnFrame = varDsc->lvOnFrame;
5807 if (varDsc->lvRegister && (lvaDoneFrameLayout == REGALLOC_FRAME_LAYOUT) &&
5808 ((varDsc->TypeGet() != TYP_LONG) || (varDsc->lvOtherReg != REG_STK)))
5810 allocateOnFrame = false;
5813 /* Ignore variables that are not on the stack frame */
5815 if (!allocateOnFrame)
5817 /* For EnC, all variables have to be allocated space on the
5818 stack, even though they may actually be enregistered. This
5819 way, the frame layout can be directly inferred from the
5823 if (!opts.compDbgEnC)
5827 else if (lclNum >= info.compLocalsCount)
5828 { // ignore temps for EnC
5832 else if (lvaGSSecurityCookie == lclNum && getNeedsGSSecurityCookie())
5834 continue; // This is allocated outside of this loop.
5837 // These need to be located as the very first variables (highest memory address)
5838 // and so they have already been assigned an offset
5840 #if FEATURE_EH_FUNCLETS
5841 lclNum == lvaPSPSym ||
5843 lclNum == lvaShadowSPslotsVar ||
5844 #endif // FEATURE_EH_FUNCLETS
5845 lclNum == lvaLocAllocSPvar || lclNum == lvaSecurityObject)
5847 assert(varDsc->lvStkOffs != BAD_STK_OFFS);
5851 if (lclNum == lvaMonAcquired)
5856 // This should be low on the stack. Hence, it will be assigned later.
5857 if (lclNum == lvaStubArgumentVar)
5859 #ifdef JIT32_GCENCODER
5860 noway_assert(codeGen->isFramePointerUsed());
5865 // This should be low on the stack. Hence, it will be assigned later.
5866 if (lclNum == lvaInlinedPInvokeFrameVar)
5868 noway_assert(codeGen->isFramePointerUsed());
5872 if (varDsc->lvIsParam)
5874 #if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
5876 // On Windows AMD64 we can use the caller-reserved stack area that is already setup
5877 assert(varDsc->lvStkOffs != BAD_STK_OFFS);
5880 #else // !_TARGET_AMD64_
5882 // A register argument that is not enregistered ends up as
5883 // a local variable which will need stack frame space.
5885 if (!varDsc->lvIsRegArg)
5890 #ifdef _TARGET_ARM64_
5891 if (info.compIsVarArgs)
5893 // Stack offset to varargs (parameters) should point to home area which will be preallocated.
5895 -initialStkOffs + genMapIntRegNumToRegArgNum(varDsc->GetArgReg()) * REGSIZE_BYTES;
5901 // On ARM we spill the registers in codeGen->regSet.rsMaskPreSpillRegArg
5902 // in the prolog, thus they don't need stack frame space.
5904 if ((codeGen->regSet.rsMaskPreSpillRegs(false) & genRegMask(varDsc->lvArgReg)) != 0)
5906 assert(varDsc->lvStkOffs != BAD_STK_OFFS);
5911 #endif // !_TARGET_AMD64_
5914 /* Make sure the type is appropriate */
5916 if (varDsc->lvIsUnsafeBuffer && compGSReorderStackLayout)
5918 if (varDsc->lvIsPtr)
5920 if ((alloc_order[cur] & ALLOC_UNSAFE_BUFFERS_WITH_PTRS) == 0)
5922 assignMore |= ALLOC_UNSAFE_BUFFERS_WITH_PTRS;
5928 if ((alloc_order[cur] & ALLOC_UNSAFE_BUFFERS) == 0)
5930 assignMore |= ALLOC_UNSAFE_BUFFERS;
5935 else if (varTypeIsGC(varDsc->TypeGet()) && varDsc->lvTracked)
5937 if ((alloc_order[cur] & ALLOC_PTRS) == 0)
5939 assignMore |= ALLOC_PTRS;
5945 if ((alloc_order[cur] & ALLOC_NON_PTRS) == 0)
5947 assignMore |= ALLOC_NON_PTRS;
5952 /* Need to align the offset? */
5954 if (mustDoubleAlign && (varDsc->lvType == TYP_DOUBLE // Align doubles for ARM and x86
5956 || varDsc->lvType == TYP_LONG // Align longs for ARM
5958 #ifndef _TARGET_64BIT_
5959 || varDsc->lvStructDoubleAlign // Align when lvStructDoubleAlign is true
5960 #endif // !_TARGET_64BIT_
5963 noway_assert((compLclFrameSize % TARGET_POINTER_SIZE) == 0);
5965 if ((lvaDoneFrameLayout != FINAL_FRAME_LAYOUT) && !have_LclVarDoubleAlign)
5967 // If this is the first TYP_LONG, TYP_DOUBLE or double aligned struct
5968 // then we have seen in this loop then we allocate a pointer sized
5969 // stack slot since we may need to double align this LclVar
5970 // when lvaDoneFrameLayout == FINAL_FRAME_LAYOUT
5972 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5973 stkOffs -= TARGET_POINTER_SIZE;
5977 if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
5979 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5980 stkOffs -= TARGET_POINTER_SIZE;
5983 // We should now have a double-aligned (stkOffs+preSpillSize)
5984 noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
5987 // Remember that we had to double align a LclVar
5988 have_LclVarDoubleAlign = true;
5991 // Reserve the stack space for this variable
5992 stkOffs = lvaAllocLocalAndSetVirtualOffset(lclNum, lvaLclSize(lclNum), stkOffs);
5993 #ifdef _TARGET_ARM64_
5994 // If we have an incoming register argument that has a struct promoted field
5995 // then we need to copy the lvStkOff (the stack home) from the reg arg to the field lclvar
5997 if (varDsc->lvIsRegArg && varDsc->lvPromotedStruct())
5999 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
6001 unsigned fieldVarNum = varDsc->lvFieldLclStart;
6002 lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
6004 #endif // _TARGET_ARM64_
6006 // If we have an incoming register argument that has a promoted long
6007 // then we need to copy the lvStkOff (the stack home) from the reg arg to the field lclvar
6009 if (varDsc->lvIsRegArg && varDsc->lvPromoted)
6011 assert(varTypeIsLong(varDsc) && (varDsc->lvFieldCnt == 2));
6013 unsigned fieldVarNum = varDsc->lvFieldLclStart;
6014 lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
6015 lvaTable[fieldVarNum + 1].lvStkOffs = varDsc->lvStkOffs + 4;
6017 #endif // _TARGET_ARM_
6021 if (getNeedsGSSecurityCookie() && !compGSReorderStackLayout)
6023 // LOCALLOC used, but we have no unsafe buffer. Allocated cookie last, close to localloc buffer.
6024 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaGSSecurityCookie, lvaLclSize(lvaGSSecurityCookie), stkOffs);
6027 if (tempsAllocated == false)
6029 /*-------------------------------------------------------------------------
6033 *-------------------------------------------------------------------------
6035 stkOffs = lvaAllocateTemps(stkOffs, mustDoubleAlign);
6038 /*-------------------------------------------------------------------------
6040 * Now do some final stuff
6042 *-------------------------------------------------------------------------
6045 // lvaInlinedPInvokeFrameVar and lvaStubArgumentVar need to be assigned last
6046 // Important: The stack walker depends on lvaStubArgumentVar immediately
6047 // following lvaInlinedPInvokeFrameVar in the frame.
6049 if (lvaStubArgumentVar != BAD_VAR_NUM)
6051 #ifdef JIT32_GCENCODER
6052 noway_assert(codeGen->isFramePointerUsed());
6054 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaStubArgumentVar, lvaLclSize(lvaStubArgumentVar), stkOffs);
6057 if (lvaInlinedPInvokeFrameVar != BAD_VAR_NUM)
6059 noway_assert(codeGen->isFramePointerUsed());
6061 lvaAllocLocalAndSetVirtualOffset(lvaInlinedPInvokeFrameVar, lvaLclSize(lvaInlinedPInvokeFrameVar), stkOffs);
6064 if (mustDoubleAlign)
6066 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6068 // Allocate a pointer sized stack slot, since we may need to double align here
6069 // when lvaDoneFrameLayout == FINAL_FRAME_LAYOUT
6071 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6072 stkOffs -= TARGET_POINTER_SIZE;
6074 if (have_LclVarDoubleAlign)
6076 // If we have any TYP_LONG, TYP_DOUBLE or double aligned structs
6077 // the we need to allocate a second pointer sized stack slot,
6078 // since we may need to double align the last LclVar that we saw
6079 // in the loop above. We do this so that the offsets that we
6080 // calculate for the stack frame are always greater than they will
6081 // be in the final layout.
6083 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6084 stkOffs -= TARGET_POINTER_SIZE;
6087 else // FINAL_FRAME_LAYOUT
6089 if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
6091 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6092 stkOffs -= TARGET_POINTER_SIZE;
6094 // We should now have a double-aligned (stkOffs+preSpillSize)
6095 noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
6099 #if FEATURE_EH_FUNCLETS && defined(_TARGET_AMD64_)
6100 if (lvaPSPSym != BAD_VAR_NUM)
6102 // On AMD64, if we need a PSPSym, allocate it last, immediately above the outgoing argument
6103 // space. Any padding will be higher on the stack than this
6104 // (including the padding added by lvaAlignFrame()).
6105 noway_assert(codeGen->isFramePointerUsed()); // We need an explicit frame pointer
6106 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaPSPSym, TARGET_POINTER_SIZE, stkOffs);
6108 #endif // FEATURE_EH_FUNCLETS && defined(_TARGET_AMD64_)
6110 #ifdef _TARGET_ARM64_
6111 if (isFramePointerUsed())
6113 // Create space for saving FP and LR.
6114 stkOffs -= 2 * REGSIZE_BYTES;
6116 #endif // _TARGET_ARM64_
6118 #if FEATURE_FIXED_OUT_ARGS
6119 if (lvaOutgoingArgSpaceSize > 0)
6121 #if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI) // No 4 slots for outgoing params on System V.
6122 noway_assert(lvaOutgoingArgSpaceSize >= (4 * TARGET_POINTER_SIZE));
6124 noway_assert((lvaOutgoingArgSpaceSize % TARGET_POINTER_SIZE) == 0);
6126 // Give it a value so we can avoid asserts in CHK builds.
6127 // Since this will always use an SP relative offset of zero
6128 // at the end of lvaFixVirtualFrameOffsets, it will be set to absolute '0'
6130 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaOutgoingArgSpaceVar, lvaLclSize(lvaOutgoingArgSpaceVar), stkOffs);
6132 #endif // FEATURE_FIXED_OUT_ARGS
6134 // compLclFrameSize equals our negated virtual stack offset minus the pushed registers and return address
6135 // and the pushed frame pointer register which for some strange reason isn't part of 'compCalleeRegsPushed'.
6136 int pushedCount = compCalleeRegsPushed;
6138 #ifdef _TARGET_ARM64_
6139 if (info.compIsVarArgs)
6141 pushedCount += MAX_REG_ARG;
6145 #ifdef _TARGET_XARCH_
6146 if (codeGen->doubleAlignOrFramePointerUsed())
6148 pushedCount += 1; // pushed EBP (frame pointer)
6150 pushedCount += 1; // pushed PC (return address)
6153 noway_assert(compLclFrameSize == (unsigned)-(stkOffs + (pushedCount * (int)TARGET_POINTER_SIZE)));
6156 int Compiler::lvaAllocLocalAndSetVirtualOffset(unsigned lclNum, unsigned size, int stkOffs)
6158 noway_assert(lclNum != BAD_VAR_NUM);
6160 #ifdef _TARGET_64BIT_
6161 // Before final frame layout, assume the worst case, that every >=8 byte local will need
6162 // maximum padding to be aligned. This is because we generate code based on the stack offset
6163 // computed during tentative frame layout. These offsets cannot get bigger during final
6164 // frame layout, as that would possibly require different code generation (for example,
6165 // using a 4-byte offset instead of a 1-byte offset in an instruction). The offsets can get
6166 // smaller. It is possible there is different alignment at the point locals are allocated
6167 // between tentative and final frame layout which would introduce padding between locals
6168 // and thus increase the offset (from the stack pointer) of one of the locals. Hence the
6169 // need to assume the worst alignment before final frame layout.
6170 // We could probably improve this by sorting all the objects by alignment,
6171 // such that all 8 byte objects are together, 4 byte objects are together, etc., which
6172 // would require at most one alignment padding per group.
6174 // TYP_SIMD structs locals have alignment preference given by getSIMDTypeAlignment() for
6175 // better performance.
6176 if ((size >= 8) && ((lvaDoneFrameLayout != FINAL_FRAME_LAYOUT) || ((stkOffs % 8) != 0)
6177 #if defined(FEATURE_SIMD) && ALIGN_SIMD_TYPES
6178 || lclVarIsSIMDType(lclNum)
6182 // Note that stack offsets are negative or equal to zero
6183 assert(stkOffs <= 0);
6185 // alignment padding
6187 #if defined(FEATURE_SIMD) && ALIGN_SIMD_TYPES
6188 if (lclVarIsSIMDType(lclNum) && !lvaIsImplicitByRefLocal(lclNum))
6190 int alignment = getSIMDTypeAlignment(lvaTable[lclNum].lvType);
6192 if (stkOffs % alignment != 0)
6194 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6196 pad = alignment - 1;
6197 // Note that all the objects will probably be misaligned, but we'll fix that in final layout.
6201 pad = alignment + (stkOffs % alignment); // +1 to +(alignment-1) bytes
6206 #endif // FEATURE_SIMD && ALIGN_SIMD_TYPES
6208 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6211 // Note that all the objects will probably be misaligned, but we'll fix that in final layout.
6215 pad = 8 + (stkOffs % 8); // +1 to +7 bytes
6218 // Will the pad ever be anything except 4? Do we put smaller-than-4-sized objects on the stack?
6219 lvaIncrementFrameSize(pad);
6226 gtDispLclVar(lclNum, /*pad*/ false);
6227 printf(", size=%d, stkOffs=%c0x%x, pad=%d\n", size, stkOffs < 0 ? '-' : '+',
6228 stkOffs < 0 ? -stkOffs : stkOffs, pad);
6232 #endif // _TARGET_64BIT_
6234 /* Reserve space on the stack by bumping the frame size */
6236 lvaIncrementFrameSize(size);
6238 lvaTable[lclNum].lvStkOffs = stkOffs;
6244 gtDispLclVar(lclNum, /*pad*/ false);
6245 printf(", size=%d, stkOffs=%c0x%x\n", size, stkOffs < 0 ? '-' : '+', stkOffs < 0 ? -stkOffs : stkOffs);
6252 #ifdef _TARGET_AMD64_
6253 /*****************************************************************************
6254 * lvaIsCalleeSavedIntRegCountEven() : returns true if the number of integer registers
6255 * pushed onto stack is even including RBP if used as frame pointer
6257 * Note that this excludes return address (PC) pushed by caller. To know whether
6258 * the SP offset after pushing integer registers is aligned, we need to take
6259 * negation of this routine.
6261 bool Compiler::lvaIsCalleeSavedIntRegCountEven()
6263 unsigned regsPushed = compCalleeRegsPushed + (codeGen->isFramePointerUsed() ? 1 : 0);
6264 return (regsPushed % (16 / REGSIZE_BYTES)) == 0;
6266 #endif //_TARGET_AMD64_
6268 /*****************************************************************************
6269 * lvaAlignFrame() : After allocating everything on the frame, reserve any
6270 * extra space needed to keep the frame aligned
6272 void Compiler::lvaAlignFrame()
6274 #if defined(_TARGET_AMD64_)
6276 // Leaf frames do not need full alignment, but the unwind info is smaller if we
6277 // are at least 8 byte aligned (and we assert as much)
6278 if ((compLclFrameSize % 8) != 0)
6280 lvaIncrementFrameSize(8 - (compLclFrameSize % 8));
6282 else if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6284 // If we are not doing final layout, we don't know the exact value of compLclFrameSize
6285 // and thus do not know how much we will need to add in order to be aligned.
6286 // We add 8 so compLclFrameSize is still a multiple of 8.
6287 lvaIncrementFrameSize(8);
6289 assert((compLclFrameSize % 8) == 0);
6291 // Ensure that the stack is always 16-byte aligned by grabbing an unused QWORD
6292 // if needed, but off by 8 because of the return value.
6293 // And don't forget that compCalleeRegsPused does *not* include RBP if we are
6294 // using it as the frame pointer.
6296 bool regPushedCountAligned = lvaIsCalleeSavedIntRegCountEven();
6297 bool lclFrameSizeAligned = (compLclFrameSize % 16) == 0;
6299 // If this isn't the final frame layout, assume we have to push an extra QWORD
6300 // Just so the offsets are true upper limits.
6301 CLANG_FORMAT_COMMENT_ANCHOR;
6303 #ifdef UNIX_AMD64_ABI
6304 // The compNeedToAlignFrame flag is indicating if there is a need to align the frame.
6305 // On AMD64-Windows, if there are calls, 4 slots for the outgoing ars are allocated, except for
6306 // FastTailCall. This slots makes the frame size non-zero, so alignment logic will be called.
6307 // On AMD64-Unix, there are no such slots. There is a possibility to have calls in the method with frame size of 0.
6308 // The frame alignment logic won't kick in. This flags takes care of the AMD64-Unix case by remembering that there
6309 // are calls and making sure the frame alignment logic is executed.
6310 bool stackNeedsAlignment = (compLclFrameSize != 0 || opts.compNeedToAlignFrame);
6311 #else // !UNIX_AMD64_ABI
6312 bool stackNeedsAlignment = compLclFrameSize != 0;
6313 #endif // !UNIX_AMD64_ABI
6314 if ((!codeGen->isFramePointerUsed() && (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)) ||
6315 (stackNeedsAlignment && (regPushedCountAligned == lclFrameSizeAligned)))
6317 lvaIncrementFrameSize(REGSIZE_BYTES);
6320 #elif defined(_TARGET_ARM64_)
6322 // The stack on ARM64 must be 16 byte aligned.
6324 // First, align up to 8.
6325 if ((compLclFrameSize % 8) != 0)
6327 lvaIncrementFrameSize(8 - (compLclFrameSize % 8));
6329 else if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6331 // If we are not doing final layout, we don't know the exact value of compLclFrameSize
6332 // and thus do not know how much we will need to add in order to be aligned.
6333 // We add 8 so compLclFrameSize is still a multiple of 8.
6334 lvaIncrementFrameSize(8);
6336 assert((compLclFrameSize % 8) == 0);
6338 // Ensure that the stack is always 16-byte aligned by grabbing an unused QWORD
6340 bool regPushedCountAligned = (compCalleeRegsPushed % (16 / REGSIZE_BYTES)) == 0;
6341 bool lclFrameSizeAligned = (compLclFrameSize % 16) == 0;
6343 // If this isn't the final frame layout, assume we have to push an extra QWORD
6344 // Just so the offsets are true upper limits.
6345 if ((lvaDoneFrameLayout != FINAL_FRAME_LAYOUT) || (regPushedCountAligned != lclFrameSizeAligned))
6347 lvaIncrementFrameSize(REGSIZE_BYTES);
6350 #elif defined(_TARGET_ARM_)
6352 // Ensure that stack offsets will be double-aligned by grabbing an unused DWORD if needed.
6354 bool lclFrameSizeAligned = (compLclFrameSize % sizeof(double)) == 0;
6355 bool regPushedCountAligned = ((compCalleeRegsPushed + genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true))) %
6356 (sizeof(double) / TARGET_POINTER_SIZE)) == 0;
6358 if (regPushedCountAligned != lclFrameSizeAligned)
6360 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6363 #elif defined(_TARGET_X86_)
6366 if (genDoubleAlign())
6368 // Double Frame Alignement for x86 is handled in Compiler::lvaAssignVirtualFrameOffsetsToLocals()
6370 if (compLclFrameSize == 0)
6372 // This can only happen with JitStress=1 or JitDoubleAlign=2
6373 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6378 if (STACK_ALIGN > REGSIZE_BYTES)
6380 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6382 // If we are not doing final layout, we don't know the exact value of compLclFrameSize
6383 // and thus do not know how much we will need to add in order to be aligned.
6384 // We add the maximum pad that we could ever have (which is 12)
6385 lvaIncrementFrameSize(STACK_ALIGN - REGSIZE_BYTES);
6388 // Align the stack with STACK_ALIGN value.
6389 int adjustFrameSize = compLclFrameSize;
6390 #if defined(UNIX_X86_ABI)
6391 bool isEbpPushed = codeGen->isFramePointerUsed();
6393 isEbpPushed |= genDoubleAlign();
6395 // we need to consider spilled register(s) plus return address and/or EBP
6396 int adjustCount = compCalleeRegsPushed + 1 + (isEbpPushed ? 1 : 0);
6397 adjustFrameSize += (adjustCount * REGSIZE_BYTES) % STACK_ALIGN;
6399 if ((adjustFrameSize % STACK_ALIGN) != 0)
6401 lvaIncrementFrameSize(STACK_ALIGN - (adjustFrameSize % STACK_ALIGN));
6406 NYI("TARGET specific lvaAlignFrame");
6407 #endif // !_TARGET_AMD64_
6410 /*****************************************************************************
6411 * lvaAssignFrameOffsetsToPromotedStructs() : Assign offsets to fields
6412 * within a promoted struct (worker for lvaAssignFrameOffsets).
6414 void Compiler::lvaAssignFrameOffsetsToPromotedStructs()
6416 LclVarDsc* varDsc = lvaTable;
6417 for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++, varDsc++)
6419 // For promoted struct fields that are params, we will
6420 // assign their offsets in lvaAssignVirtualFrameOffsetToArg().
6421 // This is not true for the System V systems since there is no
6422 // outgoing args space. Assign the dependently promoted fields properly.
6424 if (varDsc->lvIsStructField
6425 #ifndef UNIX_AMD64_ABI
6426 #if !defined(_TARGET_ARM_) || defined(LEGACY_BACKEND)
6427 // Non-legacy ARM: lo/hi parts of a promoted long arg need to be updated.
6429 // For System V platforms there is no outgoing args space.
6430 // A register passed struct arg is homed on the stack in a separate local var.
6431 // The offset of these structs is already calculated in lvaAssignVirtualFrameOffsetToArg methos.
6432 // Make sure the code below is not executed for these structs and the offset is not changed.
6433 && !varDsc->lvIsParam
6434 #endif // !defined(_TARGET_ARM_) || defined(LEGACY_BACKEND)
6435 #endif // UNIX_AMD64_ABI
6438 LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl];
6439 lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);
6441 if (promotionType == PROMOTION_TYPE_INDEPENDENT)
6443 // The stack offset for these field locals must have been calculated
6444 // by the normal frame offset assignment.
6449 noway_assert(promotionType == PROMOTION_TYPE_DEPENDENT);
6450 noway_assert(varDsc->lvOnFrame);
6451 if (parentvarDsc->lvOnFrame)
6453 varDsc->lvStkOffs = parentvarDsc->lvStkOffs + varDsc->lvFldOffset;
6457 varDsc->lvOnFrame = false;
6458 noway_assert(varDsc->lvRefCnt == 0);
6465 /*****************************************************************************
6466 * lvaAllocateTemps() : Assign virtual offsets to temps (always negative).
6468 int Compiler::lvaAllocateTemps(int stkOffs, bool mustDoubleAlign)
6470 unsigned spillTempSize = 0;
6472 if (lvaDoneFrameLayout == FINAL_FRAME_LAYOUT)
6474 int preSpillSize = 0;
6476 preSpillSize = genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true)) * TARGET_POINTER_SIZE;
6480 bool assignPtrs = true;
6482 /* Allocate temps */
6484 if (TRACK_GC_TEMP_LIFETIMES)
6486 /* first pointers, then non-pointers in second pass */
6492 /* Pointers and non-pointers together in single pass */
6497 assert(tmpAllFree());
6501 for (TempDsc* temp = tmpListBeg(); temp != nullptr; temp = tmpListNxt(temp))
6503 var_types tempType = temp->tdTempType();
6506 /* Make sure the type is appropriate */
6508 if (!assignPtrs && varTypeIsGC(tempType))
6512 if (!assignNptr && !varTypeIsGC(tempType))
6517 size = temp->tdTempSize();
6519 /* Figure out and record the stack offset of the temp */
6521 /* Need to align the offset? */
6522 CLANG_FORMAT_COMMENT_ANCHOR;
6524 #ifdef _TARGET_64BIT_
6525 if (varTypeIsGC(tempType) && ((stkOffs % TARGET_POINTER_SIZE) != 0))
6527 // Calculate 'pad' as the number of bytes to align up 'stkOffs' to be a multiple of TARGET_POINTER_SIZE
6528 // In practice this is really just a fancy way of writing 4. (as all stack locations are at least 4-byte
6529 // aligned). Note stkOffs is always negative, so (stkOffs % TARGET_POINTER_SIZE) yields a negative
6532 int alignPad = (int)AlignmentPad((unsigned)-stkOffs, TARGET_POINTER_SIZE);
6534 spillTempSize += alignPad;
6535 lvaIncrementFrameSize(alignPad);
6536 stkOffs -= alignPad;
6538 noway_assert((stkOffs % TARGET_POINTER_SIZE) == 0);
6542 if (mustDoubleAlign && (tempType == TYP_DOUBLE)) // Align doubles for x86 and ARM
6544 noway_assert((compLclFrameSize % TARGET_POINTER_SIZE) == 0);
6546 if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
6548 spillTempSize += TARGET_POINTER_SIZE;
6549 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6550 stkOffs -= TARGET_POINTER_SIZE;
6552 // We should now have a double-aligned (stkOffs+preSpillSize)
6553 noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
6556 spillTempSize += size;
6557 lvaIncrementFrameSize(size);
6559 temp->tdSetTempOffs(stkOffs);
6562 // Only required for the ARM platform that we have an accurate estimate for the spillTempSize
6563 noway_assert(spillTempSize <= lvaGetMaxSpillTempSize());
6566 /* If we've only assigned some temps, go back and do the rest now */
6570 assignNptr = !assignNptr;
6571 assignPtrs = !assignPtrs;
6577 else // We haven't run codegen, so there are no Spill temps yet!
6579 unsigned size = lvaGetMaxSpillTempSize();
6581 lvaIncrementFrameSize(size);
6590 /*****************************************************************************
6592 * Dump the register a local is in right now.
6593 * For non-LSRA, this will be the register it is always in. For LSRA, it's only the current
6594 * location, since the location changes and it is updated throughout code generation based on
6595 * LSRA register assignments.
6598 void Compiler::lvaDumpRegLocation(unsigned lclNum)
6600 LclVarDsc* varDsc = lvaTable + lclNum;
6601 var_types type = varDsc->TypeGet();
6603 #if FEATURE_STACK_FP_X87
6604 if (varTypeIsFloating(type))
6606 printf("fpu stack ");
6610 if (isRegPairType(type))
6614 noway_assert(varDsc->lvRegNum != REG_STK);
6616 if (doLSRA() && varDsc->lvRegNum == REG_STK)
6618 /* Hi-only enregistered long */
6619 int offset = varDsc->lvStkOffs;
6620 printf("%-3s:[%1s0x%02X]",
6621 getRegName(varDsc->lvOtherReg), // hi32
6622 (offset < 0 ? "-" : "+"), (offset < 0 ? -offset : offset));
6624 else if (varDsc->lvOtherReg != REG_STK)
6626 /* Fully enregistered long */
6628 getRegName(varDsc->lvOtherReg), // hi32
6629 getRegName(varDsc->lvRegNum)); // lo32
6633 /* Partially enregistered long */
6634 int offset = varDsc->lvStkOffs + 4;
6635 printf("[%1s0x%02X]:%-3s", (offset < 0 ? "-" : "+"), (offset < 0 ? -offset : offset),
6636 getRegName(varDsc->lvRegNum)); // lo32
6640 else if (varDsc->TypeGet() == TYP_DOUBLE)
6642 #ifdef LEGACY_BACKEND
6643 // The assigned registers are `lvRegNum:lvOtherReg`
6644 printf("%3s:%-3s ", getRegName(varDsc->lvRegNum), getRegName(varDsc->lvOtherReg));
6646 // The assigned registers are `lvRegNum:RegNext(lvRegNum)`
6647 printf("%3s:%-3s ", getRegName(varDsc->lvRegNum), getRegName(REG_NEXT(varDsc->lvRegNum)));
6650 #endif // !_TARGET_ARM_
6653 printf("%3s ", getRegName(varDsc->lvRegNum));
6657 /*****************************************************************************
6659 * Dump the frame location assigned to a local.
6660 * For non-LSRA, this will only be valid if there is no assigned register.
6661 * For LSRA, it's the home location, even though the variable doesn't always live
6662 * in its home location.
6665 void Compiler::lvaDumpFrameLocation(unsigned lclNum)
6671 offset = lvaFrameAddress(lclNum, compLocallocUsed, &baseReg, 0);
6674 offset = lvaFrameAddress(lclNum, &EBPbased);
6675 baseReg = EBPbased ? REG_FPBASE : REG_SPBASE;
6678 printf("[%2s%1s0x%02X] ", getRegName(baseReg), (offset < 0 ? "-" : "+"), (offset < 0 ? -offset : offset));
6681 /*****************************************************************************
6683 * dump a single lvaTable entry
6686 void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t refCntWtdWidth)
6688 LclVarDsc* varDsc = lvaTable + lclNum;
6689 var_types type = varDsc->TypeGet();
6691 if (curState == INITIAL_FRAME_LAYOUT)
6694 gtDispLclVar(lclNum);
6696 printf(" %7s ", varTypeName(type));
6697 if (genTypeSize(type) == 0)
6699 printf("(%2d) ", lvaLclSize(lclNum));
6704 if (varDsc->lvRefCnt == 0)
6706 // Print this with a special indicator that the variable is unused. Even though the
6707 // variable itself is unused, it might be a struct that is promoted, so seeing it
6708 // can be useful when looking at the promoted struct fields. It's also weird to see
6709 // missing var numbers if these aren't printed.
6713 #if FEATURE_FIXED_OUT_ARGS
6714 if ((lclNum == lvaOutgoingArgSpaceVar) && (lvaLclSize(lclNum) == 0))
6716 // Similar to above; print this anyway.
6725 gtDispLclVar(lclNum);
6727 printf("[V%02u", lclNum);
6728 if (varDsc->lvTracked)
6730 printf(",T%02u]", varDsc->lvVarIndex);
6737 printf(" (%3u,%*s)", varDsc->lvRefCnt, (int)refCntWtdWidth, refCntWtd2str(varDsc->lvRefCntWtd));
6739 printf(" %7s ", varTypeName(type));
6740 if (genTypeSize(type) == 0)
6742 printf("(%2d) ", lvaLclSize(lclNum));
6749 // The register or stack location field is 11 characters wide.
6750 if (varDsc->lvRefCnt == 0)
6752 printf("zero-ref ");
6754 else if (varDsc->lvRegister != 0)
6756 // It's always a register, and always in the same register.
6757 lvaDumpRegLocation(lclNum);
6759 else if (varDsc->lvOnFrame == 0)
6761 printf("registers ");
6765 // For RyuJIT backend, it might be in a register part of the time, but it will definitely have a stack home
6766 // location. Otherwise, it's always on the stack.
6767 if (lvaDoneFrameLayout != NO_FRAME_LAYOUT)
6769 lvaDumpFrameLocation(lclNum);
6774 if (varDsc->lvIsHfaRegArg())
6776 if (varDsc->lvHfaTypeIsFloat())
6778 printf(" (enregistered HFA: float) ");
6782 printf(" (enregistered HFA: double)");
6786 if (varDsc->lvDoNotEnregister)
6788 printf(" do-not-enreg[");
6789 if (varDsc->lvAddrExposed)
6793 if (varTypeIsStruct(varDsc))
6797 if (varDsc->lvVMNeedsStackAddr)
6801 if (varDsc->lvLiveInOutOfHndlr)
6805 if (varDsc->lvLclFieldExpr)
6809 if (varDsc->lvLclBlockOpAddr)
6813 if (varDsc->lvLiveAcrossUCall)
6817 if (varDsc->lvIsMultiRegArg)
6821 if (varDsc->lvIsMultiRegRet)
6825 #ifdef JIT32_GCENCODER
6826 if (varDsc->lvPinned)
6828 #endif // JIT32_GCENCODER
6832 if (varDsc->lvIsMultiRegArg)
6834 printf(" multireg-arg");
6836 if (varDsc->lvIsMultiRegRet)
6838 printf(" multireg-ret");
6840 if (varDsc->lvMustInit)
6842 printf(" must-init");
6844 if (varDsc->lvAddrExposed)
6846 printf(" addr-exposed");
6848 if (varDsc->lvHasLdAddrOp)
6850 printf(" ld-addr-op");
6852 if (varDsc->lvVerTypeInfo.IsThisPtr())
6856 if (varDsc->lvPinned)
6860 #ifdef LEGACY_BACKEND
6861 if (varDsc->lvRefAssign)
6863 printf(" ref-asgn");
6866 if (varDsc->lvStackByref)
6868 printf(" stack-byref");
6870 if (varDsc->lvClassHnd != nullptr)
6872 printf(" class-hnd");
6874 if (varDsc->lvClassIsExact)
6878 #ifndef _TARGET_64BIT_
6879 if (varDsc->lvStructDoubleAlign)
6880 printf(" double-align");
6881 #endif // !_TARGET_64BIT_
6882 if (varDsc->lvOverlappingFields)
6884 printf(" overlapping-fields");
6887 if (compGSReorderStackLayout && !varDsc->lvRegister)
6889 if (varDsc->lvIsPtr)
6893 if (varDsc->lvIsUnsafeBuffer)
6895 printf(" unsafe-buffer");
6898 if (varDsc->lvIsStructField)
6900 LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl];
6901 #if !defined(_TARGET_64BIT_)
6902 if (varTypeIsLong(parentvarDsc))
6904 bool isLo = (lclNum == parentvarDsc->lvFieldLclStart);
6905 printf(" V%02u.%s(offs=0x%02x)", varDsc->lvParentLcl, isLo ? "lo" : "hi", isLo ? 0 : genTypeSize(TYP_INT));
6908 #endif // !defined(_TARGET_64BIT_)
6910 CORINFO_CLASS_HANDLE typeHnd = parentvarDsc->lvVerTypeInfo.GetClassHandle();
6911 CORINFO_FIELD_HANDLE fldHnd = info.compCompHnd->getFieldInClass(typeHnd, varDsc->lvFldOrdinal);
6913 printf(" V%02u.%s(offs=0x%02x)", varDsc->lvParentLcl, eeGetFieldName(fldHnd), varDsc->lvFldOffset);
6915 lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);
6916 // We should never have lvIsStructField set if it is a reg-sized non-field-addressed struct.
6917 assert(!varDsc->lvRegStruct);
6918 switch (promotionType)
6920 case PROMOTION_TYPE_NONE:
6923 case PROMOTION_TYPE_DEPENDENT:
6926 case PROMOTION_TYPE_INDEPENDENT:
6936 /*****************************************************************************
6941 void Compiler::lvaTableDump(FrameLayoutState curState)
6943 if (curState == NO_FRAME_LAYOUT)
6945 curState = lvaDoneFrameLayout;
6946 if (curState == NO_FRAME_LAYOUT)
6948 // Still no layout? Could be a bug, but just display the initial layout
6949 curState = INITIAL_FRAME_LAYOUT;
6953 if (curState == INITIAL_FRAME_LAYOUT)
6955 printf("; Initial");
6957 else if (curState == PRE_REGALLOC_FRAME_LAYOUT)
6959 printf("; Pre-RegAlloc");
6961 else if (curState == REGALLOC_FRAME_LAYOUT)
6963 printf("; RegAlloc");
6965 else if (curState == TENTATIVE_FRAME_LAYOUT)
6967 printf("; Tentative");
6969 else if (curState == FINAL_FRAME_LAYOUT)
6975 printf("UNKNOWN FrameLayoutState!");
6979 printf(" local variable assignments\n");
6985 // Figure out some sizes, to help line things up
6987 size_t refCntWtdWidth = 6; // Use 6 as the minimum width
6989 if (curState != INITIAL_FRAME_LAYOUT) // don't need this info for INITIAL_FRAME_LAYOUT
6991 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
6993 size_t width = strlen(refCntWtd2str(varDsc->lvRefCntWtd));
6994 if (width > refCntWtdWidth)
6996 refCntWtdWidth = width;
7001 // Do the actual output
7003 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
7005 lvaDumpEntry(lclNum, curState, refCntWtdWidth);
7008 //-------------------------------------------------------------------------
7009 // Display the code-gen temps
7011 assert(tmpAllFree());
7012 for (TempDsc* temp = tmpListBeg(); temp != nullptr; temp = tmpListNxt(temp))
7014 printf("; TEMP_%02u %26s%*s%7s -> ", -temp->tdTempNum(), " ", refCntWtdWidth, " ",
7015 varTypeName(temp->tdTempType()));
7016 int offset = temp->tdTempOffs();
7017 printf(" [%2s%1s0x%02X]\n", isFramePointerUsed() ? STR_FPBASE : STR_SPBASE, (offset < 0 ? "-" : "+"),
7018 (offset < 0 ? -offset : offset));
7021 if (curState >= TENTATIVE_FRAME_LAYOUT)
7024 printf("; Lcl frame size = %d\n", compLclFrameSize);
7029 /*****************************************************************************
7031 * Conservatively estimate the layout of the stack frame.
7033 * This function is only used before final frame layout. It conservatively estimates the
7034 * number of callee-saved registers that must be saved, then calls lvaAssignFrameOffsets().
7035 * To do final frame layout, the callee-saved registers are known precisely, so
7036 * lvaAssignFrameOffsets() is called directly.
7038 * Returns the (conservative, that is, overly large) estimated size of the frame,
7039 * including the callee-saved registers. This is only used by the emitter during code
7040 * generation when estimating the size of the offset of instructions accessing temps,
7041 * and only if temps have a larger offset than variables.
7044 unsigned Compiler::lvaFrameSize(FrameLayoutState curState)
7046 assert(curState < FINAL_FRAME_LAYOUT);
7050 /* Layout the stack frame conservatively.
7051 Assume all callee-saved registers are spilled to stack */
7053 compCalleeRegsPushed = CNT_CALLEE_SAVED;
7055 #if defined(_TARGET_ARMARCH_)
7056 if (compFloatingPointUsed)
7057 compCalleeRegsPushed += CNT_CALLEE_SAVED_FLOAT;
7059 compCalleeRegsPushed++; // we always push LR. See genPushCalleeSavedRegisters
7060 #elif defined(_TARGET_AMD64_)
7061 if (compFloatingPointUsed)
7063 compCalleeFPRegsSavedMask = RBM_FLT_CALLEE_SAVED;
7067 compCalleeFPRegsSavedMask = RBM_NONE;
7072 if (genDoubleAlign())
7074 // X86 only - account for extra 4-byte pad that may be created by "and esp, -8" instruction
7075 compCalleeRegsPushed++;
7079 #ifdef _TARGET_XARCH_
7080 // Since FP/EBP is included in the SAVED_REG_MAXSZ we need to
7081 // subtract 1 register if codeGen->isFramePointerUsed() is true.
7082 if (codeGen->isFramePointerUsed())
7084 compCalleeRegsPushed--;
7088 lvaAssignFrameOffsets(curState);
7090 unsigned calleeSavedRegMaxSz = CALLEE_SAVED_REG_MAXSZ;
7091 #if defined(_TARGET_ARMARCH_)
7092 if (compFloatingPointUsed)
7094 calleeSavedRegMaxSz += CALLEE_SAVED_FLOAT_MAXSZ;
7096 calleeSavedRegMaxSz += REGSIZE_BYTES; // we always push LR. See genPushCalleeSavedRegisters
7099 result = compLclFrameSize + calleeSavedRegMaxSz;
7103 //------------------------------------------------------------------------
7104 // lvaGetSPRelativeOffset: Given a variable, return the offset of that
7105 // variable in the frame from the stack pointer. This number will be positive,
7106 // since the stack pointer must be at a lower address than everything on the
7109 // This can't be called for localloc functions, since the stack pointer
7110 // varies, and thus there is no fixed offset to a variable from the stack pointer.
7113 // varNum - the variable number
7118 int Compiler::lvaGetSPRelativeOffset(unsigned varNum)
7120 assert(!compLocallocUsed);
7121 assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7122 assert(varNum < lvaCount);
7123 const LclVarDsc* varDsc = lvaTable + varNum;
7124 assert(varDsc->lvOnFrame);
7125 int spRelativeOffset;
7127 if (varDsc->lvFramePointerBased)
7129 // The stack offset is relative to the frame pointer, so convert it to be
7130 // relative to the stack pointer (which makes no sense for localloc functions).
7131 spRelativeOffset = varDsc->lvStkOffs + codeGen->genSPtoFPdelta();
7135 spRelativeOffset = varDsc->lvStkOffs;
7138 assert(spRelativeOffset >= 0);
7139 return spRelativeOffset;
7142 /*****************************************************************************
7144 * Return the caller-SP-relative stack offset of a local/parameter.
7145 * Requires the local to be on the stack and frame layout to be complete.
7148 int Compiler::lvaGetCallerSPRelativeOffset(unsigned varNum)
7150 assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7151 assert(varNum < lvaCount);
7152 LclVarDsc* varDsc = lvaTable + varNum;
7153 assert(varDsc->lvOnFrame);
7155 return lvaToCallerSPRelativeOffset(varDsc->lvStkOffs, varDsc->lvFramePointerBased);
7158 int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased)
7160 assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7164 offset += codeGen->genCallerSPtoFPdelta();
7168 offset += codeGen->genCallerSPtoInitialSPdelta();
7174 /*****************************************************************************
7176 * Return the Initial-SP-relative stack offset of a local/parameter.
7177 * Requires the local to be on the stack and frame layout to be complete.
7180 int Compiler::lvaGetInitialSPRelativeOffset(unsigned varNum)
7182 assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7183 assert(varNum < lvaCount);
7184 LclVarDsc* varDsc = lvaTable + varNum;
7185 assert(varDsc->lvOnFrame);
7187 return lvaToInitialSPRelativeOffset(varDsc->lvStkOffs, varDsc->lvFramePointerBased);
7190 // Given a local variable offset, and whether that offset is frame-pointer based, return its offset from Initial-SP.
7191 // This is used, for example, to figure out the offset of the frame pointer from Initial-SP.
7192 int Compiler::lvaToInitialSPRelativeOffset(unsigned offset, bool isFpBased)
7194 assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7195 #ifdef _TARGET_AMD64_
7198 // Currently, the frame starts by pushing ebp, ebp points to the saved ebp
7199 // (so we have ebp pointer chaining). Add the fixed-size frame size plus the
7200 // size of the callee-saved regs (not including ebp itself) to find Initial-SP.
7202 assert(codeGen->isFramePointerUsed());
7203 offset += codeGen->genSPtoFPdelta();
7207 // The offset is correct already!
7209 #else // !_TARGET_AMD64_
7210 NYI("lvaToInitialSPRelativeOffset");
7211 #endif // !_TARGET_AMD64_
7216 /*****************************************************************************/
7219 /*****************************************************************************
7220 * Pick a padding size at "random" for the local.
7221 * 0 means that it should not be converted to a GT_LCL_FLD
7224 static unsigned LCL_FLD_PADDING(unsigned lclNum)
7226 // Convert every 2nd variable
7232 // Pick a padding size at "random"
7233 unsigned size = lclNum % 7;
7238 /*****************************************************************************
7240 * Callback for fgWalkAllTreesPre()
7241 * Convert as many GT_LCL_VAR's to GT_LCL_FLD's
7246 The stress mode does 2 passes.
7248 In the first pass we will mark the locals where we CAN't apply the stress mode.
7249 In the second pass we will do the appropiate morphing wherever we've not determined we can't do it.
7251 Compiler::fgWalkResult Compiler::lvaStressLclFldCB(GenTree** pTree, fgWalkData* data)
7253 GenTree* tree = *pTree;
7254 genTreeOps oper = tree->OperGet();
7264 if (tree->gtOp.gtOp1->gtOper != GT_LCL_VAR)
7266 return WALK_CONTINUE;
7268 lcl = tree->gtOp.gtOp1;
7272 return WALK_CONTINUE;
7275 Compiler* pComp = ((lvaStressLclFldArgs*)data->pCallbackData)->m_pCompiler;
7276 bool bFirstPass = ((lvaStressLclFldArgs*)data->pCallbackData)->m_bFirstPass;
7277 noway_assert(lcl->gtOper == GT_LCL_VAR);
7278 unsigned lclNum = lcl->gtLclVarCommon.gtLclNum;
7279 var_types type = lcl->TypeGet();
7280 LclVarDsc* varDsc = &pComp->lvaTable[lclNum];
7282 if (varDsc->lvNoLclFldStress)
7284 // Already determined we can't do anything for this var
7285 return WALK_SKIP_SUBTREES;
7290 // Ignore arguments and temps
7291 if (varDsc->lvIsParam || lclNum >= pComp->info.compLocalsCount)
7293 varDsc->lvNoLclFldStress = true;
7294 return WALK_SKIP_SUBTREES;
7297 // Fix for lcl_fld stress mode
7298 if (varDsc->lvKeepType)
7300 varDsc->lvNoLclFldStress = true;
7301 return WALK_SKIP_SUBTREES;
7304 // Can't have GC ptrs in TYP_BLK.
7305 if (!varTypeIsArithmetic(type))
7307 varDsc->lvNoLclFldStress = true;
7308 return WALK_SKIP_SUBTREES;
7311 // Weed out "small" types like TYP_BYTE as we don't mark the GT_LCL_VAR
7312 // node with the accurate small type. If we bash lvaTable[].lvType,
7313 // then there will be no indication that it was ever a small type.
7314 var_types varType = varDsc->TypeGet();
7315 if (varType != TYP_BLK && genTypeSize(varType) != genTypeSize(genActualType(varType)))
7317 varDsc->lvNoLclFldStress = true;
7318 return WALK_SKIP_SUBTREES;
7321 // Offset some of the local variable by a "random" non-zero amount
7322 unsigned padding = LCL_FLD_PADDING(lclNum);
7325 varDsc->lvNoLclFldStress = true;
7326 return WALK_SKIP_SUBTREES;
7332 noway_assert(varDsc->lvType == lcl->gtType || varDsc->lvType == TYP_BLK);
7333 var_types varType = varDsc->TypeGet();
7335 // Calculate padding
7336 unsigned padding = LCL_FLD_PADDING(lclNum);
7338 #ifdef _TARGET_ARMARCH_
7339 // We need to support alignment requirements to access memory on ARM ARCH
7340 unsigned alignment = 1;
7341 pComp->codeGen->InferOpSizeAlign(lcl, &alignment);
7342 alignment = (unsigned)roundUp(alignment, TARGET_POINTER_SIZE);
7343 padding = (unsigned)roundUp(padding, alignment);
7344 #endif // _TARGET_ARMARCH_
7346 // Change the variable to a TYP_BLK
7347 if (varType != TYP_BLK)
7349 varDsc->lvExactSize = (unsigned)(roundUp(padding + pComp->lvaLclSize(lclNum), TARGET_POINTER_SIZE));
7350 varDsc->lvType = TYP_BLK;
7351 pComp->lvaSetVarAddrExposed(lclNum);
7354 tree->gtFlags |= GTF_GLOB_REF;
7356 /* Now morph the tree appropriately */
7357 if (oper == GT_LCL_VAR)
7359 /* Change lclVar(lclNum) to lclFld(lclNum,padding) */
7361 tree->ChangeOper(GT_LCL_FLD);
7362 tree->gtLclFld.gtLclOffs = padding;
7366 /* Change addr(lclVar) to addr(lclVar)+padding */
7368 noway_assert(oper == GT_ADDR);
7369 GenTree* paddingTree = pComp->gtNewIconNode(padding);
7370 GenTree* newAddr = pComp->gtNewOperNode(GT_ADD, tree->gtType, tree, paddingTree);
7374 lcl->gtType = TYP_BLK;
7378 return WALK_SKIP_SUBTREES;
7381 /*****************************************************************************/
7383 void Compiler::lvaStressLclFld()
7385 if (!compStressCompile(STRESS_LCL_FLDS, 5))
7390 lvaStressLclFldArgs Args;
7391 Args.m_pCompiler = this;
7392 Args.m_bFirstPass = true;
7395 fgWalkAllTreesPre(lvaStressLclFldCB, &Args);
7398 Args.m_bFirstPass = false;
7399 fgWalkAllTreesPre(lvaStressLclFldCB, &Args);
7404 /*****************************************************************************
7406 * A little routine that displays a local variable bitset.
7407 * 'set' is mask of variables that have to be displayed
7408 * 'allVars' is the complete set of interesting variables (blank space is
7409 * inserted if its corresponding bit is not in 'set').
7413 void Compiler::lvaDispVarSet(VARSET_VALARG_TP set)
7415 VARSET_TP allVars(VarSetOps::MakeEmpty(this));
7416 lvaDispVarSet(set, allVars);
7419 void Compiler::lvaDispVarSet(VARSET_VALARG_TP set, VARSET_VALARG_TP allVars)
7423 bool needSpace = false;
7425 for (unsigned index = 0; index < lvaTrackedCount; index++)
7427 if (VarSetOps::IsMember(this, set, index))
7432 /* Look for the matching variable */
7434 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
7436 if ((varDsc->lvVarIndex == index) && varDsc->lvTracked)
7451 printf("V%02u", lclNum);
7453 else if (VarSetOps::IsMember(this, allVars, index))