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 lvaRefCountState = RCS_INVALID;
40 lvaGenericsContextUseCount = 0;
42 lvaTrackedToVarNum = nullptr;
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 #ifdef JIT32_GCENCODER
61 lvaLocAllocSPvar = BAD_VAR_NUM;
62 #endif // JIT32_GCENCODER
63 lvaNewObjArrayArgs = BAD_VAR_NUM;
64 lvaGSSecurityCookie = BAD_VAR_NUM;
66 lvaVarargsBaseOfStkArgs = BAD_VAR_NUM;
67 #endif // _TARGET_X86_
68 lvaVarargsHandleArg = BAD_VAR_NUM;
69 lvaSecurityObject = BAD_VAR_NUM;
70 lvaStubArgumentVar = BAD_VAR_NUM;
71 lvaArg0Var = BAD_VAR_NUM;
72 lvaMonAcquired = BAD_VAR_NUM;
74 lvaInlineeReturnSpillTemp = BAD_VAR_NUM;
76 gsShadowVarInfo = nullptr;
77 #if FEATURE_EH_FUNCLETS
78 lvaPSPSym = BAD_VAR_NUM;
81 lvaSIMDInitTempVarNum = BAD_VAR_NUM;
82 #endif // FEATURE_SIMD
85 structPromotionHelper = new (this, CMK_Generic) StructPromotionHelper(this);
88 /*****************************************************************************/
90 void Compiler::lvaInitTypeRef()
93 /* x86 args look something like this:
94 [this ptr] [hidden return buffer] [declared arguments]* [generic context] [var arg cookie]
96 x64 is closer to the native ABI:
97 [this ptr] [hidden return buffer] [generic context] [var arg cookie] [declared arguments]*
98 (Note: prior to .NET Framework 4.5.1 for Windows 8.1 (but not .NET Framework 4.5.1 "downlevel"),
99 the "hidden return buffer" came before the "this ptr". Now, the "this ptr" comes first. This
100 is different from the C++ order, where the "hidden return buffer" always comes first.)
102 ARM and ARM64 are the same as the current x64 convention:
103 [this ptr] [hidden return buffer] [generic context] [var arg cookie] [declared arguments]*
106 The var arg cookie and generic context are swapped with respect to the user arguments
109 /* Set compArgsCount and compLocalsCount */
111 info.compArgsCount = info.compMethodInfo->args.numArgs;
113 // Is there a 'this' pointer
115 if (!info.compIsStatic)
117 info.compArgsCount++;
121 info.compThisArg = BAD_VAR_NUM;
124 info.compILargsCount = info.compArgsCount;
127 if (featureSIMD && (info.compRetNativeType == TYP_STRUCT))
129 var_types structType = impNormStructType(info.compMethodInfo->args.retTypeClass);
130 info.compRetType = structType;
132 #endif // FEATURE_SIMD
134 // Are we returning a struct using a return buffer argument?
136 const bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo);
138 // Possibly change the compRetNativeType from TYP_STRUCT to a "primitive" type
139 // when we are returning a struct by value and it fits in one register
141 if (!hasRetBuffArg && varTypeIsStruct(info.compRetNativeType))
143 CORINFO_CLASS_HANDLE retClsHnd = info.compMethodInfo->args.retTypeClass;
145 Compiler::structPassingKind howToReturnStruct;
146 var_types returnType = getReturnTypeForStruct(retClsHnd, &howToReturnStruct);
148 // We can safely widen the return type for enclosed structs.
149 if ((howToReturnStruct == SPK_PrimitiveType) || (howToReturnStruct == SPK_EnclosingType))
151 assert(returnType != TYP_UNKNOWN);
152 assert(!varTypeIsStruct(returnType));
154 info.compRetNativeType = returnType;
156 // ToDo: Refactor this common code sequence into its own method as it is used 4+ times
157 if ((returnType == TYP_LONG) && (compLongUsed == false))
161 else if (((returnType == TYP_FLOAT) || (returnType == TYP_DOUBLE)) && (compFloatingPointUsed == false))
163 compFloatingPointUsed = true;
168 // Do we have a RetBuffArg?
172 info.compArgsCount++;
176 info.compRetBuffArg = BAD_VAR_NUM;
179 /* There is a 'hidden' cookie pushed last when the
180 calling convention is varargs */
182 if (info.compIsVarArgs)
184 info.compArgsCount++;
187 // Is there an extra parameter used to pass instantiation info to
188 // shared generic methods and shared generic struct instance methods?
189 if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
191 info.compArgsCount++;
195 info.compTypeCtxtArg = BAD_VAR_NUM;
198 lvaCount = info.compLocalsCount = info.compArgsCount + info.compMethodInfo->locals.numArgs;
200 info.compILlocalsCount = info.compILargsCount + info.compMethodInfo->locals.numArgs;
202 /* Now allocate the variable descriptor table */
204 if (compIsForInlining())
206 lvaTable = impInlineInfo->InlinerCompiler->lvaTable;
207 lvaCount = impInlineInfo->InlinerCompiler->lvaCount;
208 lvaTableCnt = impInlineInfo->InlinerCompiler->lvaTableCnt;
210 // No more stuff needs to be done.
214 lvaTableCnt = lvaCount * 2;
216 if (lvaTableCnt < 16)
221 lvaTable = getAllocator(CMK_LvaTable).allocate<LclVarDsc>(lvaTableCnt);
222 size_t tableSize = lvaTableCnt * sizeof(*lvaTable);
223 memset(lvaTable, 0, tableSize);
224 for (unsigned i = 0; i < lvaTableCnt; i++)
226 new (&lvaTable[i], jitstd::placement_t()) LclVarDsc(); // call the constructor.
229 //-------------------------------------------------------------------------
230 // Count the arguments and initialize the respective lvaTable[] entries
232 // First the implicit arguments
233 //-------------------------------------------------------------------------
235 InitVarDscInfo varDscInfo;
236 varDscInfo.Init(lvaTable, hasRetBuffArg);
238 lvaInitArgs(&varDscInfo);
240 //-------------------------------------------------------------------------
241 // Finally the local variables
242 //-------------------------------------------------------------------------
244 unsigned varNum = varDscInfo.varNum;
245 LclVarDsc* varDsc = varDscInfo.varDsc;
246 CORINFO_ARG_LIST_HANDLE localsSig = info.compMethodInfo->locals.args;
248 for (unsigned i = 0; i < info.compMethodInfo->locals.numArgs;
249 i++, varNum++, varDsc++, localsSig = info.compCompHnd->getArgNext(localsSig))
251 CORINFO_CLASS_HANDLE typeHnd;
252 CorInfoTypeWithMod corInfoTypeWithMod =
253 info.compCompHnd->getArgType(&info.compMethodInfo->locals, localsSig, &typeHnd);
254 CorInfoType corInfoType = strip(corInfoTypeWithMod);
256 lvaInitVarDsc(varDsc, varNum, corInfoType, typeHnd, localsSig, &info.compMethodInfo->locals);
258 varDsc->lvPinned = ((corInfoTypeWithMod & CORINFO_TYPE_MOD_PINNED) != 0);
259 varDsc->lvOnFrame = true; // The final home for this local variable might be our local stack frame
261 if (corInfoType == CORINFO_TYPE_CLASS)
263 CORINFO_CLASS_HANDLE clsHnd = info.compCompHnd->getArgClass(&info.compMethodInfo->locals, localsSig);
264 lvaSetClass(varNum, clsHnd);
268 if ( // If there already exist unsafe buffers, don't mark more structs as unsafe
269 // as that will cause them to be placed along with the real unsafe buffers,
270 // unnecessarily exposing them to overruns. This can affect GS tests which
271 // intentionally do buffer-overruns.
272 !getNeedsGSSecurityCookie() &&
273 // GS checks require the stack to be re-ordered, which can't be done with EnC
274 !opts.compDbgEnC && compStressCompile(STRESS_UNSAFE_BUFFER_CHECKS, 25))
276 setNeedsGSSecurityCookie();
277 compGSReorderStackLayout = true;
279 for (unsigned i = 0; i < lvaCount; i++)
281 if ((lvaTable[i].lvType == TYP_STRUCT) && compStressCompile(STRESS_GENERIC_VARN, 60))
283 lvaTable[i].lvIsUnsafeBuffer = true;
288 if (getNeedsGSSecurityCookie())
290 // Ensure that there will be at least one stack variable since
291 // we require that the GSCookie does not have a 0 stack offset.
292 unsigned dummy = lvaGrabTempWithImplicitUse(false DEBUGARG("GSCookie dummy"));
293 lvaTable[dummy].lvType = TYP_INT;
296 // Allocate the lvaOutgoingArgSpaceVar now because we can run into problems in the
297 // emitter when the varNum is greater that 32767 (see emitLclVarAddr::initLclVarAddr)
298 lvaAllocOutgoingArgSpaceVar();
303 lvaTableDump(INITIAL_FRAME_LAYOUT);
308 /*****************************************************************************/
309 void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo)
313 #if defined(_TARGET_ARM_) && defined(PROFILING_SUPPORTED)
314 // Prespill all argument regs on to stack in case of Arm when under profiler.
315 if (compIsProfilerHookNeeded())
317 codeGen->regSet.rsMaskPreSpillRegArg |= RBM_ARG_REGS;
321 //----------------------------------------------------------------------
323 /* Is there a "this" pointer ? */
324 lvaInitThisPtr(varDscInfo);
326 /* If we have a hidden return-buffer parameter, that comes here */
327 lvaInitRetBuffArg(varDscInfo);
329 //======================================================================
331 #if USER_ARGS_COME_LAST
332 //@GENERICS: final instantiation-info argument for shared generic methods
333 // and shared generic struct instance methods
334 lvaInitGenericsCtxt(varDscInfo);
336 /* If the method is varargs, process the varargs cookie */
337 lvaInitVarArgsHandle(varDscInfo);
340 //-------------------------------------------------------------------------
341 // Now walk the function signature for the explicit user arguments
342 //-------------------------------------------------------------------------
343 lvaInitUserArgs(varDscInfo);
345 #if !USER_ARGS_COME_LAST
346 //@GENERICS: final instantiation-info argument for shared generic methods
347 // and shared generic struct instance methods
348 lvaInitGenericsCtxt(varDscInfo);
350 /* If the method is varargs, process the varargs cookie */
351 lvaInitVarArgsHandle(varDscInfo);
354 //----------------------------------------------------------------------
356 // We have set info.compArgsCount in compCompile()
357 noway_assert(varDscInfo->varNum == info.compArgsCount);
358 assert(varDscInfo->intRegArgNum <= MAX_REG_ARG);
360 codeGen->intRegState.rsCalleeRegArgCount = varDscInfo->intRegArgNum;
361 codeGen->floatRegState.rsCalleeRegArgCount = varDscInfo->floatRegArgNum;
363 #if FEATURE_FASTTAILCALL
364 // Save the stack usage information
365 // We can get register usage information using codeGen->intRegState and
366 // codeGen->floatRegState
367 info.compArgStackSize = varDscInfo->stackArgSize;
368 #endif // FEATURE_FASTTAILCALL
370 // The total argument size must be aligned.
371 noway_assert((compArgSize % TARGET_POINTER_SIZE) == 0);
374 /* We can not pass more than 2^16 dwords as arguments as the "ret"
375 instruction can only pop 2^16 arguments. Could be handled correctly
376 but it will be very difficult for fully interruptible code */
378 if (compArgSize != (size_t)(unsigned short)compArgSize)
379 NO_WAY("Too many arguments for the \"ret\" instruction to pop");
383 /*****************************************************************************/
384 void Compiler::lvaInitThisPtr(InitVarDscInfo* varDscInfo)
386 LclVarDsc* varDsc = varDscInfo->varDsc;
387 if (!info.compIsStatic)
389 varDsc->lvIsParam = 1;
391 varDsc->lvSingleDef = 1;
396 lvaArg0Var = info.compThisArg = varDscInfo->varNum;
397 noway_assert(info.compThisArg == 0);
399 if (eeIsValueClass(info.compClassHnd))
401 varDsc->lvType = TYP_BYREF;
405 var_types simdBaseType = TYP_UNKNOWN;
406 var_types type = impNormStructType(info.compClassHnd, nullptr, nullptr, &simdBaseType);
407 if (simdBaseType != TYP_UNKNOWN)
409 assert(varTypeIsSIMD(type));
410 varDsc->lvSIMDType = true;
411 varDsc->lvBaseType = simdBaseType;
412 varDsc->lvExactSize = genTypeSize(type);
415 #endif // FEATURE_SIMD
419 varDsc->lvType = TYP_REF;
420 lvaSetClass(varDscInfo->varNum, info.compClassHnd);
423 if (tiVerificationNeeded)
425 varDsc->lvVerTypeInfo = verMakeTypeInfo(info.compClassHnd);
427 if (varDsc->lvVerTypeInfo.IsValueClass())
429 varDsc->lvVerTypeInfo.MakeByRef();
434 varDsc->lvVerTypeInfo = typeInfo();
437 // Mark the 'this' pointer for the method
438 varDsc->lvVerTypeInfo.SetIsThisPtr();
440 varDsc->lvIsRegArg = 1;
441 noway_assert(varDscInfo->intRegArgNum == 0);
443 varDsc->lvArgReg = genMapRegArgNumToRegNum(varDscInfo->allocRegArg(TYP_INT), varDsc->TypeGet());
444 #if FEATURE_MULTIREG_ARGS
445 varDsc->lvOtherArgReg = REG_NA;
447 varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
452 printf("'this' passed in register %s\n", getRegName(varDsc->lvArgReg));
455 compArgSize += TARGET_POINTER_SIZE;
457 varDscInfo->varNum++;
458 varDscInfo->varDsc++;
462 /*****************************************************************************/
463 void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo)
465 LclVarDsc* varDsc = varDscInfo->varDsc;
466 bool hasRetBuffArg = impMethodInfo_hasRetBuffArg(info.compMethodInfo);
468 // These two should always match
469 noway_assert(hasRetBuffArg == varDscInfo->hasRetBufArg);
473 info.compRetBuffArg = varDscInfo->varNum;
474 varDsc->lvType = TYP_BYREF;
475 varDsc->lvIsParam = 1;
476 varDsc->lvIsRegArg = 1;
478 varDsc->lvSingleDef = 1;
480 if (hasFixedRetBuffReg())
482 varDsc->lvArgReg = theFixedRetBuffReg();
486 unsigned retBuffArgNum = varDscInfo->allocRegArg(TYP_INT);
487 varDsc->lvArgReg = genMapIntRegArgNumToRegNum(retBuffArgNum);
490 #if FEATURE_MULTIREG_ARGS
491 varDsc->lvOtherArgReg = REG_NA;
493 varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
495 info.compRetBuffDefStack = 0;
496 if (info.compRetType == TYP_STRUCT)
498 CORINFO_SIG_INFO sigInfo;
499 info.compCompHnd->getMethodSig(info.compMethodHnd, &sigInfo);
500 assert(JITtype2varType(sigInfo.retType) == info.compRetType); // Else shouldn't have a ret buff.
502 info.compRetBuffDefStack =
503 (info.compCompHnd->isStructRequiringStackAllocRetBuf(sigInfo.retTypeClass) == TRUE);
504 if (info.compRetBuffDefStack)
506 // If we're assured that the ret buff argument points into a callers stack, we will type it as
508 // (native int/unmanaged pointer) so that it's not tracked as a GC ref.
509 varDsc->lvType = TYP_I_IMPL;
513 else if (featureSIMD && varTypeIsSIMD(info.compRetType))
515 varDsc->lvSIMDType = true;
517 getBaseTypeAndSizeOfSIMDType(info.compMethodInfo->args.retTypeClass, &varDsc->lvExactSize);
518 assert(varDsc->lvBaseType != TYP_UNKNOWN);
520 #endif // FEATURE_SIMD
522 assert(isValidIntArgReg(varDsc->lvArgReg));
527 printf("'__retBuf' passed in register %s\n", getRegName(varDsc->lvArgReg));
531 /* Update the total argument size, count and varDsc */
533 compArgSize += TARGET_POINTER_SIZE;
534 varDscInfo->varNum++;
535 varDscInfo->varDsc++;
539 /*****************************************************************************/
540 void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo)
542 //-------------------------------------------------------------------------
543 // Walk the function signature for the explicit arguments
544 //-------------------------------------------------------------------------
546 #if defined(_TARGET_X86_)
547 // Only (some of) the implicit args are enregistered for varargs
548 varDscInfo->maxIntRegArgNum = info.compIsVarArgs ? varDscInfo->intRegArgNum : MAX_REG_ARG;
549 #elif defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
550 // On System V type environment the float registers are not indexed together with the int ones.
551 varDscInfo->floatRegArgNum = varDscInfo->intRegArgNum;
554 CORINFO_ARG_LIST_HANDLE argLst = info.compMethodInfo->args.args;
556 const unsigned argSigLen = info.compMethodInfo->args.numArgs;
558 regMaskTP doubleAlignMask = RBM_NONE;
559 for (unsigned i = 0; i < argSigLen;
560 i++, varDscInfo->varNum++, varDscInfo->varDsc++, argLst = info.compCompHnd->getArgNext(argLst))
562 LclVarDsc* varDsc = varDscInfo->varDsc;
563 CORINFO_CLASS_HANDLE typeHnd = nullptr;
565 CorInfoTypeWithMod corInfoType = info.compCompHnd->getArgType(&info.compMethodInfo->args, argLst, &typeHnd);
566 varDsc->lvIsParam = 1;
568 varDsc->lvSingleDef = 1;
571 lvaInitVarDsc(varDsc, varDscInfo->varNum, strip(corInfoType), typeHnd, argLst, &info.compMethodInfo->args);
573 if (strip(corInfoType) == CORINFO_TYPE_CLASS)
575 CORINFO_CLASS_HANDLE clsHnd = info.compCompHnd->getArgClass(&info.compMethodInfo->args, argLst);
576 lvaSetClass(varDscInfo->varNum, clsHnd);
579 // For ARM, ARM64, and AMD64 varargs, all arguments go in integer registers
580 var_types argType = mangleVarArgsType(varDsc->TypeGet());
581 var_types origArgType = argType;
582 // ARM softfp calling convention should affect only the floating point arguments.
583 // Otherwise there appear too many surplus pre-spills and other memory operations
584 // with the associated locations .
585 bool isSoftFPPreSpill = opts.compUseSoftFP && varTypeIsFloating(varDsc->TypeGet());
586 unsigned argSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
587 unsigned cSlots = argSize / TARGET_POINTER_SIZE; // the total number of slots of this argument
588 bool isHfaArg = false;
589 var_types hfaType = TYP_UNDEF;
591 #if defined(_TARGET_ARM64_) && defined(_TARGET_UNIX_)
592 // Native varargs on arm64 unix use the regular calling convention.
593 if (!opts.compUseSoftFP)
595 // Methods that use VarArg or SoftFP cannot have HFA arguments
596 if (!info.compIsVarArgs && !opts.compUseSoftFP)
597 #endif // defined(_TARGET_ARM64_) && defined(_TARGET_UNIX_)
599 // If the argType is a struct, then check if it is an HFA
600 if (varTypeIsStruct(argType))
602 hfaType = GetHfaType(typeHnd); // set to float or double if it is an HFA, otherwise TYP_UNDEF
603 isHfaArg = varTypeIsFloating(hfaType);
606 else if (info.compIsVarArgs)
609 // Currently native varargs is not implemented on non windows targets.
611 // Note that some targets like Arm64 Unix should not need much work as
612 // the ABI is the same. While other targets may only need small changes
613 // such as amd64 Unix, which just expects RAX to pass numFPArguments.
614 NYI("InitUserArgs for Vararg callee is not yet implemented on non Windows targets.");
620 // We have an HFA argument, so from here on out treat the type as a float or double.
621 // The orginal struct type is available by using origArgType
622 // We also update the cSlots to be the number of float/double fields in the HFA
624 cSlots = varDsc->lvHfaSlots();
626 // The number of slots that must be enregistered if we are to consider this argument enregistered.
627 // This is normally the same as cSlots, since we normally either enregister the entire object,
628 // or none of it. For structs on ARM, however, we only need to enregister a single slot to consider
629 // it enregistered, as long as we can split the rest onto the stack.
630 unsigned cSlotsToEnregister = cSlots;
632 #if defined(_TARGET_ARM64_) && FEATURE_ARG_SPLIT
634 // On arm64 Windows we will need to properly handle the case where a >8byte <=16byte
635 // struct is split between register r7 and virtual stack slot s[0]
636 // We will only do this for calls to vararg methods on Windows Arm64
638 // !!This does not affect the normal arm64 calling convention or Unix Arm64!!
639 if (this->info.compIsVarArgs && argType == TYP_STRUCT)
641 if (varDscInfo->canEnreg(TYP_INT, 1) && // The beginning of the struct can go in a register
642 !varDscInfo->canEnreg(TYP_INT, cSlots)) // The end of the struct can't fit in a register
644 cSlotsToEnregister = 1; // Force the split
648 #endif // defined(_TARGET_ARM64_) && FEATURE_ARG_SPLIT
651 // On ARM we pass the first 4 words of integer arguments and non-HFA structs in registers.
652 // But we pre-spill user arguments in varargs methods and structs.
655 bool preSpill = info.compIsVarArgs || isSoftFPPreSpill;
660 assert(varDsc->lvSize() == argSize);
661 cAlign = varDsc->lvStructDoubleAlign ? 2 : 1;
663 // HFA arguments go on the stack frame. They don't get spilled in the prolog like struct
664 // arguments passed in the integer registers but get homed immediately after the prolog.
667 // TODO-Arm32-Windows: vararg struct should be forced to split like
669 cSlotsToEnregister = 1; // HFAs must be totally enregistered or not, but other structs can be split.
684 if (isRegParamType(argType))
686 compArgSize += varDscInfo->alignReg(argType, cAlign) * REGSIZE_BYTES;
689 if (argType == TYP_STRUCT)
691 // Are we going to split the struct between registers and stack? We can do that as long as
692 // no floating-point arguments have been put on the stack.
694 // From the ARM Procedure Call Standard:
695 // Rule C.5: "If the NCRN is less than r4 **and** the NSAA is equal to the SP,"
696 // then split the argument between registers and stack. Implication: if something
697 // has already been spilled to the stack, then anything that would normally be
698 // split between the core registers and the stack will be put on the stack.
699 // Anything that follows will also be on the stack. However, if something from
700 // floating point regs has been spilled to the stack, we can still use r0-r3 until they are full.
702 if (varDscInfo->canEnreg(TYP_INT, 1) && // The beginning of the struct can go in a register
703 !varDscInfo->canEnreg(TYP_INT, cSlots) && // The end of the struct can't fit in a register
704 varDscInfo->existAnyFloatStackArgs()) // There's at least one stack-based FP arg already
706 varDscInfo->setAllRegArgUsed(TYP_INT); // Prevent all future use of integer registers
707 preSpill = false; // This struct won't be prespilled, since it will go on the stack
713 for (unsigned ix = 0; ix < cSlots; ix++)
715 if (!varDscInfo->canEnreg(TYP_INT, ix + 1))
719 regMaskTP regMask = genMapArgNumToRegMask(varDscInfo->regArgNum(TYP_INT) + ix, TYP_INT);
722 doubleAlignMask |= regMask;
724 codeGen->regSet.rsMaskPreSpillRegArg |= regMask;
727 #else // !_TARGET_ARM_
728 #if defined(UNIX_AMD64_ABI)
729 SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
730 if (varTypeIsStruct(argType))
732 assert(typeHnd != nullptr);
733 eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc);
734 if (structDesc.passedInRegisters)
736 unsigned intRegCount = 0;
737 unsigned floatRegCount = 0;
739 for (unsigned int i = 0; i < structDesc.eightByteCount; i++)
741 if (structDesc.IsIntegralSlot(i))
745 else if (structDesc.IsSseSlot(i))
751 assert(false && "Invalid eightbyte classification type.");
756 if (intRegCount != 0 && !varDscInfo->canEnreg(TYP_INT, intRegCount))
758 structDesc.passedInRegisters = false; // No register to enregister the eightbytes.
761 if (floatRegCount != 0 && !varDscInfo->canEnreg(TYP_FLOAT, floatRegCount))
763 structDesc.passedInRegisters = false; // No register to enregister the eightbytes.
767 #endif // UNIX_AMD64_ABI
768 #endif // !_TARGET_ARM_
770 // The final home for this incoming register might be our local stack frame.
771 // For System V platforms the final home will always be on the local stack frame.
772 varDsc->lvOnFrame = true;
774 bool canPassArgInRegisters = false;
776 #if defined(UNIX_AMD64_ABI)
777 if (varTypeIsStruct(argType))
779 canPassArgInRegisters = structDesc.passedInRegisters;
782 #endif // defined(UNIX_AMD64_ABI)
784 canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister);
787 if (canPassArgInRegisters)
789 /* Another register argument */
791 // Allocate the registers we need. allocRegArg() returns the first argument register number of the set.
792 // For non-HFA structs, we still "try" to enregister the whole thing; it will just max out if splitting
793 // to the stack happens.
794 unsigned firstAllocatedRegArgNum = 0;
796 #if FEATURE_MULTIREG_ARGS
797 varDsc->lvOtherArgReg = REG_NA;
798 #endif // FEATURE_MULTIREG_ARGS
800 #if defined(UNIX_AMD64_ABI)
801 unsigned secondAllocatedRegArgNum = 0;
802 var_types firstEightByteType = TYP_UNDEF;
803 var_types secondEightByteType = TYP_UNDEF;
805 if (varTypeIsStruct(argType))
807 if (structDesc.eightByteCount >= 1)
809 firstEightByteType = GetEightByteType(structDesc, 0);
810 firstAllocatedRegArgNum = varDscInfo->allocRegArg(firstEightByteType, 1);
814 #endif // defined(UNIX_AMD64_ABI)
816 firstAllocatedRegArgNum = varDscInfo->allocRegArg(argType, cSlots);
821 // We need to save the fact that this HFA is enregistered
822 varDsc->lvSetIsHfa();
823 varDsc->lvSetIsHfaRegArg();
824 varDsc->SetHfaType(hfaType);
825 varDsc->lvIsMultiRegArg = (varDsc->lvHfaSlots() > 1);
828 varDsc->lvIsRegArg = 1;
830 #if FEATURE_MULTIREG_ARGS
831 if (varTypeIsStruct(argType))
833 #if defined(UNIX_AMD64_ABI)
834 varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, firstEightByteType);
836 // If there is a second eightbyte, get a register for it too and map the arg to the reg number.
837 if (structDesc.eightByteCount >= 2)
839 secondEightByteType = GetEightByteType(structDesc, 1);
840 secondAllocatedRegArgNum = varDscInfo->allocRegArg(secondEightByteType, 1);
843 if (secondEightByteType != TYP_UNDEF)
845 varDsc->lvOtherArgReg = genMapRegArgNumToRegNum(secondAllocatedRegArgNum, secondEightByteType);
847 #else // ARM32 or ARM64
848 varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, TYP_I_IMPL);
849 #ifdef _TARGET_ARM64_
852 varDsc->lvOtherArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum + 1, TYP_I_IMPL);
854 #endif // _TARGET_ARM64_
855 #endif // defined(UNIX_AMD64_ABI)
858 #endif // FEATURE_MULTIREG_ARGS
860 varDsc->lvArgReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argType);
864 if (varDsc->TypeGet() == TYP_LONG)
866 varDsc->lvOtherReg = genMapRegArgNumToRegNum(firstAllocatedRegArgNum + 1, TYP_INT);
868 #endif // _TARGET_ARM_
873 printf("Arg #%u passed in register(s) ", varDscInfo->varNum);
874 bool isFloat = false;
875 #if defined(UNIX_AMD64_ABI)
876 if (varTypeIsStruct(argType) && (structDesc.eightByteCount >= 1))
878 isFloat = varTypeIsFloating(firstEightByteType);
883 isFloat = varTypeIsFloating(argType);
885 #endif // !UNIX_AMD64_ABI
887 #if defined(UNIX_AMD64_ABI)
888 if (varTypeIsStruct(argType))
890 // Print both registers, just to be clear
891 if (firstEightByteType == TYP_UNDEF)
893 printf("firstEightByte: <not used>");
897 printf("firstEightByte: %s",
898 getRegName(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, firstEightByteType),
902 if (secondEightByteType == TYP_UNDEF)
904 printf(", secondEightByte: <not used>");
908 printf(", secondEightByte: %s",
909 getRegName(genMapRegArgNumToRegNum(secondAllocatedRegArgNum, secondEightByteType),
910 varTypeIsFloating(secondEightByteType)));
914 #endif // defined(UNIX_AMD64_ABI)
916 isFloat = varTypeIsFloating(argType);
917 unsigned regArgNum = genMapRegNumToRegArgNum(varDsc->lvArgReg, argType);
919 for (unsigned ix = 0; ix < cSlots; ix++, regArgNum++)
926 if (!isFloat && (regArgNum >= varDscInfo->maxIntRegArgNum)) // a struct has been split between
927 // registers and stack
929 printf(" stack slots:%d", cSlots - ix);
936 // Print register size prefix
937 if (argType == TYP_DOUBLE)
939 // Print both registers, just to be clear
940 printf("%s/%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat),
941 getRegName(genMapRegArgNumToRegNum(regArgNum + 1, argType), isFloat));
943 // doubles take 2 slots
944 assert(ix + 1 < cSlots);
950 printf("%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat));
954 #endif // _TARGET_ARM_
956 printf("%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType), isFloat));
963 } // end if (canPassArgInRegisters)
966 #if defined(_TARGET_ARM_)
967 varDscInfo->setAllRegArgUsed(argType);
968 if (varTypeIsFloating(argType))
970 varDscInfo->setAnyFloatStackArgs();
973 #elif defined(_TARGET_ARM64_)
975 // If we needed to use the stack in order to pass this argument then
976 // record the fact that we have used up any remaining registers of this 'type'
977 // This prevents any 'backfilling' from occuring on ARM64
979 varDscInfo->setAllRegArgUsed(argType);
981 #endif // _TARGET_XXX_
983 #if FEATURE_FASTTAILCALL
984 varDscInfo->stackArgSize += roundUp(argSize, TARGET_POINTER_SIZE);
985 #endif // FEATURE_FASTTAILCALL
988 #ifdef UNIX_AMD64_ABI
989 // The arg size is returning the number of bytes of the argument. For a struct it could return a size not a
990 // multiple of TARGET_POINTER_SIZE. The stack allocated space should always be multiple of TARGET_POINTER_SIZE,
992 compArgSize += roundUp(argSize, TARGET_POINTER_SIZE);
993 #else // !UNIX_AMD64_ABI
994 compArgSize += argSize;
995 #endif // !UNIX_AMD64_ABI
996 if (info.compIsVarArgs || isHfaArg || isSoftFPPreSpill)
998 #if defined(_TARGET_X86_)
999 varDsc->lvStkOffs = compArgSize;
1000 #else // !_TARGET_X86_
1001 // TODO-CQ: We shouldn't have to go as far as to declare these
1002 // address-exposed -- DoNotEnregister should suffice.
1003 lvaSetVarAddrExposed(varDscInfo->varNum);
1004 #endif // !_TARGET_X86_
1006 } // for each user arg
1009 if (doubleAlignMask != RBM_NONE)
1011 assert(RBM_ARG_REGS == 0xF);
1012 assert((doubleAlignMask & RBM_ARG_REGS) == doubleAlignMask);
1013 if (doubleAlignMask != RBM_NONE && doubleAlignMask != RBM_ARG_REGS)
1015 // doubleAlignMask can only be 0011 and/or 1100 as 'double aligned types' can
1016 // begin at r0 or r2.
1017 assert(doubleAlignMask == 0x3 || doubleAlignMask == 0xC /* || 0xF is if'ed out */);
1019 // Now if doubleAlignMask is 0011 i.e., {r0,r1} and we prespill r2 or r3
1020 // but not both, then the stack would be misaligned for r0. So spill both
1023 // ; +0 --- caller SP double aligned ----
1026 // ; -c r0 r0 <-- misaligned.
1027 // ; callee saved regs
1028 if (doubleAlignMask == 0x3 && doubleAlignMask != codeGen->regSet.rsMaskPreSpillRegArg)
1030 codeGen->regSet.rsMaskPreSpillAlign =
1031 (~codeGen->regSet.rsMaskPreSpillRegArg & ~doubleAlignMask) & RBM_ARG_REGS;
1035 #endif // _TARGET_ARM_
1038 /*****************************************************************************/
1039 void Compiler::lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo)
1041 //@GENERICS: final instantiation-info argument for shared generic methods
1042 // and shared generic struct instance methods
1043 if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
1045 info.compTypeCtxtArg = varDscInfo->varNum;
1047 LclVarDsc* varDsc = varDscInfo->varDsc;
1048 varDsc->lvIsParam = 1;
1050 varDsc->lvSingleDef = 1;
1053 varDsc->lvType = TYP_I_IMPL;
1055 if (varDscInfo->canEnreg(TYP_I_IMPL))
1057 /* Another register argument */
1059 varDsc->lvIsRegArg = 1;
1060 varDsc->lvArgReg = genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet());
1061 #if FEATURE_MULTIREG_ARGS
1062 varDsc->lvOtherArgReg = REG_NA;
1064 varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
1066 varDscInfo->intRegArgNum++;
1071 printf("'GenCtxt' passed in register %s\n", getRegName(varDsc->lvArgReg));
1077 // We need to mark these as being on the stack, as this is not done elsewhere in the case that canEnreg
1079 varDsc->lvOnFrame = true;
1080 #if FEATURE_FASTTAILCALL
1081 varDscInfo->stackArgSize += TARGET_POINTER_SIZE;
1082 #endif // FEATURE_FASTTAILCALL
1085 compArgSize += TARGET_POINTER_SIZE;
1087 #if defined(_TARGET_X86_)
1088 if (info.compIsVarArgs)
1089 varDsc->lvStkOffs = compArgSize;
1090 #endif // _TARGET_X86_
1092 varDscInfo->varNum++;
1093 varDscInfo->varDsc++;
1097 /*****************************************************************************/
1098 void Compiler::lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo)
1100 if (info.compIsVarArgs)
1102 lvaVarargsHandleArg = varDscInfo->varNum;
1104 LclVarDsc* varDsc = varDscInfo->varDsc;
1105 varDsc->lvType = TYP_I_IMPL;
1106 varDsc->lvIsParam = 1;
1107 // Make sure this lives in the stack -- address may be reported to the VM.
1108 // TODO-CQ: This should probably be:
1109 // lvaSetVarDoNotEnregister(varDscInfo->varNum DEBUGARG(DNER_VMNeedsStackAddr));
1110 // But that causes problems, so, for expedience, I switched back to this heavyweight
1111 // hammer. But I think it should be possible to switch; it may just work now
1112 // that other problems are fixed.
1113 lvaSetVarAddrExposed(varDscInfo->varNum);
1116 varDsc->lvSingleDef = 1;
1119 if (varDscInfo->canEnreg(TYP_I_IMPL))
1121 /* Another register argument */
1123 unsigned varArgHndArgNum = varDscInfo->allocRegArg(TYP_I_IMPL);
1125 varDsc->lvIsRegArg = 1;
1126 varDsc->lvArgReg = genMapRegArgNumToRegNum(varArgHndArgNum, TYP_I_IMPL);
1127 #if FEATURE_MULTIREG_ARGS
1128 varDsc->lvOtherArgReg = REG_NA;
1130 varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
1132 // This has to be spilled right in front of the real arguments and we have
1133 // to pre-spill all the argument registers explicitly because we only have
1134 // have symbols for the declared ones, not any potential variadic ones.
1135 for (unsigned ix = varArgHndArgNum; ix < ArrLen(intArgMasks); ix++)
1137 codeGen->regSet.rsMaskPreSpillRegArg |= intArgMasks[ix];
1139 #endif // _TARGET_ARM_
1144 printf("'VarArgHnd' passed in register %s\n", getRegName(varDsc->lvArgReg));
1150 // We need to mark these as being on the stack, as this is not done elsewhere in the case that canEnreg
1152 varDsc->lvOnFrame = true;
1153 #if FEATURE_FASTTAILCALL
1154 varDscInfo->stackArgSize += TARGET_POINTER_SIZE;
1155 #endif // FEATURE_FASTTAILCALL
1158 /* Update the total argument size, count and varDsc */
1160 compArgSize += TARGET_POINTER_SIZE;
1162 varDscInfo->varNum++;
1163 varDscInfo->varDsc++;
1165 #if defined(_TARGET_X86_)
1166 varDsc->lvStkOffs = compArgSize;
1168 // Allocate a temp to point at the beginning of the args
1170 lvaVarargsBaseOfStkArgs = lvaGrabTemp(false DEBUGARG("Varargs BaseOfStkArgs"));
1171 lvaTable[lvaVarargsBaseOfStkArgs].lvType = TYP_I_IMPL;
1173 #endif // _TARGET_X86_
1177 /*****************************************************************************/
1178 void Compiler::lvaInitVarDsc(LclVarDsc* varDsc,
1180 CorInfoType corInfoType,
1181 CORINFO_CLASS_HANDLE typeHnd,
1182 CORINFO_ARG_LIST_HANDLE varList,
1183 CORINFO_SIG_INFO* varSig)
1185 noway_assert(varDsc == &lvaTable[varNum]);
1187 switch (corInfoType)
1189 // Mark types that looks like a pointer for doing shadow-copying of
1190 // parameters if we have an unsafe buffer.
1191 // Note that this does not handle structs with pointer fields. Instead,
1192 // we rely on using the assign-groups/equivalence-groups in
1193 // gsFindVulnerableParams() to determine if a buffer-struct contains a
1194 // pointer. We could do better by having the EE determine this for us.
1195 // Note that we want to keep buffers without pointers at lower memory
1196 // addresses than buffers with pointers.
1197 case CORINFO_TYPE_PTR:
1198 case CORINFO_TYPE_BYREF:
1199 case CORINFO_TYPE_CLASS:
1200 case CORINFO_TYPE_STRING:
1201 case CORINFO_TYPE_VAR:
1202 case CORINFO_TYPE_REFANY:
1203 varDsc->lvIsPtr = 1;
1209 var_types type = JITtype2varType(corInfoType);
1210 if (varTypeIsFloating(type))
1212 compFloatingPointUsed = true;
1215 if (tiVerificationNeeded)
1217 varDsc->lvVerTypeInfo = verParseArgSigToTypeInfo(varSig, varList);
1220 if (tiVerificationNeeded)
1222 if (varDsc->lvIsParam)
1224 // For an incoming ValueType we better be able to have the full type information
1225 // so that we can layout the parameter offsets correctly
1227 if (varTypeIsStruct(type) && varDsc->lvVerTypeInfo.IsDead())
1229 BADCODE("invalid ValueType parameter");
1232 // For an incoming reference type we need to verify that the actual type is
1233 // a reference type and not a valuetype.
1235 if (type == TYP_REF &&
1236 !(varDsc->lvVerTypeInfo.IsType(TI_REF) || varDsc->lvVerTypeInfo.IsUnboxedGenericTypeVar()))
1238 BADCODE("parameter type mismatch");
1242 // Disallow byrefs to byref like objects (ArgTypeHandle)
1243 // techncally we could get away with just not setting them
1244 if (varDsc->lvVerTypeInfo.IsByRef() && verIsByRefLike(DereferenceByRef(varDsc->lvVerTypeInfo)))
1246 varDsc->lvVerTypeInfo = typeInfo();
1249 // we don't want the EE to assert in lvaSetStruct on bad sigs, so change
1250 // the JIT type to avoid even trying to call back
1251 if (varTypeIsStruct(type) && varDsc->lvVerTypeInfo.IsDead())
1259 unsigned cFlags = info.compCompHnd->getClassAttribs(typeHnd);
1261 // We can get typeHnds for primitive types, these are value types which only contain
1262 // a primitive. We will need the typeHnd to distinguish them, so we store it here.
1263 if ((cFlags & CORINFO_FLG_VALUECLASS) && !varTypeIsStruct(type))
1265 if (tiVerificationNeeded == false)
1267 // printf("This is a struct that the JIT will treat as a primitive\n");
1268 varDsc->lvVerTypeInfo = verMakeTypeInfo(typeHnd);
1272 varDsc->lvOverlappingFields = StructHasOverlappingFields(cFlags);
1275 if (varTypeIsGC(type))
1277 varDsc->lvStructGcCount = 1;
1280 // Set the lvType (before this point it is TYP_UNDEF).
1281 if ((varTypeIsStruct(type)))
1283 lvaSetStruct(varNum, typeHnd, typeHnd != nullptr, !tiVerificationNeeded);
1284 if (info.compIsVarArgs)
1286 lvaSetStructUsedAsVarArg(varNum);
1291 varDsc->lvType = type;
1295 if (type == TYP_BOOL)
1297 varDsc->lvIsBoolean = true;
1302 varDsc->lvStkOffs = BAD_STK_OFFS;
1305 #if FEATURE_MULTIREG_ARGS
1306 varDsc->lvOtherArgReg = REG_NA;
1307 #endif // FEATURE_MULTIREG_ARGS
1310 /*****************************************************************************
1311 * Returns our internal varNum for a given IL variable.
1312 * Asserts assume it is called after lvaTable[] has been set up.
1315 unsigned Compiler::compMapILvarNum(unsigned ILvarNum)
1317 noway_assert(ILvarNum < info.compILlocalsCount || ILvarNum > unsigned(ICorDebugInfo::UNKNOWN_ILNUM));
1321 if (ILvarNum == (unsigned)ICorDebugInfo::VARARGS_HND_ILNUM)
1323 // The varargs cookie is the last argument in lvaTable[]
1324 noway_assert(info.compIsVarArgs);
1326 varNum = lvaVarargsHandleArg;
1327 noway_assert(lvaTable[varNum].lvIsParam);
1329 else if (ILvarNum == (unsigned)ICorDebugInfo::RETBUF_ILNUM)
1331 noway_assert(info.compRetBuffArg != BAD_VAR_NUM);
1332 varNum = info.compRetBuffArg;
1334 else if (ILvarNum == (unsigned)ICorDebugInfo::TYPECTXT_ILNUM)
1336 noway_assert(info.compTypeCtxtArg >= 0);
1337 varNum = unsigned(info.compTypeCtxtArg);
1339 else if (ILvarNum < info.compILargsCount)
1342 varNum = compMapILargNum(ILvarNum);
1343 noway_assert(lvaTable[varNum].lvIsParam);
1345 else if (ILvarNum < info.compILlocalsCount)
1348 unsigned lclNum = ILvarNum - info.compILargsCount;
1349 varNum = info.compArgsCount + lclNum;
1350 noway_assert(!lvaTable[varNum].lvIsParam);
1357 noway_assert(varNum < info.compLocalsCount);
1361 /*****************************************************************************
1362 * Returns the IL variable number given our internal varNum.
1363 * Special return values are VARG_ILNUM, RETBUF_ILNUM, TYPECTXT_ILNUM.
1365 * Returns UNKNOWN_ILNUM if it can't be mapped.
1368 unsigned Compiler::compMap2ILvarNum(unsigned varNum)
1370 if (compIsForInlining())
1372 return impInlineInfo->InlinerCompiler->compMap2ILvarNum(varNum);
1375 noway_assert(varNum < lvaCount);
1377 if (varNum == info.compRetBuffArg)
1379 return (unsigned)ICorDebugInfo::RETBUF_ILNUM;
1382 // Is this a varargs function?
1383 if (info.compIsVarArgs && varNum == lvaVarargsHandleArg)
1385 return (unsigned)ICorDebugInfo::VARARGS_HND_ILNUM;
1388 // We create an extra argument for the type context parameter
1389 // needed for shared generic code.
1390 if ((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) && varNum == (unsigned)info.compTypeCtxtArg)
1392 return (unsigned)ICorDebugInfo::TYPECTXT_ILNUM;
1395 #if FEATURE_FIXED_OUT_ARGS
1396 if (varNum == lvaOutgoingArgSpaceVar)
1398 return (unsigned)ICorDebugInfo::UNKNOWN_ILNUM; // Cannot be mapped
1400 #endif // FEATURE_FIXED_OUT_ARGS
1402 // Now mutate varNum to remove extra parameters from the count.
1403 if ((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) && varNum > (unsigned)info.compTypeCtxtArg)
1408 if (info.compIsVarArgs && varNum > lvaVarargsHandleArg)
1413 /* Is there a hidden argument for the return buffer.
1414 Note that this code works because if the RetBuffArg is not present,
1415 compRetBuffArg will be BAD_VAR_NUM */
1416 if (info.compRetBuffArg != BAD_VAR_NUM && varNum > info.compRetBuffArg)
1421 if (varNum >= info.compLocalsCount)
1423 return (unsigned)ICorDebugInfo::UNKNOWN_ILNUM; // Cannot be mapped
1429 /*****************************************************************************
1430 * Returns true if variable "varNum" may be address-exposed.
1433 bool Compiler::lvaVarAddrExposed(unsigned varNum)
1435 noway_assert(varNum < lvaCount);
1436 LclVarDsc* varDsc = &lvaTable[varNum];
1438 return varDsc->lvAddrExposed;
1441 /*****************************************************************************
1442 * Returns true iff variable "varNum" should not be enregistered (or one of several reasons).
1445 bool Compiler::lvaVarDoNotEnregister(unsigned varNum)
1447 noway_assert(varNum < lvaCount);
1448 LclVarDsc* varDsc = &lvaTable[varNum];
1450 return varDsc->lvDoNotEnregister;
1453 /*****************************************************************************
1454 * Returns the handle to the class of the local variable varNum
1457 CORINFO_CLASS_HANDLE Compiler::lvaGetStruct(unsigned varNum)
1459 noway_assert(varNum < lvaCount);
1460 LclVarDsc* varDsc = &lvaTable[varNum];
1462 return varDsc->lvVerTypeInfo.GetClassHandleForValueClass();
1465 //--------------------------------------------------------------------------------------------
1466 // lvaFieldOffsetCmp - a static compare function passed to qsort() by Compiler::StructPromotionHelper;
1467 // compares fields' offsets.
1470 // field1 - pointer to the first field;
1471 // field2 - pointer to the second field.
1474 // 0 if the fields' offsets are equal, 1 if the first field has bigger offset, -1 otherwise.
1476 int __cdecl Compiler::lvaFieldOffsetCmp(const void* field1, const void* field2)
1478 lvaStructFieldInfo* pFieldInfo1 = (lvaStructFieldInfo*)field1;
1479 lvaStructFieldInfo* pFieldInfo2 = (lvaStructFieldInfo*)field2;
1481 if (pFieldInfo1->fldOffset == pFieldInfo2->fldOffset)
1487 return (pFieldInfo1->fldOffset > pFieldInfo2->fldOffset) ? +1 : -1;
1491 //------------------------------------------------------------------------
1492 // StructPromotionHelper constructor.
1495 // compiler - pointer to a compiler to get access to an allocator, compHandle etc.
1497 Compiler::StructPromotionHelper::StructPromotionHelper(Compiler* compiler)
1498 : compiler(compiler)
1499 , structPromotionInfo()
1501 , requiresScratchVar(false)
1502 #endif // _TARGET_ARM_
1504 , retypedFieldsMap(compiler->getAllocator(CMK_DebugOnly))
1510 //--------------------------------------------------------------------------------------------
1511 // GetRequiresScratchVar - do we need a stack area to assemble small fields in order to place them in a register.
1514 // true if there was a small promoted variable and scratch var is required .
1516 bool Compiler::StructPromotionHelper::GetRequiresScratchVar()
1518 return requiresScratchVar;
1521 #endif // _TARGET_ARM_
1523 //--------------------------------------------------------------------------------------------
1524 // TryPromoteStructVar - promote struct var if it is possible and profitable.
1527 // lclNum - struct number to try.
1530 // true if the struct var was promoted.
1532 bool Compiler::StructPromotionHelper::TryPromoteStructVar(unsigned lclNum)
1534 if (CanPromoteStructVar(lclNum))
1537 // Often-useful debugging code: if you've narrowed down a struct-promotion problem to a single
1538 // method, this allows you to select a subset of the vars to promote (by 1-based ordinal number).
1539 static int structPromoVarNum = 0;
1540 structPromoVarNum++;
1541 if (atoi(getenv("structpromovarnumlo")) <= structPromoVarNum && structPromoVarNum <= atoi(getenv("structpromovarnumhi")))
1543 if (ShouldPromoteStructVar(lclNum))
1545 PromoteStructVar(lclNum);
1553 //--------------------------------------------------------------------------------------------
1554 // CheckRetypedAsScalar - check that the fldType for this fieldHnd was retyped as requested type.
1557 // fieldHnd - the field handle;
1558 // requestedType - as which type the field was accessed;
1561 // For example it can happen when such struct A { struct B { long c } } is compiled and we access A.B.c,
1562 // it could look like "GT_FIELD struct B.c -> ADDR -> GT_FIELD struct A.B -> ADDR -> LCL_VAR A" , but
1563 // "GT_FIELD struct A.B -> ADDR -> LCL_VAR A" can be promoted to "LCL_VAR long A.B" and then
1564 // there is type mistmatch between "GT_FIELD struct B.c" and "LCL_VAR long A.B".
1566 void Compiler::StructPromotionHelper::CheckRetypedAsScalar(CORINFO_FIELD_HANDLE fieldHnd, var_types requestedType)
1568 assert(retypedFieldsMap.Lookup(fieldHnd));
1569 assert(retypedFieldsMap[fieldHnd] == requestedType);
1573 //--------------------------------------------------------------------------------------------
1574 // CanPromoteStructType - checks if the struct type can be promoted.
1577 // typeHnd - struct handle to check.
1580 // true if the struct type can be promoted.
1583 // The last analyzed type is memorized to skip the check if we ask about the same time again next.
1584 // However, it was not found profitable to memorize all analyzed types in a map.
1586 // The check initializes only nessasary fields in lvaStructPromotionInfo,
1587 // so if the promotion is rejected early than most fields will be uninitialized.
1589 bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE typeHnd)
1591 assert(compiler->eeIsValueClass(typeHnd));
1592 if (structPromotionInfo.typeHnd == typeHnd)
1594 // Asking for the same type of struct as the last time.
1595 // Nothing need to be done.
1597 return structPromotionInfo.canPromote;
1600 // Analyze this type from scratch.
1601 structPromotionInfo = lvaStructPromotionInfo(typeHnd);
1603 // sizeof(double) represents the size of the largest primitive type that we can struct promote.
1604 // In the future this may be changing to XMM_REGSIZE_BYTES.
1605 // Note: MaxOffset is used below to declare a local array, and therefore must be a compile-time constant.
1606 CLANG_FORMAT_COMMENT_ANCHOR;
1607 #if defined(FEATURE_SIMD)
1608 #if defined(_TARGET_XARCH_)
1609 // This will allow promotion of 4 Vector<T> fields on AVX2 or Vector256<T> on AVX,
1610 // or 8 Vector<T>/Vector128<T> fields on SSE2.
1611 const int MaxOffset = MAX_NumOfFieldsInPromotableStruct * YMM_REGSIZE_BYTES;
1612 #elif defined(_TARGET_ARM64_)
1613 const int MaxOffset = MAX_NumOfFieldsInPromotableStruct * FP_REGSIZE_BYTES;
1614 #endif // defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)
1615 #else // !FEATURE_SIMD
1616 const int MaxOffset = MAX_NumOfFieldsInPromotableStruct * sizeof(double);
1617 #endif // !FEATURE_SIMD
1619 assert((BYTE)MaxOffset == MaxOffset); // because lvaStructFieldInfo.fldOffset is byte-sized
1620 assert((BYTE)MAX_NumOfFieldsInPromotableStruct ==
1621 MAX_NumOfFieldsInPromotableStruct); // because lvaStructFieldInfo.fieldCnt is byte-sized
1623 bool containsGCpointers = false;
1625 COMP_HANDLE compHandle = compiler->info.compCompHnd;
1627 unsigned structSize = compHandle->getClassSize(typeHnd);
1628 if (structSize > MaxOffset)
1630 return false; // struct is too large
1633 unsigned fieldCnt = compHandle->getClassNumInstanceFields(typeHnd);
1634 if (fieldCnt == 0 || fieldCnt > MAX_NumOfFieldsInPromotableStruct)
1636 return false; // struct must have between 1 and MAX_NumOfFieldsInPromotableStruct fields
1639 structPromotionInfo.fieldCnt = (unsigned char)fieldCnt;
1640 DWORD typeFlags = compHandle->getClassAttribs(typeHnd);
1642 bool overlappingFields = StructHasOverlappingFields(typeFlags);
1643 if (overlappingFields)
1648 // Don't struct promote if we have an CUSTOMLAYOUT flag on an HFA type
1649 if (StructHasCustomLayout(typeFlags) && compiler->IsHfa(typeHnd))
1655 // On ARM, we have a requirement on the struct alignment; see below.
1656 unsigned structAlignment = roundUp(compHandle->getClassAlignmentRequirement(typeHnd), TARGET_POINTER_SIZE);
1657 #endif // _TARGET_ARM_
1659 unsigned fieldsSize = 0;
1661 for (BYTE ordinal = 0; ordinal < fieldCnt; ++ordinal)
1663 CORINFO_FIELD_HANDLE fieldHnd = compHandle->getFieldInClass(typeHnd, ordinal);
1664 structPromotionInfo.fields[ordinal] = GetFieldInfo(fieldHnd, ordinal);
1665 const lvaStructFieldInfo& fieldInfo = structPromotionInfo.fields[ordinal];
1667 noway_assert(fieldInfo.fldOffset < structSize);
1669 if (fieldInfo.fldSize == 0)
1671 // Not a scalar type.
1675 if ((fieldInfo.fldOffset % fieldInfo.fldSize) != 0)
1677 // The code in Compiler::genPushArgList that reconstitutes
1678 // struct values on the stack from promoted fields expects
1679 // those fields to be at their natural alignment.
1683 if (varTypeIsGC(fieldInfo.fldType))
1685 containsGCpointers = true;
1688 // The end offset for this field should never be larger than our structSize.
1689 noway_assert(fieldInfo.fldOffset + fieldInfo.fldSize <= structSize);
1691 fieldsSize += fieldInfo.fldSize;
1694 // On ARM, for struct types that don't use explicit layout, the alignment of the struct is
1695 // at least the max alignment of its fields. We take advantage of this invariant in struct promotion,
1696 // so verify it here.
1697 if (fieldInfo.fldSize > structAlignment)
1699 // Don't promote vars whose struct types violates the invariant. (Alignment == size for primitives.)
1702 // If we have any small fields we will allocate a single PromotedStructScratch local var for the method.
1703 // This is a stack area that we use to assemble the small fields in order to place them in a register
1706 if (fieldInfo.fldSize < TARGET_POINTER_SIZE)
1708 requiresScratchVar = true;
1710 #endif // _TARGET_ARM_
1713 // If we saw any GC pointer or by-ref fields above then CORINFO_FLG_CONTAINS_GC_PTR or
1714 // CORINFO_FLG_CONTAINS_STACK_PTR has to be set!
1715 noway_assert((containsGCpointers == false) ||
1716 ((typeFlags & (CORINFO_FLG_CONTAINS_GC_PTR | CORINFO_FLG_CONTAINS_STACK_PTR)) != 0));
1718 // If we have "Custom Layout" then we might have an explicit Size attribute
1719 // Managed C++ uses this for its structs, such C++ types will not contain GC pointers.
1721 // The current VM implementation also incorrectly sets the CORINFO_FLG_CUSTOMLAYOUT
1722 // whenever a managed value class contains any GC pointers.
1723 // (See the comment for VMFLAG_NOT_TIGHTLY_PACKED in class.h)
1725 // It is important to struct promote managed value classes that have GC pointers
1726 // So we compute the correct value for "CustomLayout" here
1728 if (StructHasCustomLayout(typeFlags) && ((typeFlags & CORINFO_FLG_CONTAINS_GC_PTR) == 0))
1730 structPromotionInfo.customLayout = true;
1733 // Check if this promoted struct contains any holes.
1734 assert(!overlappingFields);
1735 if (fieldsSize != structSize)
1737 // If sizes do not match it means we have an overlapping fields or holes.
1738 // Overlapping fields were rejected early, so here it can mean only holes.
1739 structPromotionInfo.containsHoles = true;
1742 // Cool, this struct is promotable.
1744 structPromotionInfo.canPromote = true;
1748 //--------------------------------------------------------------------------------------------
1749 // CanPromoteStructVar - checks if the struct can be promoted.
1752 // lclNum - struct number to check.
1755 // true if the struct var can be promoted.
1757 bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum)
1759 LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
1761 assert(varTypeIsStruct(varDsc));
1762 assert(!varDsc->lvPromoted); // Don't ask again :)
1764 // If this lclVar is used in a SIMD intrinsic, then we don't want to struct promote it.
1765 // Note, however, that SIMD lclVars that are NOT used in a SIMD intrinsic may be
1766 // profitably promoted.
1767 if (varDsc->lvIsUsedInSIMDIntrinsic())
1769 JITDUMP(" struct promotion of V%02u is disabled because lvIsUsedInSIMDIntrinsic()\n", lclNum);
1773 // Reject struct promotion of parameters when -GS stack reordering is enabled
1774 // as we could introduce shadow copies of them.
1775 if (varDsc->lvIsParam && compiler->compGSReorderStackLayout)
1777 JITDUMP(" struct promotion of V%02u is disabled because lvIsParam and compGSReorderStackLayout\n", lclNum);
1781 // Explicitly check for HFA reg args and reject them for promotion here.
1782 // Promoting HFA args will fire an assert in lvaAssignFrameOffsets
1783 // when the HFA reg arg is struct promoted.
1785 // TODO-PERF - Allow struct promotion for HFA register arguments
1786 if (varDsc->lvIsHfaRegArg())
1788 JITDUMP(" struct promotion of V%02u is disabled because lvIsHfaRegArg()\n", lclNum);
1792 #if !FEATURE_MULTIREG_STRUCT_PROMOTE
1793 if (varDsc->lvIsMultiRegArg)
1795 JITDUMP(" struct promotion of V%02u is disabled because lvIsMultiRegArg\n", lclNum);
1800 if (varDsc->lvIsMultiRegRet)
1802 JITDUMP(" struct promotion of V%02u is disabled because lvIsMultiRegRet\n", lclNum);
1806 CORINFO_CLASS_HANDLE typeHnd = varDsc->lvVerTypeInfo.GetClassHandle();
1807 return CanPromoteStructType(typeHnd);
1810 //--------------------------------------------------------------------------------------------
1811 // ShouldPromoteStructVar - Should a struct var be promoted if it can be promoted?
1812 // This routine mainly performs profitability checks. Right now it also has
1813 // some correctness checks due to limitations of down-stream phases.
1816 // lclNum - struct local number;
1819 // true if the struct should be promoted.
1821 bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum)
1823 assert(lclNum < compiler->lvaCount);
1825 LclVarDsc* varDsc = &compiler->lvaTable[lclNum];
1826 assert(varTypeIsStruct(varDsc));
1827 assert(varDsc->lvVerTypeInfo.GetClassHandle() == structPromotionInfo.typeHnd);
1828 assert(structPromotionInfo.canPromote);
1830 bool shouldPromote = true;
1832 // We *can* promote; *should* we promote?
1833 // We should only do so if promotion has potential savings. One source of savings
1834 // is if a field of the struct is accessed, since this access will be turned into
1835 // an access of the corresponding promoted field variable. Even if there are no
1836 // field accesses, but only block-level operations on the whole struct, if the struct
1837 // has only one or two fields, then doing those block operations field-wise is probably faster
1838 // than doing a whole-variable block operation (e.g., a hardware "copy loop" on x86).
1839 // Struct promotion also provides the following benefits: reduce stack frame size,
1840 // reduce the need for zero init of stack frame and fine grained constant/copy prop.
1841 // Asm diffs indicate that promoting structs up to 3 fields is a net size win.
1842 // So if no fields are accessed independently, and there are four or more fields,
1843 // then do not promote.
1845 // TODO: Ideally we would want to consider the impact of whether the struct is
1846 // passed as a parameter or assigned the return value of a call. Because once promoted,
1847 // struct copying is done by field by field assignment instead of a more efficient
1848 // rep.stos or xmm reg based copy.
1849 if (structPromotionInfo.fieldCnt > 3 && !varDsc->lvFieldAccessed)
1851 JITDUMP("Not promoting promotable struct local V%02u: #fields = %d, fieldAccessed = %d.\n", lclNum,
1852 structPromotionInfo.fieldCnt, varDsc->lvFieldAccessed);
1853 shouldPromote = false;
1855 #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) || defined(_TARGET_ARM_)
1856 // TODO-PERF - Only do this when the LclVar is used in an argument context
1857 // TODO-ARM64 - HFA support should also eliminate the need for this.
1858 // TODO-ARM32 - HFA support should also eliminate the need for this.
1859 // TODO-LSRA - Currently doesn't support the passing of floating point LCL_VARS in the integer registers
1861 // For now we currently don't promote structs with a single float field
1862 // Promoting it can cause us to shuffle it back and forth between the int and
1863 // the float regs when it is used as a argument, which is very expensive for XARCH
1865 else if ((structPromotionInfo.fieldCnt == 1) && varTypeIsFloating(structPromotionInfo.fields[0].fldType))
1867 JITDUMP("Not promoting promotable struct local V%02u: #fields = %d because it is a struct with "
1868 "single float field.\n",
1869 lclNum, structPromotionInfo.fieldCnt);
1870 shouldPromote = false;
1872 #endif // _TARGET_AMD64_ || _TARGET_ARM64_ || _TARGET_ARM_
1873 else if (varDsc->lvIsParam && !compiler->lvaIsImplicitByRefLocal(lclNum))
1875 #if FEATURE_MULTIREG_STRUCT_PROMOTE
1876 // Is this a variable holding a value with exactly two fields passed in
1877 // multiple registers?
1878 if ((structPromotionInfo.fieldCnt != 2) && compiler->lvaIsMultiregStruct(varDsc, compiler->info.compIsVarArgs))
1880 JITDUMP("Not promoting multireg struct local V%02u, because lvIsParam is true and #fields != 2\n", lclNum);
1881 shouldPromote = false;
1884 #endif // !FEATURE_MULTIREG_STRUCT_PROMOTE
1886 // TODO-PERF - Implement struct promotion for incoming multireg structs
1887 // Currently it hits assert(lvFieldCnt==1) in lclvar.cpp line 4417
1888 // Also the implementation of jmp uses the 4 byte move to store
1889 // byte parameters to the stack, so that if we have a byte field
1890 // with something else occupying the same 4-byte slot, it will
1891 // overwrite other fields.
1892 if (structPromotionInfo.fieldCnt != 1)
1894 JITDUMP("Not promoting promotable struct local V%02u, because lvIsParam is true and #fields = "
1896 lclNum, structPromotionInfo.fieldCnt);
1897 shouldPromote = false;
1902 // If the lvRefCnt is zero and we have a struct promoted parameter we can end up with an extra store of
1903 // the the incoming register into the stack frame slot.
1904 // In that case, we would like to avoid promortion.
1905 // However we haven't yet computed the lvRefCnt values so we can't do that.
1907 CLANG_FORMAT_COMMENT_ANCHOR;
1909 return shouldPromote;
1912 //--------------------------------------------------------------------------------------------
1913 // SortStructFields - sort the fields according to the increasing order of the field offset.
1916 // This is needed because the fields need to be pushed on stack (when referenced as a struct) in offset order.
1918 void Compiler::StructPromotionHelper::SortStructFields()
1920 assert(!structPromotionInfo.fieldsSorted);
1921 qsort(structPromotionInfo.fields, structPromotionInfo.fieldCnt, sizeof(*structPromotionInfo.fields),
1923 structPromotionInfo.fieldsSorted = true;
1926 //--------------------------------------------------------------------------------------------
1927 // GetFieldInfo - get struct field information.
1929 // fieldHnd - field handle to get info for;
1930 // ordinal - field ordinal.
1933 // field information.
1935 Compiler::lvaStructFieldInfo Compiler::StructPromotionHelper::GetFieldInfo(CORINFO_FIELD_HANDLE fieldHnd, BYTE ordinal)
1937 lvaStructFieldInfo fieldInfo;
1938 fieldInfo.fldHnd = fieldHnd;
1940 unsigned fldOffset = compiler->info.compCompHnd->getFieldOffset(fieldInfo.fldHnd);
1941 fieldInfo.fldOffset = (BYTE)fldOffset;
1943 fieldInfo.fldOrdinal = ordinal;
1944 CorInfoType corType = compiler->info.compCompHnd->getFieldType(fieldInfo.fldHnd, &fieldInfo.fldTypeHnd);
1945 fieldInfo.fldType = JITtype2varType(corType);
1946 fieldInfo.fldSize = genTypeSize(fieldInfo.fldType);
1949 // Check to see if this is a SIMD type.
1950 // We will only check this if we have already found a SIMD type, which will be true if
1951 // we have encountered any SIMD intrinsics.
1952 if (compiler->usesSIMDTypes() && (fieldInfo.fldSize == 0) && compiler->isSIMDorHWSIMDClass(fieldInfo.fldTypeHnd))
1955 var_types simdBaseType = compiler->getBaseTypeAndSizeOfSIMDType(fieldInfo.fldTypeHnd, &simdSize);
1956 if (simdBaseType != TYP_UNKNOWN)
1958 fieldInfo.fldType = compiler->getSIMDTypeForSize(simdSize);
1959 fieldInfo.fldSize = simdSize;
1961 retypedFieldsMap.Set(fieldInfo.fldHnd, fieldInfo.fldType);
1965 #endif // FEATURE_SIMD
1967 if (fieldInfo.fldSize == 0)
1969 TryPromoteStructField(fieldInfo);
1975 //--------------------------------------------------------------------------------------------
1976 // TryPromoteStructField - checks that this struct's field is a struct that can be promoted as scalar type
1977 // aligned at its natural boundary. Promotes the field as a scalar if the check succeeded.
1980 // fieldInfo - information about the field in the outer struct.
1983 // true if the internal struct was promoted.
1985 bool Compiler::StructPromotionHelper::TryPromoteStructField(lvaStructFieldInfo& fieldInfo)
1987 // Size of TYP_BLK, TYP_FUNC, TYP_VOID and TYP_STRUCT is zero.
1988 // Early out if field type is other than TYP_STRUCT.
1989 // This is a defensive check as we don't expect a struct to have
1990 // fields of TYP_BLK, TYP_FUNC or TYP_VOID.
1991 if (fieldInfo.fldType != TYP_STRUCT)
1996 COMP_HANDLE compHandle = compiler->info.compCompHnd;
1998 // Do not promote if the struct field in turn has more than one field.
1999 if (compHandle->getClassNumInstanceFields(fieldInfo.fldTypeHnd) != 1)
2004 COMP_HANDLE compHandl = compiler->info.compCompHnd;
2006 // Do not promote if the single field is not aligned at its natural boundary within
2007 // the struct field.
2008 CORINFO_FIELD_HANDLE innerFieldHndl = compHandle->getFieldInClass(fieldInfo.fldTypeHnd, 0);
2009 unsigned innerFieldOffset = compHandle->getFieldOffset(innerFieldHndl);
2010 if (innerFieldOffset != 0)
2015 CorInfoType fieldCorType = compHandle->getFieldType(innerFieldHndl);
2016 var_types fieldVarType = JITtype2varType(fieldCorType);
2017 unsigned fieldSize = genTypeSize(fieldVarType);
2019 // Do not promote if either not a primitive type or size equal to ptr size on
2020 // target or a struct containing a single floating-point field.
2022 // TODO-PERF: Structs containing a single floating-point field on Amd64
2023 // need to be passed in integer registers. Right now LSRA doesn't support
2024 // passing of floating-point LCL_VARS in integer registers. Enabling promotion
2025 // of such structs results in an assert in lsra right now.
2027 // TODO-PERF: Right now promotion is confined to struct containing a ptr sized
2028 // field (int/uint/ref/byref on 32-bits and long/ulong/ref/byref on 64-bits).
2029 // Though this would serve the purpose of promoting Span<T> containing ByReference<T>,
2030 // this can be extended to other primitive types as long as they are aligned at their
2031 // natural boundary.
2033 // TODO-CQ: Right now we only promote an actual SIMD typed field, which would cause
2034 // a nested SIMD type to fail promotion.
2035 if (fieldSize == 0 || fieldSize != TARGET_POINTER_SIZE || varTypeIsFloating(fieldVarType))
2037 JITDUMP("Promotion blocked: struct contains struct field with one field,"
2038 " but that field has invalid size or type");
2042 // Insist this wrapped field occupy all of its parent storage.
2043 unsigned innerStructSize = compHandle->getClassSize(fieldInfo.fldTypeHnd);
2045 if (fieldSize != innerStructSize)
2047 JITDUMP("Promotion blocked: struct contains struct field with one field,"
2048 " but that field is not the same size as its parent.");
2052 // Retype the field as the type of the single field of the struct.
2053 // This is a hack that allows us to promote such fields before we support recursive struct promotion
2054 // (tracked by #10019).
2055 fieldInfo.fldType = fieldVarType;
2056 fieldInfo.fldSize = fieldSize;
2058 retypedFieldsMap.Set(fieldInfo.fldHnd, fieldInfo.fldType);
2063 //--------------------------------------------------------------------------------------------
2064 // PromoteStructVar - promote struct variable.
2067 // lclNum - struct local number;
2069 void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum)
2071 LclVarDsc* varDsc = &compiler->lvaTable[lclNum];
2073 // We should never see a reg-sized non-field-addressed struct here.
2074 assert(!varDsc->lvRegStruct);
2076 assert(varDsc->lvVerTypeInfo.GetClassHandle() == structPromotionInfo.typeHnd);
2077 assert(structPromotionInfo.canPromote);
2079 varDsc->lvFieldCnt = structPromotionInfo.fieldCnt;
2080 varDsc->lvFieldLclStart = compiler->lvaCount;
2081 varDsc->lvPromoted = true;
2082 varDsc->lvContainsHoles = structPromotionInfo.containsHoles;
2083 varDsc->lvCustomLayout = structPromotionInfo.customLayout;
2086 // Don't change the source to a TYP_BLK either.
2087 varDsc->lvKeepType = 1;
2091 if (compiler->verbose)
2093 printf("\nPromoting struct local V%02u (%s):", lclNum,
2094 compiler->eeGetClassName(varDsc->lvVerTypeInfo.GetClassHandle()));
2098 if (!structPromotionInfo.fieldsSorted)
2103 for (unsigned index = 0; index < structPromotionInfo.fieldCnt; ++index)
2105 const lvaStructFieldInfo* pFieldInfo = &structPromotionInfo.fields[index];
2107 if (varTypeIsFloating(pFieldInfo->fldType) || varTypeIsSIMD(pFieldInfo->fldType))
2109 // Whenever we promote a struct that contains a floating point field
2110 // it's possible we transition from a method that originally only had integer
2111 // local vars to start having FP. We have to communicate this through this flag
2112 // since LSRA later on will use this flag to determine whether or not to track FP register sets.
2113 compiler->compFloatingPointUsed = true;
2116 // Now grab the temp for the field local.
2120 sprintf_s(buf, sizeof(buf), "%s V%02u.%s (fldOffset=0x%x)", "field", lclNum,
2121 compiler->eeGetFieldName(pFieldInfo->fldHnd), pFieldInfo->fldOffset);
2123 // We need to copy 'buf' as lvaGrabTemp() below caches a copy to its argument.
2124 size_t len = strlen(buf) + 1;
2125 char* bufp = compiler->getAllocator(CMK_DebugOnly).allocate<char>(len);
2126 strcpy_s(bufp, len, buf);
2130 noway_assert(pFieldInfo->fldOffset > (pFieldInfo - 1)->fldOffset);
2134 // Lifetime of field locals might span multiple BBs, so they must be long lifetime temps.
2135 unsigned varNum = compiler->lvaGrabTemp(false DEBUGARG(bufp));
2137 varDsc = &compiler->lvaTable[lclNum]; // lvaGrabTemp can reallocate the lvaTable
2139 LclVarDsc* fieldVarDsc = &compiler->lvaTable[varNum];
2140 fieldVarDsc->lvType = pFieldInfo->fldType;
2141 fieldVarDsc->lvExactSize = pFieldInfo->fldSize;
2142 fieldVarDsc->lvIsStructField = true;
2143 fieldVarDsc->lvFieldHnd = pFieldInfo->fldHnd;
2144 fieldVarDsc->lvFldOffset = pFieldInfo->fldOffset;
2145 fieldVarDsc->lvFldOrdinal = pFieldInfo->fldOrdinal;
2146 fieldVarDsc->lvParentLcl = lclNum;
2147 fieldVarDsc->lvIsParam = varDsc->lvIsParam;
2148 #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
2149 // Do we have a parameter that can be enregistered?
2151 if (varDsc->lvIsRegArg)
2153 fieldVarDsc->lvIsRegArg = true;
2154 fieldVarDsc->lvArgReg = varDsc->lvArgReg;
2155 #if FEATURE_MULTIREG_ARGS && defined(FEATURE_SIMD)
2156 if (varTypeIsSIMD(fieldVarDsc) && !compiler->lvaIsImplicitByRefLocal(lclNum))
2158 // This field is a SIMD type, and will be considered to be passed in multiple registers
2159 // if the parent struct was. Note that this code relies on the fact that if there is
2160 // a SIMD field of an enregisterable struct, it is the only field.
2161 // We will assert that, in case future changes are made to the ABI.
2162 assert(varDsc->lvFieldCnt == 1);
2163 fieldVarDsc->lvOtherArgReg = varDsc->lvOtherArgReg;
2165 #endif // FEATURE_MULTIREG_ARGS && defined(FEATURE_SIMD)
2170 if (varTypeIsSIMD(pFieldInfo->fldType))
2172 // Set size to zero so that lvaSetStruct will appropriately set the SIMD-relevant fields.
2173 fieldVarDsc->lvExactSize = 0;
2174 compiler->lvaSetStruct(varNum, pFieldInfo->fldTypeHnd, false, true);
2176 #endif // FEATURE_SIMD
2179 // This temporary should not be converted to a double in stress mode,
2180 // because we introduce assigns to it after the stress conversion
2181 fieldVarDsc->lvKeepType = 1;
2186 #if !defined(_TARGET_64BIT_)
2187 //------------------------------------------------------------------------
2188 // lvaPromoteLongVars: "Struct promote" all register candidate longs as if they are structs of two ints.
2196 void Compiler::lvaPromoteLongVars()
2198 if ((opts.compFlags & CLFLG_REGVAR) == 0)
2203 // The lvaTable might grow as we grab temps. Make a local copy here.
2204 unsigned startLvaCount = lvaCount;
2205 for (unsigned lclNum = 0; lclNum < startLvaCount; lclNum++)
2207 LclVarDsc* varDsc = &lvaTable[lclNum];
2208 if (!varTypeIsLong(varDsc) || varDsc->lvDoNotEnregister || varDsc->lvIsMultiRegArgOrRet() ||
2209 (varDsc->lvRefCnt() == 0) || varDsc->lvIsStructField || (fgNoStructPromotion && varDsc->lvIsParam))
2214 varDsc->lvFieldCnt = 2;
2215 varDsc->lvFieldLclStart = lvaCount;
2216 varDsc->lvPromoted = true;
2217 varDsc->lvContainsHoles = false;
2222 printf("\nPromoting long local V%02u:", lclNum);
2226 bool isParam = varDsc->lvIsParam;
2228 for (unsigned index = 0; index < 2; ++index)
2230 // Grab the temp for the field local.
2231 CLANG_FORMAT_COMMENT_ANCHOR;
2235 sprintf_s(buf, sizeof(buf), "%s V%02u.%s (fldOffset=0x%x)", "field", lclNum, index == 0 ? "lo" : "hi",
2238 // We need to copy 'buf' as lvaGrabTemp() below caches a copy to its argument.
2239 size_t len = strlen(buf) + 1;
2240 char* bufp = getAllocator(CMK_DebugOnly).allocate<char>(len);
2241 strcpy_s(bufp, len, buf);
2244 unsigned varNum = lvaGrabTemp(false DEBUGARG(bufp)); // Lifetime of field locals might span multiple BBs, so
2245 // they are long lifetime temps.
2247 LclVarDsc* fieldVarDsc = &lvaTable[varNum];
2248 fieldVarDsc->lvType = TYP_INT;
2249 fieldVarDsc->lvExactSize = genTypeSize(TYP_INT);
2250 fieldVarDsc->lvIsStructField = true;
2251 fieldVarDsc->lvFldOffset = (unsigned char)(index * genTypeSize(TYP_INT));
2252 fieldVarDsc->lvFldOrdinal = (unsigned char)index;
2253 fieldVarDsc->lvParentLcl = lclNum;
2254 // Currently we do not support enregistering incoming promoted aggregates with more than one field.
2257 fieldVarDsc->lvIsParam = true;
2258 lvaSetVarDoNotEnregister(varNum DEBUGARG(DNER_LongParamField));
2266 printf("\nlvaTable after lvaPromoteLongVars\n");
2271 #endif // !defined(_TARGET_64BIT_)
2273 //--------------------------------------------------------------------------------------------
2274 // lvaGetFieldLocal - returns the local var index for a promoted field in a promoted struct var.
2277 // varDsc - the promoted struct var descriptor;
2278 // fldOffset - field offset in the struct.
2281 // the index of the local that represents this field.
2283 unsigned Compiler::lvaGetFieldLocal(const LclVarDsc* varDsc, unsigned int fldOffset)
2285 noway_assert(varTypeIsStruct(varDsc));
2286 noway_assert(varDsc->lvPromoted);
2288 for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
2290 noway_assert(lvaTable[i].lvIsStructField);
2291 noway_assert(lvaTable[i].lvParentLcl == (unsigned)(varDsc - lvaTable));
2292 if (lvaTable[i].lvFldOffset == fldOffset)
2298 // This is the not-found error return path, the caller should check for BAD_VAR_NUM
2302 /*****************************************************************************
2304 * Set the local var "varNum" as address-exposed.
2305 * If this is a promoted struct, label it's fields the same way.
2308 void Compiler::lvaSetVarAddrExposed(unsigned varNum)
2310 noway_assert(varNum < lvaCount);
2312 LclVarDsc* varDsc = &lvaTable[varNum];
2314 varDsc->lvAddrExposed = 1;
2316 if (varDsc->lvPromoted)
2318 noway_assert(varTypeIsStruct(varDsc));
2320 for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
2322 noway_assert(lvaTable[i].lvIsStructField);
2323 lvaTable[i].lvAddrExposed = 1; // Make field local as address-exposed.
2324 lvaSetVarDoNotEnregister(i DEBUGARG(DNER_AddrExposed));
2328 lvaSetVarDoNotEnregister(varNum DEBUGARG(DNER_AddrExposed));
2331 /*****************************************************************************
2333 * Record that the local var "varNum" should not be enregistered (for one of several reasons.)
2336 void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregisterReason reason))
2338 noway_assert(varNum < lvaCount);
2339 LclVarDsc* varDsc = &lvaTable[varNum];
2340 varDsc->lvDoNotEnregister = 1;
2345 printf("\nLocal V%02u should not be enregistered because: ", varNum);
2349 case DNER_AddrExposed:
2350 JITDUMP("it is address exposed\n");
2351 assert(varDsc->lvAddrExposed);
2354 JITDUMP("it is a struct\n");
2355 assert(varTypeIsStruct(varDsc));
2357 case DNER_IsStructArg:
2358 JITDUMP("it is a struct arg\n");
2359 assert(varTypeIsStruct(varDsc));
2362 JITDUMP("written in a block op\n");
2363 varDsc->lvLclBlockOpAddr = 1;
2365 case DNER_LocalField:
2366 JITDUMP("was accessed as a local field\n");
2367 varDsc->lvLclFieldExpr = 1;
2369 case DNER_VMNeedsStackAddr:
2370 JITDUMP("needs stack addr\n");
2371 varDsc->lvVMNeedsStackAddr = 1;
2373 case DNER_LiveInOutOfHandler:
2374 JITDUMP("live in/out of a handler\n");
2375 varDsc->lvLiveInOutOfHndlr = 1;
2377 case DNER_LiveAcrossUnmanagedCall:
2378 JITDUMP("live across unmanaged call\n");
2379 varDsc->lvLiveAcrossUCall = 1;
2382 JITDUMP("field of a dependently promoted struct\n");
2383 assert(varDsc->lvIsStructField && (lvaGetParentPromotionType(varNum) != PROMOTION_TYPE_INDEPENDENT));
2385 case DNER_NoRegVars:
2386 JITDUMP("opts.compFlags & CLFLG_REGVAR is not set\n");
2387 assert((opts.compFlags & CLFLG_REGVAR) == 0);
2389 case DNER_MinOptsGC:
2390 JITDUMP("It is a GC Ref and we are compiling MinOpts\n");
2391 assert(!JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet()));
2393 #ifdef JIT32_GCENCODER
2394 case DNER_PinningRef:
2395 JITDUMP("pinning ref\n");
2396 assert(varDsc->lvPinned);
2399 #if !defined(_TARGET_64BIT_)
2400 case DNER_LongParamField:
2401 JITDUMP("it is a decomposed field of a long parameter\n");
2411 // Returns true if this local var is a multireg struct.
2412 // TODO-Throughput: This does a lookup on the class handle, and in the outgoing arg context
2413 // this information is already available on the fgArgTabEntry, and shouldn't need to be
2416 bool Compiler::lvaIsMultiregStruct(LclVarDsc* varDsc, bool isVarArg)
2418 if (varTypeIsStruct(varDsc->TypeGet()))
2420 CORINFO_CLASS_HANDLE clsHnd = varDsc->lvVerTypeInfo.GetClassHandleForValueClass();
2421 structPassingKind howToPassStruct;
2423 var_types type = getArgTypeForStruct(clsHnd, &howToPassStruct, isVarArg, varDsc->lvExactSize);
2425 if (howToPassStruct == SPK_ByValueAsHfa)
2427 assert(type == TYP_STRUCT);
2431 #if defined(UNIX_AMD64_ABI) || defined(_TARGET_ARM64_)
2432 if (howToPassStruct == SPK_ByValue)
2434 assert(type == TYP_STRUCT);
2442 /*****************************************************************************
2443 * Set the lvClass for a local variable of a struct type */
2445 void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool unsafeValueClsCheck, bool setTypeInfo)
2447 noway_assert(varNum < lvaCount);
2449 LclVarDsc* varDsc = &lvaTable[varNum];
2452 varDsc->lvVerTypeInfo = typeInfo(TI_STRUCT, typeHnd);
2455 // Set the type and associated info if we haven't already set it.
2456 var_types structType = varDsc->lvType;
2457 if (varDsc->lvType == TYP_UNDEF)
2459 varDsc->lvType = TYP_STRUCT;
2461 if (varDsc->lvExactSize == 0)
2463 varDsc->lvExactSize = info.compCompHnd->getClassSize(typeHnd);
2465 size_t lvSize = varDsc->lvSize();
2466 assert((lvSize % TARGET_POINTER_SIZE) ==
2467 0); // The struct needs to be a multiple of TARGET_POINTER_SIZE bytes for getClassGClayout() to be valid.
2468 varDsc->lvGcLayout = getAllocator(CMK_LvaTable).allocate<BYTE>(lvSize / TARGET_POINTER_SIZE);
2470 var_types simdBaseType = TYP_UNKNOWN;
2471 varDsc->lvType = impNormStructType(typeHnd, varDsc->lvGcLayout, &numGCVars, &simdBaseType);
2473 // We only save the count of GC vars in a struct up to 7.
2478 varDsc->lvStructGcCount = numGCVars;
2480 if (simdBaseType != TYP_UNKNOWN)
2482 assert(varTypeIsSIMD(varDsc));
2483 varDsc->lvSIMDType = true;
2484 varDsc->lvBaseType = simdBaseType;
2486 #endif // FEATURE_SIMD
2488 // for structs that are small enough, we check and set lvIsHfa and lvHfaTypeIsFloat
2489 if (varDsc->lvExactSize <= MAX_PASS_MULTIREG_BYTES)
2491 var_types hfaType = GetHfaType(typeHnd); // set to float or double if it is an HFA, otherwise TYP_UNDEF
2492 if (varTypeIsFloating(hfaType))
2494 varDsc->_lvIsHfa = true;
2495 varDsc->lvSetHfaTypeIsFloat(hfaType == TYP_FLOAT);
2497 // hfa variables can never contain GC pointers
2498 assert(varDsc->lvStructGcCount == 0);
2499 // The size of this struct should be evenly divisible by 4 or 8
2500 assert((varDsc->lvExactSize % genTypeSize(hfaType)) == 0);
2501 // The number of elements in the HFA should fit into our MAX_ARG_REG_COUNT limit
2502 assert((varDsc->lvExactSize / genTypeSize(hfaType)) <= MAX_ARG_REG_COUNT);
2505 #endif // FEATURE_HFA
2510 assert(!varTypeIsSIMD(varDsc) || (varDsc->lvBaseType != TYP_UNKNOWN));
2511 #endif // FEATURE_SIMD
2514 #ifndef _TARGET_64BIT_
2515 BOOL fDoubleAlignHint = FALSE;
2517 fDoubleAlignHint = TRUE;
2520 if (info.compCompHnd->getClassAlignmentRequirement(typeHnd, fDoubleAlignHint) == 8)
2525 printf("Marking struct in V%02i with double align flag\n", varNum);
2528 varDsc->lvStructDoubleAlign = 1;
2530 #endif // not _TARGET_64BIT_
2532 unsigned classAttribs = info.compCompHnd->getClassAttribs(typeHnd);
2534 varDsc->lvOverlappingFields = StructHasOverlappingFields(classAttribs);
2536 // Check whether this local is an unsafe value type and requires GS cookie protection.
2537 // GS checks require the stack to be re-ordered, which can't be done with EnC.
2538 if (unsafeValueClsCheck && (classAttribs & CORINFO_FLG_UNSAFE_VALUECLASS) && !opts.compDbgEnC)
2540 setNeedsGSSecurityCookie();
2541 compGSReorderStackLayout = true;
2542 varDsc->lvIsUnsafeBuffer = true;
2546 //------------------------------------------------------------------------
2547 // lvaSetStructUsedAsVarArg: update hfa information for vararg struct args
2550 // varNum -- number of the variable
2553 // This only affects arm64 varargs on windows where we need to pass
2554 // hfa arguments as if they are not HFAs.
2556 // This function should only be called if the struct is used in a varargs
2559 void Compiler::lvaSetStructUsedAsVarArg(unsigned varNum)
2562 #if defined(_TARGET_WINDOWS_) && defined(_TARGET_ARM64_)
2563 LclVarDsc* varDsc = &lvaTable[varNum];
2564 // For varargs methods incoming and outgoing arguments should not be treated
2566 varDsc->_lvIsHfa = false;
2567 varDsc->_lvHfaTypeIsFloat = false;
2568 #endif // defined(_TARGET_WINDOWS_) && defined(_TARGET_ARM64_)
2569 #endif // FEATURE_HFA
2572 //------------------------------------------------------------------------
2573 // lvaSetClass: set class information for a local var.
2576 // varNum -- number of the variable
2577 // clsHnd -- class handle to use in set or update
2578 // isExact -- true if class is known exactly
2581 // varNum must not already have a ref class handle.
2583 void Compiler::lvaSetClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool isExact)
2585 noway_assert(varNum < lvaCount);
2587 // If we are just importing, we cannot reliably track local ref types,
2588 // since the jit maps CORINFO_TYPE_VAR to TYP_REF.
2589 if (compIsForImportOnly())
2594 // Else we should have a type handle.
2595 assert(clsHnd != nullptr);
2597 LclVarDsc* varDsc = &lvaTable[varNum];
2598 assert(varDsc->lvType == TYP_REF);
2600 // We shoud not have any ref type information for this var.
2601 assert(varDsc->lvClassHnd == nullptr);
2602 assert(!varDsc->lvClassIsExact);
2604 JITDUMP("\nlvaSetClass: setting class for V%02i to (%p) %s %s\n", varNum, dspPtr(clsHnd),
2605 info.compCompHnd->getClassName(clsHnd), isExact ? " [exact]" : "");
2607 varDsc->lvClassHnd = clsHnd;
2608 varDsc->lvClassIsExact = isExact;
2611 //------------------------------------------------------------------------
2612 // lvaSetClass: set class information for a local var from a tree or stack type
2615 // varNum -- number of the variable. Must be a single def local
2616 // tree -- tree establishing the variable's value
2617 // stackHnd -- handle for the type from the evaluation stack
2620 // Preferentially uses the tree's type, when available. Since not all
2621 // tree kinds can track ref types, the stack type is used as a
2624 void Compiler::lvaSetClass(unsigned varNum, GenTree* tree, CORINFO_CLASS_HANDLE stackHnd)
2626 bool isExact = false;
2627 bool isNonNull = false;
2628 CORINFO_CLASS_HANDLE clsHnd = gtGetClassHandle(tree, &isExact, &isNonNull);
2630 if (clsHnd != nullptr)
2632 lvaSetClass(varNum, clsHnd, isExact);
2634 else if (stackHnd != nullptr)
2636 lvaSetClass(varNum, stackHnd);
2640 //------------------------------------------------------------------------
2641 // lvaUpdateClass: update class information for a local var.
2644 // varNum -- number of the variable
2645 // clsHnd -- class handle to use in set or update
2646 // isExact -- true if class is known exactly
2650 // This method models the type update rule for an assignment.
2652 // Updates currently should only happen for single-def user args or
2653 // locals, when we are processing the expression actually being
2654 // used to initialize the local (or inlined arg). The update will
2655 // change the local from the declared type to the type of the
2658 // These updates should always *improve* what we know about the
2659 // type, that is making an inexact type exact, or changing a type
2660 // to some subtype. However the jit lacks precise type information
2661 // for shared code, so ensuring this is so is currently not
2664 void Compiler::lvaUpdateClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool isExact)
2666 assert(varNum < lvaCount);
2668 // If we are just importing, we cannot reliably track local ref types,
2669 // since the jit maps CORINFO_TYPE_VAR to TYP_REF.
2670 if (compIsForImportOnly())
2675 // Else we should have a class handle to consider
2676 assert(clsHnd != nullptr);
2678 LclVarDsc* varDsc = &lvaTable[varNum];
2679 assert(varDsc->lvType == TYP_REF);
2681 // We should already have a class
2682 assert(varDsc->lvClassHnd != nullptr);
2686 // In general we only expect one update per local var. However if
2687 // a block is re-imported and that block has the only STLOC for
2688 // the var, we may see multiple updates. All subsequent updates
2689 // should agree on the type, since reimportation is triggered by
2690 // type mismatches for things other than ref types.
2691 if (varDsc->lvClassInfoUpdated)
2693 assert(varDsc->lvClassHnd == clsHnd);
2694 assert(varDsc->lvClassIsExact == isExact);
2697 // This counts as an update, even if nothing changes.
2698 varDsc->lvClassInfoUpdated = true;
2700 #endif // defined(DEBUG)
2702 // If previous type was exact, there is nothing to update. Would
2703 // like to verify new type is compatible but can't do this yet.
2704 if (varDsc->lvClassIsExact)
2709 // Are we updating the type?
2710 if (varDsc->lvClassHnd != clsHnd)
2712 JITDUMP("\nlvaUpdateClass: Updating class for V%02i from (%p) %s to (%p) %s %s\n", varNum,
2713 dspPtr(varDsc->lvClassHnd), info.compCompHnd->getClassName(varDsc->lvClassHnd), dspPtr(clsHnd),
2714 info.compCompHnd->getClassName(clsHnd), isExact ? " [exact]" : "");
2716 varDsc->lvClassHnd = clsHnd;
2717 varDsc->lvClassIsExact = isExact;
2721 // Class info matched. Are we updating exactness?
2724 JITDUMP("\nlvaUpdateClass: Updating class for V%02i (%p) %s to be exact\n", varNum, dspPtr(varDsc->lvClassHnd),
2725 info.compCompHnd->getClassName(varDsc->lvClassHnd));
2727 varDsc->lvClassIsExact = isExact;
2731 // Else we have the same handle and (in)exactness as before. Do nothing.
2735 //------------------------------------------------------------------------
2736 // lvaUpdateClass: Uupdate class information for a local var from a tree
2740 // varNum -- number of the variable. Must be a single def local
2741 // tree -- tree establishing the variable's value
2742 // stackHnd -- handle for the type from the evaluation stack
2745 // Preferentially uses the tree's type, when available. Since not all
2746 // tree kinds can track ref types, the stack type is used as a
2749 void Compiler::lvaUpdateClass(unsigned varNum, GenTree* tree, CORINFO_CLASS_HANDLE stackHnd)
2751 bool isExact = false;
2752 bool isNonNull = false;
2753 CORINFO_CLASS_HANDLE clsHnd = gtGetClassHandle(tree, &isExact, &isNonNull);
2755 if (clsHnd != nullptr)
2757 lvaUpdateClass(varNum, clsHnd, isExact);
2759 else if (stackHnd != nullptr)
2761 lvaUpdateClass(varNum, stackHnd);
2765 /*****************************************************************************
2766 * Returns the array of BYTEs containing the GC layout information
2769 BYTE* Compiler::lvaGetGcLayout(unsigned varNum)
2771 assert(varTypeIsStruct(lvaTable[varNum].lvType) && (lvaTable[varNum].lvExactSize >= TARGET_POINTER_SIZE));
2773 return lvaTable[varNum].lvGcLayout;
2776 //------------------------------------------------------------------------
2777 // lvaLclSize: returns size of a local variable, in bytes
2780 // varNum -- variable to query
2783 // Number of bytes needed on the frame for such a local.
2785 unsigned Compiler::lvaLclSize(unsigned varNum)
2787 assert(varNum < lvaCount);
2789 var_types varType = lvaTable[varNum].TypeGet();
2795 return lvaTable[varNum].lvSize();
2798 #if FEATURE_FIXED_OUT_ARGS
2799 // Note that this operation performs a read of a PhasedVar
2800 noway_assert(varNum == lvaOutgoingArgSpaceVar);
2801 return lvaOutgoingArgSpaceSize;
2802 #else // FEATURE_FIXED_OUT_ARGS
2803 assert(!"Unknown size");
2804 NO_WAY("Target doesn't support TYP_LCLBLK");
2806 // Keep prefast happy
2809 #endif // FEATURE_FIXED_OUT_ARGS
2811 default: // This must be a primitive var. Fall out of switch statement
2814 #ifdef _TARGET_64BIT_
2815 // We only need this Quirk for _TARGET_64BIT_
2816 if (lvaTable[varNum].lvQuirkToLong)
2818 noway_assert(lvaTable[varNum].lvAddrExposed);
2819 return genTypeStSz(TYP_LONG) * sizeof(int); // return 8 (2 * 4)
2822 return genTypeStSz(varType) * sizeof(int);
2826 // Return the exact width of local variable "varNum" -- the number of bytes
2827 // you'd need to copy in order to overwrite the value.
2829 unsigned Compiler::lvaLclExactSize(unsigned varNum)
2831 assert(varNum < lvaCount);
2833 var_types varType = lvaTable[varNum].TypeGet();
2839 return lvaTable[varNum].lvExactSize;
2842 #if FEATURE_FIXED_OUT_ARGS
2843 // Note that this operation performs a read of a PhasedVar
2844 noway_assert(lvaOutgoingArgSpaceSize >= 0);
2845 noway_assert(varNum == lvaOutgoingArgSpaceVar);
2846 return lvaOutgoingArgSpaceSize;
2848 #else // FEATURE_FIXED_OUT_ARGS
2849 assert(!"Unknown size");
2850 NO_WAY("Target doesn't support TYP_LCLBLK");
2852 // Keep prefast happy
2855 #endif // FEATURE_FIXED_OUT_ARGS
2857 default: // This must be a primitive var. Fall out of switch statement
2861 return genTypeSize(varType);
2864 // getCalledCount -- get the value used to normalized weights for this method
2865 // if we don't have profile data then getCalledCount will return BB_UNITY_WEIGHT (100)
2866 // otherwise it returns the number of times that profile data says the method was called.
2868 BasicBlock::weight_t BasicBlock::getCalledCount(Compiler* comp)
2870 // when we don't have profile data then fgCalledCount will be BB_UNITY_WEIGHT (100)
2871 BasicBlock::weight_t calledCount = comp->fgCalledCount;
2873 // If we haven't yet reach the place where we setup fgCalledCount it could still be zero
2874 // so return a reasonable value to use until we set it.
2876 if (calledCount == 0)
2878 if (comp->fgIsUsingProfileWeights())
2880 // When we use profile data block counts we have exact counts,
2881 // not multiples of BB_UNITY_WEIGHT (100)
2886 calledCount = comp->fgFirstBB->bbWeight;
2888 if (calledCount == 0)
2890 calledCount = BB_UNITY_WEIGHT;
2897 // getBBWeight -- get the normalized weight of this block
2898 BasicBlock::weight_t BasicBlock::getBBWeight(Compiler* comp)
2900 if (this->bbWeight == 0)
2906 weight_t calledCount = getCalledCount(comp);
2908 // Normalize the bbWeights by multiplying by BB_UNITY_WEIGHT and dividing by the calledCount.
2910 // 1. For methods that do not have IBC data the called weight will always be 100 (BB_UNITY_WEIGHT)
2911 // and the entry point bbWeight value is almost always 100 (BB_UNITY_WEIGHT)
2912 // 2. For methods that do have IBC data the called weight is the actual number of calls
2913 // from the IBC data and the entry point bbWeight value is almost always the actual
2914 // number of calls from the IBC data.
2916 // "almost always" - except for the rare case where a loop backedge jumps to BB01
2918 // We also perform a rounding operation by adding half of the 'calledCount' before performing
2921 // Thus for both cases we will return 100 (BB_UNITY_WEIGHT) for the entry point BasicBlock
2923 // Note that with a 100 (BB_UNITY_WEIGHT) values between 1 and 99 represent decimal fractions.
2924 // (i.e. 33 represents 33% and 75 represents 75%, and values greater than 100 require
2925 // some kind of loop backedge)
2928 if (this->bbWeight < (BB_MAX_WEIGHT / BB_UNITY_WEIGHT))
2930 // Calculate the result using unsigned arithmetic
2931 weight_t result = ((this->bbWeight * BB_UNITY_WEIGHT) + (calledCount / 2)) / calledCount;
2933 // We don't allow a value of zero, as that would imply rarely run
2934 return max(1, result);
2938 // Calculate the full result using floating point
2939 double fullResult = ((double)this->bbWeight * (double)BB_UNITY_WEIGHT) / (double)calledCount;
2941 if (fullResult < (double)BB_MAX_WEIGHT)
2943 // Add 0.5 and truncate to unsigned
2944 return (weight_t)(fullResult + 0.5);
2948 return BB_MAX_WEIGHT;
2954 /*****************************************************************************
2956 * Compare function passed to qsort() by Compiler::lclVars.lvaSortByRefCount().
2957 * when generating SMALL_CODE.
2958 * Return positive if dsc2 has a higher ref count
2959 * Return negative if dsc1 has a higher ref count
2960 * Return zero if the ref counts are the same
2964 int __cdecl Compiler::RefCntCmp(const void* op1, const void* op2)
2966 LclVarDsc* dsc1 = *(LclVarDsc**)op1;
2967 LclVarDsc* dsc2 = *(LclVarDsc**)op2;
2969 /* Make sure we preference tracked variables over untracked variables */
2971 if (dsc1->lvTracked != dsc2->lvTracked)
2973 return (dsc2->lvTracked) ? +1 : -1;
2976 unsigned weight1 = dsc1->lvRefCnt();
2977 unsigned weight2 = dsc2->lvRefCnt();
2979 #ifndef _TARGET_ARM_
2980 // ARM-TODO: this was disabled for ARM under !FEATURE_FP_REGALLOC; it was probably a left-over from
2981 // legacy backend. It should be enabled and verified.
2983 /* Force integer candidates to sort above float candidates */
2985 bool isFloat1 = isFloatRegType(dsc1->lvType);
2986 bool isFloat2 = isFloatRegType(dsc2->lvType);
2988 if (isFloat1 != isFloat2)
2990 if (weight2 && isFloat1)
2994 if (weight1 && isFloat2)
3001 int diff = weight2 - weight1;
3008 /* The unweighted ref counts were the same */
3009 /* If the weighted ref counts are different then use their difference */
3010 diff = dsc2->lvRefCntWtd() - dsc1->lvRefCntWtd();
3017 /* We have equal ref counts and weighted ref counts */
3019 /* Break the tie by: */
3020 /* Increasing the weight by 2 if we are a register arg */
3021 /* Increasing the weight by 0.5 if we are a GC type */
3022 /* Increasing the weight by 0.5 if we were enregistered in the previous pass */
3026 if (dsc1->lvIsRegArg)
3028 weight2 += 2 * BB_UNITY_WEIGHT;
3031 if (varTypeIsGC(dsc1->TypeGet()))
3033 weight1 += BB_UNITY_WEIGHT / 2;
3036 if (dsc1->lvRegister)
3038 weight1 += BB_UNITY_WEIGHT / 2;
3044 if (dsc2->lvIsRegArg)
3046 weight2 += 2 * BB_UNITY_WEIGHT;
3049 if (varTypeIsGC(dsc2->TypeGet()))
3051 weight2 += BB_UNITY_WEIGHT / 2;
3054 if (dsc2->lvRegister)
3056 weight2 += BB_UNITY_WEIGHT / 2;
3060 diff = weight2 - weight1;
3067 /* To achieve a Stable Sort we use the LclNum (by way of the pointer address) */
3081 /*****************************************************************************
3083 * Compare function passed to qsort() by Compiler::lclVars.lvaSortByRefCount().
3084 * when not generating SMALL_CODE.
3085 * Return positive if dsc2 has a higher weighted ref count
3086 * Return negative if dsc1 has a higher weighted ref count
3087 * Return zero if the ref counts are the same
3091 int __cdecl Compiler::WtdRefCntCmp(const void* op1, const void* op2)
3093 LclVarDsc* dsc1 = *(LclVarDsc**)op1;
3094 LclVarDsc* dsc2 = *(LclVarDsc**)op2;
3096 /* Make sure we preference tracked variables over untracked variables */
3098 if (dsc1->lvTracked != dsc2->lvTracked)
3100 return (dsc2->lvTracked) ? +1 : -1;
3103 unsigned weight1 = dsc1->lvRefCntWtd();
3104 unsigned weight2 = dsc2->lvRefCntWtd();
3106 #ifndef _TARGET_ARM_
3107 // ARM-TODO: this was disabled for ARM under !FEATURE_FP_REGALLOC; it was probably a left-over from
3108 // legacy backend. It should be enabled and verified.
3110 /* Force integer candidates to sort above float candidates */
3112 bool isFloat1 = isFloatRegType(dsc1->lvType);
3113 bool isFloat2 = isFloatRegType(dsc2->lvType);
3115 if (isFloat1 != isFloat2)
3117 if (weight2 && isFloat1)
3121 if (weight1 && isFloat2)
3128 if (weight1 && dsc1->lvIsRegArg)
3130 weight1 += 2 * BB_UNITY_WEIGHT;
3133 if (weight2 && dsc2->lvIsRegArg)
3135 weight2 += 2 * BB_UNITY_WEIGHT;
3138 if (weight2 > weight1)
3142 else if (weight2 < weight1)
3147 // Otherwise, we have equal weighted ref counts.
3149 /* If the unweighted ref counts are different then use their difference */
3150 int diff = (int)dsc2->lvRefCnt() - (int)dsc1->lvRefCnt();
3157 /* If one is a GC type and the other is not the GC type wins */
3158 if (varTypeIsGC(dsc1->TypeGet()) != varTypeIsGC(dsc2->TypeGet()))
3160 if (varTypeIsGC(dsc1->TypeGet()))
3172 /* If one was enregistered in the previous pass then it wins */
3173 if (dsc1->lvRegister != dsc2->lvRegister)
3175 if (dsc1->lvRegister)
3187 /* We have a tie! */
3189 /* To achieve a Stable Sort we use the LclNum (by way of the pointer address) */
3203 /*****************************************************************************
3205 * Sort the local variable table by refcount and assign tracking indices.
3208 void Compiler::lvaSortOnly()
3210 /* Now sort the variable table by ref-count */
3212 qsort(lvaRefSorted, lvaCount, sizeof(*lvaRefSorted), (compCodeOpt() == SMALL_CODE) ? RefCntCmp : WtdRefCntCmp);
3216 void Compiler::lvaDumpRefCounts()
3220 if (verbose && lvaCount)
3222 printf("refCnt table for '%s':\n", info.compMethodName);
3224 for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++)
3226 unsigned refCnt = lvaRefSorted[lclNum]->lvRefCnt();
3231 unsigned refCntWtd = lvaRefSorted[lclNum]->lvRefCntWtd();
3234 gtDispLclVar((unsigned)(lvaRefSorted[lclNum] - lvaTable));
3235 printf(" [%6s]: refCnt = %4u, refCntWtd = %6s", varTypeName(lvaRefSorted[lclNum]->TypeGet()), refCnt,
3236 refCntWtd2str(refCntWtd));
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 /* For now assume we'll be able to track all locals */
3284 varDsc->lvTracked = 1;
3286 /* If the ref count is zero */
3287 if (varDsc->lvRefCnt() == 0)
3289 /* Zero ref count, make this untracked */
3290 varDsc->lvTracked = 0;
3291 varDsc->setLvRefCntWtd(0);
3294 #if !defined(_TARGET_64BIT_)
3295 if (varTypeIsLong(varDsc) && varDsc->lvPromoted)
3297 varDsc->lvTracked = 0;
3299 #endif // !defined(_TARGET_64BIT_)
3301 // Variables that are address-exposed, and all struct locals, are never enregistered, or tracked.
3302 // (The struct may be promoted, and its field variables enregistered/tracked, or the VM may "normalize"
3303 // its type so that its not seen by the JIT as a struct.)
3304 // Pinned variables may not be tracked (a condition of the GCInfo representation)
3305 // or enregistered, on x86 -- it is believed that we can enregister pinned (more properly, "pinning")
3306 // references when using the general GC encoding.
3307 if (varDsc->lvAddrExposed)
3309 varDsc->lvTracked = 0;
3310 assert(varDsc->lvType != TYP_STRUCT ||
3311 varDsc->lvDoNotEnregister); // For structs, should have set this when we set lvAddrExposed.
3313 else if (varTypeIsStruct(varDsc))
3315 // Promoted structs will never be considered for enregistration anyway,
3316 // and the DoNotEnregister flag was used to indicate whether promotion was
3317 // independent or dependent.
3318 if (varDsc->lvPromoted)
3320 varDsc->lvTracked = 0;
3322 else if ((varDsc->lvType == TYP_STRUCT) && !varDsc->lvRegStruct)
3324 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_IsStruct));
3327 else if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT))
3329 // SSA must exclude struct fields that are not independently promoted
3330 // as dependent fields could be assigned using a CopyBlock
3331 // resulting in a single node causing multiple SSA definitions
3332 // which isn't currently supported by SSA
3334 // TODO-CQ: Consider using lvLclBlockOpAddr and only marking these LclVars
3335 // untracked when a blockOp is used to assign the struct.
3337 varDsc->lvTracked = 0; // so, don't mark as tracked
3338 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_DepField));
3340 else if (varDsc->lvPinned)
3342 varDsc->lvTracked = 0;
3343 #ifdef JIT32_GCENCODER
3344 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_PinningRef));
3347 else if (opts.MinOpts() && !JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet()))
3349 varDsc->lvTracked = 0;
3350 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_MinOptsGC));
3352 else if ((opts.compFlags & CLFLG_REGVAR) == 0)
3354 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_NoRegVars));
3356 #if defined(JIT32_GCENCODER) && defined(WIN64EXCEPTIONS)
3357 else if (lvaIsOriginalThisArg(lclNum) && (info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0)
3359 // For x86/Linux, we need to track "this".
3360 // However we cannot have it in tracked variables, so we set "this" pointer always untracked
3361 varDsc->lvTracked = 0;
3365 // Are we not optimizing and we have exception handlers?
3366 // if so mark all args and locals "do not enregister".
3368 if (opts.MinOpts() && compHndBBtabCount > 0)
3370 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DNER_LiveInOutOfHandler));
3374 var_types type = genActualType(varDsc->TypeGet());
3378 #if CPU_HAS_FP_SUPPORT
3391 #endif // FEATURE_SIMD
3397 noway_assert(!"lvType not set correctly");
3398 varDsc->lvType = TYP_INT;
3403 varDsc->lvTracked = 0;
3407 /* Now sort the variable table by ref-count */
3411 /* Decide which variables will be worth tracking */
3413 if (lvaCount > lclMAX_TRACKED)
3415 /* Mark all variables past the first 'lclMAX_TRACKED' as untracked */
3417 for (lclNum = lclMAX_TRACKED; lclNum < lvaCount; lclNum++)
3419 lvaRefSorted[lclNum]->lvTracked = 0;
3423 if (lvaTrackedToVarNum == nullptr)
3425 lvaTrackedToVarNum = new (getAllocator(CMK_LvaTable)) unsigned[lclMAX_TRACKED];
3429 // Re-Initialize to -1 for safety in debug build.
3430 memset(lvaTrackedToVarNum, -1, lclMAX_TRACKED * sizeof(unsigned));
3433 /* Assign indices to all the variables we've decided to track */
3435 for (lclNum = 0; lclNum < min(lvaCount, lclMAX_TRACKED); lclNum++)
3437 varDsc = lvaRefSorted[lclNum];
3438 if (varDsc->lvTracked)
3440 noway_assert(varDsc->lvRefCnt() > 0);
3442 /* This variable will be tracked - assign it an index */
3444 lvaTrackedToVarNum[lvaTrackedCount] = (unsigned)(varDsc - lvaTable); // The type of varDsc and lvaTable
3445 // is LclVarDsc. Subtraction will give us
3447 varDsc->lvVarIndex = lvaTrackedCount++;
3451 // We have a new epoch, and also cache the tracked var count in terms of size_t's sufficient to hold that many bits.
3453 lvaTrackedCountInSizeTUnits =
3454 roundUp((unsigned)lvaTrackedCount, (unsigned)(sizeof(size_t) * 8)) / unsigned(sizeof(size_t) * 8);
3457 VarSetOps::AssignNoCopy(this, lvaTrackedVars, VarSetOps::MakeFull(this));
3462 /*****************************************************************************
3464 * This is called by lvaMarkLclRefs to disqualify a variable from being
3465 * considered by optAddCopies()
3467 void LclVarDsc::lvaDisqualifyVar()
3469 this->lvDisqualify = true;
3470 this->lvSingleDef = false;
3471 this->lvDefStmt = nullptr;
3473 #endif // ASSERTION_PROP
3475 /**********************************************************************************
3476 * Get stack size of the varDsc.
3478 const size_t LclVarDsc::lvArgStackSize() const
3480 // Make sure this will have a stack size
3481 assert(!this->lvIsRegArg);
3483 size_t stackSize = 0;
3484 if (varTypeIsStruct(this))
3486 #if defined(WINDOWS_AMD64_ABI)
3487 // Structs are either passed by reference or can be passed by value using one pointer
3488 stackSize = TARGET_POINTER_SIZE;
3489 #elif defined(_TARGET_ARM64_) || defined(UNIX_AMD64_ABI)
3490 // lvSize performs a roundup.
3491 stackSize = this->lvSize();
3493 #if defined(_TARGET_ARM64_)
3494 if ((stackSize > TARGET_POINTER_SIZE * 2) && (!this->lvIsHfa()))
3496 // If the size is greater than 16 bytes then it will
3497 // be passed by reference.
3498 stackSize = TARGET_POINTER_SIZE;
3500 #endif // defined(_TARGET_ARM64_)
3502 #else // !_TARGET_ARM64_ !WINDOWS_AMD64_ABI !UNIX_AMD64_ABI
3504 NYI("Unsupported target.");
3507 #endif // !_TARGET_ARM64_ !WINDOWS_AMD64_ABI !UNIX_AMD64_ABI
3511 stackSize = TARGET_POINTER_SIZE;
3517 /**********************************************************************************
3518 * Get type of a variable when passed as an argument.
3520 var_types LclVarDsc::lvaArgType()
3522 var_types type = TypeGet();
3524 #ifdef _TARGET_AMD64_
3525 #ifdef UNIX_AMD64_ABI
3526 if (type == TYP_STRUCT)
3530 #else //! UNIX_AMD64_ABI
3531 if (type == TYP_STRUCT)
3533 switch (lvExactSize)
3545 switch (*lvGcLayout)
3569 #endif // !UNIX_AMD64_ABI
3570 #elif defined(_TARGET_ARM64_)
3571 if (type == TYP_STRUCT)
3575 #elif defined(_TARGET_X86_)
3576 // Nothing to do; use the type as is.
3579 #endif //_TARGET_AMD64_
3584 //------------------------------------------------------------------------
3585 // lvaMarkLclRefs: increment local var references counts and more
3588 // tree - some node in a tree
3589 // block - block that the tree node belongs to
3590 // stmt - stmt that the tree node belongs to
3591 // isRecompute - true if we should just recompute counts
3594 // Invoked via the MarkLocalVarsVisitor
3596 // Primarily increments the regular and weighted local var ref
3597 // counts for any local referred to directly by tree.
3601 // Accounts for implicit references to frame list root for
3602 // pinvokes that will be expanded later.
3604 // Determines if locals of TYP_BOOL can safely be considered
3605 // to hold only 0 or 1 or may have a broader range of true values.
3607 // Does some setup work for assertion prop, noting locals that are
3608 // eligible for assertion prop, single defs, and tracking which blocks
3611 // In checked builds:
3613 // Verifies that local accesses are consistenly typed.
3614 // Verifies that casts remain in bounds.
3616 void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, GenTreeStmt* stmt, bool isRecompute)
3618 const BasicBlock::weight_t weight = block->getBBWeight(this);
3620 /* Is this a call to unmanaged code ? */
3621 if (tree->gtOper == GT_CALL && tree->gtFlags & GTF_CALL_UNMANAGED)
3623 assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
3624 if (!opts.ShouldUsePInvokeHelpers())
3626 /* Get the special variable descriptor */
3628 unsigned lclNum = info.compLvFrameListRoot;
3630 noway_assert(lclNum <= lvaCount);
3631 LclVarDsc* varDsc = lvaTable + lclNum;
3633 /* Increment the ref counts twice */
3634 varDsc->incRefCnts(weight, this);
3635 varDsc->incRefCnts(weight, this);
3641 /* Is this an assigment? */
3643 if (tree->OperIs(GT_ASG))
3645 GenTree* op1 = tree->gtOp.gtOp1;
3646 GenTree* op2 = tree->gtOp.gtOp2;
3650 /* Is this an assignment to a local variable? */
3652 if (op1->gtOper == GT_LCL_VAR && op2->gtType != TYP_BOOL)
3654 /* Only simple assignments allowed for booleans */
3656 if (tree->gtOper != GT_ASG)
3661 /* Is the RHS clearly a boolean value? */
3663 switch (op2->gtOper)
3669 if (op2->gtIntCon.gtIconVal == 0)
3673 if (op2->gtIntCon.gtIconVal == 1)
3678 // Not 0 or 1, fall through ....
3683 if (op2->OperIsCompare())
3690 lclNum = op1->gtLclVarCommon.gtLclNum;
3691 noway_assert(lclNum < lvaCount);
3693 lvaTable[lclNum].lvIsBoolean = false;
3701 if ((tree->gtOper != GT_LCL_VAR) && (tree->gtOper != GT_LCL_FLD))
3706 /* This must be a local variable reference */
3708 assert((tree->gtOper == GT_LCL_VAR) || (tree->gtOper == GT_LCL_FLD));
3709 unsigned lclNum = tree->gtLclVarCommon.gtLclNum;
3711 noway_assert(lclNum < lvaCount);
3712 LclVarDsc* varDsc = lvaTable + lclNum;
3714 /* Increment the reference counts */
3716 varDsc->incRefCnts(weight, this);
3720 if (lvaVarAddrExposed(lclNum))
3722 varDsc->lvIsBoolean = false;
3725 if (tree->gtOper == GT_LCL_FLD)
3728 // variables that have uses inside a GT_LCL_FLD
3729 // cause problems, so we will disqualify them here
3730 varDsc->lvaDisqualifyVar();
3731 #endif // ASSERTION_PROP
3736 if (fgDomsComputed && IsDominatedByExceptionalEntry(block))
3738 SetVolatileHint(varDsc);
3741 /* Record if the variable has a single def or not */
3743 if (!varDsc->lvDisqualify) // If this variable is already disqualified we can skip this
3745 if (tree->gtFlags & GTF_VAR_DEF) // Is this is a def of our variable
3748 If we have one of these cases:
3749 1. We have already seen a definition (i.e lvSingleDef is true)
3750 2. or info.CompInitMem is true (thus this would be the second definition)
3751 3. or we have an assignment inside QMARK-COLON trees
3752 4. or we have an update form of assignment (i.e. +=, -=, *=)
3753 Then we must disqualify this variable for use in optAddCopies()
3755 Note that all parameters start out with lvSingleDef set to true
3757 if ((varDsc->lvSingleDef == true) || (info.compInitMem == true) || (tree->gtFlags & GTF_COLON_COND) ||
3758 (tree->gtFlags & GTF_VAR_USEASG))
3760 varDsc->lvaDisqualifyVar();
3764 varDsc->lvSingleDef = true;
3765 varDsc->lvDefStmt = stmt;
3768 else // otherwise this is a ref of our variable
3770 if (BlockSetOps::MayBeUninit(varDsc->lvRefBlks))
3772 // Lazy initialization
3773 BlockSetOps::AssignNoCopy(this, varDsc->lvRefBlks, BlockSetOps::MakeEmpty(this));
3775 BlockSetOps::AddElemD(this, varDsc->lvRefBlks, block->bbNum);
3778 #endif // ASSERTION_PROP
3780 bool allowStructs = false;
3781 #ifdef UNIX_AMD64_ABI
3782 // On System V the type of the var could be a struct type.
3783 allowStructs = varTypeIsStruct(varDsc);
3784 #endif // UNIX_AMD64_ABI
3786 /* Variables must be used as the same type throughout the method */
3787 noway_assert(tiVerificationNeeded || varDsc->lvType == TYP_UNDEF || tree->gtType == TYP_UNKNOWN ||
3788 allowStructs || genActualType(varDsc->TypeGet()) == genActualType(tree->gtType) ||
3789 (tree->gtType == TYP_BYREF && varDsc->TypeGet() == TYP_I_IMPL) ||
3790 (tree->gtType == TYP_I_IMPL && varDsc->TypeGet() == TYP_BYREF) || (tree->gtFlags & GTF_VAR_CAST) ||
3791 varTypeIsFloating(varDsc->TypeGet()) && varTypeIsFloating(tree->gtType));
3793 /* Remember the type of the reference */
3795 if (tree->gtType == TYP_UNKNOWN || varDsc->lvType == TYP_UNDEF)
3797 varDsc->lvType = tree->gtType;
3798 noway_assert(genActualType(varDsc->TypeGet()) == tree->gtType); // no truncation
3802 if (tree->gtFlags & GTF_VAR_CAST)
3804 // it should never be bigger than the variable slot
3806 // Trees don't store the full information about structs
3807 // so we can't check them.
3808 if (tree->TypeGet() != TYP_STRUCT)
3810 unsigned treeSize = genTypeSize(tree->TypeGet());
3811 unsigned varSize = genTypeSize(varDsc->TypeGet());
3812 if (varDsc->TypeGet() == TYP_STRUCT)
3814 varSize = varDsc->lvSize();
3817 assert(treeSize <= varSize);
3824 //------------------------------------------------------------------------
3825 // IsDominatedByExceptionalEntry: Check is the block dominated by an exception entry block.
3828 // block - the checking block.
3830 bool Compiler::IsDominatedByExceptionalEntry(BasicBlock* block)
3832 assert(fgDomsComputed);
3833 return block->IsDominatedByExceptionalEntryFlag();
3836 //------------------------------------------------------------------------
3837 // SetVolatileHint: Set a local var's volatile hint.
3840 // varDsc - the local variable that needs the hint.
3842 void Compiler::SetVolatileHint(LclVarDsc* varDsc)
3844 varDsc->lvVolatileHint = true;
3847 //------------------------------------------------------------------------
3848 // lvaMarkLocalVars: update local var ref counts for IR in a basic block
3851 // block - the block in question
3852 // isRecompute - true if counts are being recomputed
3855 // Invokes lvaMarkLclRefs on each tree node for each
3856 // statement in the block.
3858 void Compiler::lvaMarkLocalVars(BasicBlock* block, bool isRecompute)
3860 class MarkLocalVarsVisitor final : public GenTreeVisitor<MarkLocalVarsVisitor>
3863 BasicBlock* m_block;
3864 GenTreeStmt* m_stmt;
3873 MarkLocalVarsVisitor(Compiler* compiler, BasicBlock* block, GenTreeStmt* stmt, bool isRecompute)
3874 : GenTreeVisitor<MarkLocalVarsVisitor>(compiler), m_block(block), m_stmt(stmt), m_isRecompute(isRecompute)
3878 Compiler::fgWalkResult PreOrderVisit(GenTree** use, GenTree* user)
3880 m_compiler->lvaMarkLclRefs(*use, m_block, m_stmt, m_isRecompute);
3881 return WALK_CONTINUE;
3885 JITDUMP("\n*** %s local variables in block " FMT_BB " (weight=%s)\n", isRecompute ? "recomputing" : "marking",
3886 block->bbNum, refCntWtd2str(block->getBBWeight(this)));
3888 for (GenTreeStmt* stmt = block->FirstNonPhiDef(); stmt != nullptr; stmt = stmt->getNextStmt())
3890 MarkLocalVarsVisitor visitor(this, block, stmt, isRecompute);
3892 visitor.WalkTree(&stmt->gtStmtExpr, nullptr);
3896 //------------------------------------------------------------------------
3897 // lvaMarkLocalVars: enable normal ref counting, compute initial counts, sort locals table
3900 // Now behaves differently in minopts / debug. Instead of actually inspecting
3901 // the IR and counting references, the jit assumes all locals are referenced
3902 // and does not sort the locals table.
3904 // Also, when optimizing, lays the groundwork for assertion prop and more.
3905 // See details in lvaMarkLclRefs.
3907 void Compiler::lvaMarkLocalVars()
3910 JITDUMP("\n*************** In lvaMarkLocalVars()");
3912 // If we have direct pinvokes, verify the frame list root local was set up properly
3913 if (info.compCallUnmanaged != 0)
3915 assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
3916 if (!opts.ShouldUsePInvokeHelpers())
3918 noway_assert(info.compLvFrameListRoot >= info.compLocalsCount && info.compLvFrameListRoot < lvaCount);
3922 #if !FEATURE_EH_FUNCLETS
3924 // Grab space for exception handling
3926 if (ehNeedsShadowSPslots())
3928 // The first slot is reserved for ICodeManager::FixContext(ppEndRegion)
3929 // ie. the offset of the end-of-last-executed-filter
3930 unsigned slotsNeeded = 1;
3932 unsigned handlerNestingLevel = ehMaxHndNestingCount;
3934 if (opts.compDbgEnC && (handlerNestingLevel < (unsigned)MAX_EnC_HANDLER_NESTING_LEVEL))
3935 handlerNestingLevel = (unsigned)MAX_EnC_HANDLER_NESTING_LEVEL;
3937 slotsNeeded += handlerNestingLevel;
3939 // For a filter (which can be active at the same time as a catch/finally handler)
3941 // For zero-termination of the shadow-Stack-pointer chain
3944 lvaShadowSPslotsVar = lvaGrabTempWithImplicitUse(false DEBUGARG("lvaShadowSPslotsVar"));
3945 LclVarDsc* shadowSPslotsVar = &lvaTable[lvaShadowSPslotsVar];
3946 shadowSPslotsVar->lvType = TYP_BLK;
3947 shadowSPslotsVar->lvExactSize = (slotsNeeded * TARGET_POINTER_SIZE);
3950 #endif // !FEATURE_EH_FUNCLETS
3952 // PSPSym and LocAllocSPvar are not used by the CoreRT ABI
3953 if (!IsTargetAbi(CORINFO_CORERT_ABI))
3955 #if FEATURE_EH_FUNCLETS
3956 if (ehNeedsPSPSym())
3958 lvaPSPSym = lvaGrabTempWithImplicitUse(false DEBUGARG("PSPSym"));
3959 LclVarDsc* lclPSPSym = &lvaTable[lvaPSPSym];
3960 lclPSPSym->lvType = TYP_I_IMPL;
3962 #endif // FEATURE_EH_FUNCLETS
3964 #ifdef JIT32_GCENCODER
3965 // LocAllocSPvar is only required by the implicit frame layout expected by the VM on x86. Whether
3966 // a function contains a Localloc is conveyed in the GC information, in the InfoHdrSmall.localloc
3967 // field. The function must have an EBP frame. Then, the VM finds the LocAllocSP slot by assuming
3968 // the following stack layout:
3970 // -- higher addresses --
3971 // saved EBP <-- EBP points here
3972 // other callee-saved registers // InfoHdrSmall.savedRegsCountExclFP specifies this size
3973 // optional GS cookie // InfoHdrSmall.security is 1 if this exists
3975 // -- lower addresses --
3977 // See also eetwain.cpp::GetLocallocSPOffset() and its callers.
3978 if (compLocallocUsed)
3980 lvaLocAllocSPvar = lvaGrabTempWithImplicitUse(false DEBUGARG("LocAllocSPvar"));
3981 LclVarDsc* locAllocSPvar = &lvaTable[lvaLocAllocSPvar];
3982 locAllocSPvar->lvType = TYP_I_IMPL;
3984 #endif // JIT32_GCENCODER
3987 // Ref counting is now enabled normally.
3988 lvaRefCountState = RCS_NORMAL;
3991 const bool setSlotNumbers = true;
3993 const bool setSlotNumbers = opts.compScopeInfo && (info.compVarScopesCount > 0);
3994 #endif // defined(DEBUG)
3996 const bool isRecompute = false;
3997 lvaComputeRefCounts(isRecompute, setSlotNumbers);
3999 // If we're not optimizing, we're done.
4000 if (opts.MinOpts() || opts.compDbgCode)
4006 assert(!opts.MinOpts() && !opts.compDbgCode);
4008 // Note: optAddCopies() depends on lvaRefBlks, which is set in lvaMarkLocalVars(BasicBlock*), called above.
4012 if (lvaKeepAliveAndReportThis())
4014 lvaTable[0].lvImplicitlyReferenced = 1;
4015 // This isn't strictly needed as we will make a copy of the param-type-arg
4016 // in the prolog. However, this ensures that the LclVarDsc corresponding to
4017 // info.compTypeCtxtArg is valid.
4019 else if (lvaReportParamTypeArg())
4021 lvaTable[info.compTypeCtxtArg].lvImplicitlyReferenced = 1;
4024 lvaSortByRefCount();
4027 //------------------------------------------------------------------------
4028 // lvaComputeRefCounts: compute ref counts for locals
4031 // isRecompute -- true if we just want ref counts and no other side effects;
4032 // false means to also look for true boolean locals, lay
4033 // groundwork for assertion prop, check type consistency, etc.
4034 // See lvaMarkLclRefs for details on what else goes on.
4035 // setSlotNumbers -- true if local slot numbers should be assigned.
4038 // Some implicit references are given actual counts or weight bumps here
4039 // to match pre-existing behavior.
4041 // In fast-jitting modes where we don't ref count locals, this bypasses
4042 // actual counting, and makes all locals implicitly referenced on first
4043 // compute. It asserts all locals are implicitly referenced on recompute.
4045 void Compiler::lvaComputeRefCounts(bool isRecompute, bool setSlotNumbers)
4047 JITDUMP("\n*** lvaComputeRefCounts ***\n");
4048 unsigned lclNum = 0;
4049 LclVarDsc* varDsc = nullptr;
4051 // Fast path for minopts and debug codegen.
4053 // On first compute: mark all locals as implicitly referenced and untracked.
4054 // On recompute: do nothing.
4055 if (opts.MinOpts() || opts.compDbgCode)
4061 // All local vars should be marked as implicitly referenced
4063 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4065 const bool isSpecialVarargsParam = varDsc->lvIsParam && raIsVarargsStackArg(lclNum);
4067 if (isSpecialVarargsParam)
4069 assert(varDsc->lvRefCnt() == 0);
4073 assert(varDsc->lvImplicitlyReferenced);
4076 assert(!varDsc->lvTracked);
4078 #endif // defined (DEBUG)
4084 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4086 // Using lvImplicitlyReferenced here ensures that we can't
4087 // accidentally make locals be unreferenced later by decrementing
4088 // the ref count to zero.
4090 // If, in minopts/debug, we really want to allow locals to become
4091 // unreferenced later, we'll have to explicitly clear this bit.
4092 varDsc->setLvRefCnt(0);
4093 varDsc->setLvRefCntWtd(0);
4095 // Special case for some varargs params ... these must
4096 // remain unreferenced.
4097 const bool isSpecialVarargsParam = varDsc->lvIsParam && raIsVarargsStackArg(lclNum);
4099 if (!isSpecialVarargsParam)
4101 varDsc->lvImplicitlyReferenced = 1;
4104 varDsc->lvTracked = 0;
4108 varDsc->lvSlotNum = lclNum;
4111 // Assert that it's ok to bypass the type repair logic in lvaMarkLclRefs
4112 assert((varDsc->lvType != TYP_UNDEF) && (varDsc->lvType != TYP_VOID) && (varDsc->lvType != TYP_UNKNOWN));
4116 lvaTrackedCount = 0;
4117 lvaTrackedCountInSizeTUnits = 0;
4121 // Slower path we take when optimizing, to get accurate counts.
4123 // First, reset all explicit ref counts and weights.
4124 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4126 varDsc->setLvRefCnt(0);
4127 varDsc->setLvRefCntWtd(BB_ZERO_WEIGHT);
4131 varDsc->lvSlotNum = lclNum;
4135 JITDUMP("\n*** lvaComputeRefCounts -- explicit counts ***\n");
4137 // Second, account for all explicit local variable references
4138 for (BasicBlock* block = fgFirstBB; block; block = block->bbNext)
4142 assert(isRecompute);
4144 const BasicBlock::weight_t weight = block->getBBWeight(this);
4145 for (GenTree* node : LIR::AsRange(block).NonPhiNodes())
4147 switch (node->OperGet())
4151 case GT_LCL_VAR_ADDR:
4152 case GT_LCL_FLD_ADDR:
4153 case GT_STORE_LCL_VAR:
4154 case GT_STORE_LCL_FLD:
4156 const unsigned lclNum = node->AsLclVarCommon()->gtLclNum;
4157 lvaTable[lclNum].incRefCnts(weight, this);
4168 lvaMarkLocalVars(block, isRecompute);
4172 JITDUMP("\n*** lvaComputeRefCounts -- implicit counts ***\n");
4174 // Third, bump ref counts for some implicit prolog references
4175 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4177 // Todo: review justification for these count bumps.
4178 if (varDsc->lvIsRegArg)
4180 if ((lclNum < info.compArgsCount) && (varDsc->lvRefCnt() > 0))
4182 // Fix 388376 ARM JitStress WP7
4183 varDsc->incRefCnts(BB_UNITY_WEIGHT, this);
4184 varDsc->incRefCnts(BB_UNITY_WEIGHT, this);
4187 // Ref count bump that was in lvaPromoteStructVar
4189 // This was formerly done during RCS_EARLY counting,
4190 // and we did not used to reset counts like we do now.
4191 if (varDsc->lvIsStructField)
4193 varDsc->incRefCnts(BB_UNITY_WEIGHT, this);
4197 // If we have JMP, all arguments must have a location
4198 // even if we don't use them inside the method
4199 if (compJmpOpUsed && varDsc->lvIsParam && (varDsc->lvRefCnt() == 0))
4201 // except when we have varargs and the argument is
4202 // passed on the stack. In that case, it's important
4203 // for the ref count to be zero, so that we don't attempt
4204 // to track them for GC info (which is not possible since we
4205 // don't know their offset in the stack). See the assert at the
4206 // end of raMarkStkVars and bug #28949 for more info.
4207 if (!raIsVarargsStackArg(lclNum))
4209 varDsc->lvImplicitlyReferenced = 1;
4215 void Compiler::lvaAllocOutgoingArgSpaceVar()
4217 #if FEATURE_FIXED_OUT_ARGS
4219 // Setup the outgoing argument region, in case we end up using it later
4221 if (lvaOutgoingArgSpaceVar == BAD_VAR_NUM)
4223 lvaOutgoingArgSpaceVar = lvaGrabTemp(false DEBUGARG("OutgoingArgSpace"));
4225 lvaTable[lvaOutgoingArgSpaceVar].lvType = TYP_LCLBLK;
4226 lvaTable[lvaOutgoingArgSpaceVar].lvImplicitlyReferenced = 1;
4229 noway_assert(lvaOutgoingArgSpaceVar >= info.compLocalsCount && lvaOutgoingArgSpaceVar < lvaCount);
4231 #endif // FEATURE_FIXED_OUT_ARGS
4234 inline void Compiler::lvaIncrementFrameSize(unsigned size)
4236 if (size > MAX_FrameSize || compLclFrameSize + size > MAX_FrameSize)
4238 BADCODE("Frame size overflow");
4241 compLclFrameSize += size;
4244 /****************************************************************************
4246 * Return true if absolute offsets of temps are larger than vars, or in other
4247 * words, did we allocate temps before of after vars. The /GS buffer overrun
4248 * checks want temps to be at low stack addresses than buffers
4250 bool Compiler::lvaTempsHaveLargerOffsetThanVars()
4253 // We never want to place the temps with larger offsets for ARM
4256 if (compGSReorderStackLayout)
4258 return codeGen->isFramePointerUsed();
4267 /****************************************************************************
4269 * Return an upper bound estimate for the size of the compiler spill temps
4272 unsigned Compiler::lvaGetMaxSpillTempSize()
4274 unsigned result = 0;
4276 if (lvaDoneFrameLayout >= REGALLOC_FRAME_LAYOUT)
4278 result = codeGen->regSet.tmpGetTotalSize();
4282 result = MAX_SPILL_TEMP_SIZE;
4288 /*****************************************************************************
4290 * Compute stack frame offsets for arguments, locals and optionally temps.
4292 * The frame is laid out as follows for x86:
4297 * |-----------------------|
4300 * |-----------------------| <---- Virtual '0'
4301 * | return address |
4302 * +=======================+
4303 * |Callee saved registers |
4304 * |-----------------------|
4306 * |-----------------------|
4308 * |-----------------------| <---- Ambient ESP
4309 * | Arguments for the |
4321 * |-----------------------|
4324 * |-----------------------| <---- Virtual '0'
4325 * | return address |
4326 * +=======================+
4328 * |-----------------------| <---- EBP
4329 * |Callee saved registers |
4330 * |-----------------------|
4331 * | security object |
4332 * |-----------------------|
4334 * |-----------------------|
4335 * | Last-executed-filter |
4336 * |-----------------------|
4340 * |-----------------------|
4344 * ~-----------------------|
4346 * |-----------------------|
4348 * |-----------------------| <---- Ambient ESP
4349 * | Arguments for the |
4358 * The frame is laid out as follows for x64:
4362 * |-----------------------|
4365 * |-----------------------|
4366 * | 4 fixed incoming |
4367 * | argument slots |
4368 * |-----------------------| <---- Caller's SP & Virtual '0'
4369 * | return address |
4370 * +=======================+
4371 * | Callee saved Int regs |
4372 * -------------------------
4373 * | Padding | <---- this padding (0 or 8 bytes) is to ensure flt registers are saved at a mem location aligned at 16-bytes
4374 * | | so that we can save 128-bit callee saved xmm regs using performant "movaps" instruction instead of "movups"
4375 * -------------------------
4376 * | Callee saved Flt regs | <----- entire 128-bits of callee saved xmm registers are stored here
4377 * |-----------------------|
4379 * |-----------------------|
4381 * |-----------------------|
4382 * | Arguments for the |
4385 * |-----------------------|
4386 * | 4 fixed outgoing |
4387 * | argument slots |
4388 * |-----------------------| <---- Ambient RSP
4397 * |-----------------------|
4400 * |-----------------------|
4401 * | 4 fixed incoming |
4402 * | argument slots |
4403 * |-----------------------| <---- Caller's SP & Virtual '0'
4404 * | return address |
4405 * +=======================+
4406 * | Callee saved Int regs |
4407 * -------------------------
4409 * -------------------------
4410 * | Callee saved Flt regs |
4411 * |-----------------------|
4412 * | security object |
4413 * |-----------------------|
4415 * |-----------------------|
4421 * |-----------------------|
4423 * |-----------------------|
4425 * ~ localloc ~ // not in frames with EH
4427 * |-----------------------|
4428 * | PSPSym | // only in frames with EH (thus no localloc)
4430 * |-----------------------| <---- RBP in localloc frames (max 240 bytes from Initial-SP)
4431 * | Arguments for the |
4434 * |-----------------------|
4435 * | 4 fixed outgoing |
4436 * | argument slots |
4437 * |-----------------------| <---- Ambient RSP (before localloc, this is Initial-SP)
4444 * The frame is laid out as follows for ARM (this is a general picture; details may differ for different conditions):
4448 * |-----------------------|
4451 * +=======================+ <---- Caller's SP
4452 * | Pre-spill registers |
4453 * |-----------------------| <---- Virtual '0'
4454 * |Callee saved registers |
4455 * |-----------------------|
4456 * ~ possible double align ~
4457 * |-----------------------|
4458 * | security object |
4459 * |-----------------------|
4461 * |-----------------------|
4462 * | possible GS cookie |
4463 * |-----------------------|
4465 * |-----------------------|
4466 * | possible GS cookie |
4467 * |-----------------------|
4469 * |-----------------------|
4470 * | Stub Argument Var |
4471 * |-----------------------|
4472 * |Inlined PInvoke Frame V|
4473 * |-----------------------|
4474 * ~ possible double align ~
4475 * |-----------------------|
4476 * | Arguments for the |
4479 * |-----------------------| <---- Ambient SP
4488 * |-----------------------|
4491 * +=======================+ <---- Caller's SP
4492 * | Pre-spill registers |
4493 * |-----------------------| <---- Virtual '0'
4494 * |Callee saved registers |
4495 * |-----------------------|
4496 * | PSPSym | // Only for frames with EH, which means FP-based frames
4497 * |-----------------------|
4498 * ~ possible double align ~
4499 * |-----------------------|
4500 * | security object |
4501 * |-----------------------|
4503 * |-----------------------|
4504 * | possible GS cookie |
4505 * |-----------------------|
4507 * |-----------------------|
4508 * | possible GS cookie |
4509 * |-----------------------|
4511 * |-----------------------|
4512 * | Stub Argument Var |
4513 * |-----------------------|
4514 * |Inlined PInvoke Frame V|
4515 * |-----------------------|
4516 * ~ possible double align ~
4517 * |-----------------------|
4519 * |-----------------------|
4520 * | Arguments for the |
4523 * |-----------------------| <---- Ambient SP
4530 * The frame is laid out as follows for ARM64 (this is a general picture; details may differ for different conditions):
4531 * TODO-ARM64-NYI: this is preliminary (copied from ARM and modified), and needs to be reviewed.
4532 * NOTE: SP must be 16-byte aligned, so there may be alignment slots in the frame.
4533 * We will often save and establish a frame pointer to create better ETW stack walks.
4537 * |-----------------------|
4540 * +=======================+ <---- Caller's SP
4541 * | homed | // this is only needed if reg argument need to be homed, e.g., for varargs
4542 * | register arguments |
4543 * |-----------------------| <---- Virtual '0'
4544 * |Callee saved registers |
4546 * |-----------------------|
4547 * | security object |
4548 * |-----------------------|
4550 * |-----------------------|
4551 * | possible GS cookie |
4552 * |-----------------------|
4554 * |-----------------------|
4555 * | possible GS cookie |
4556 * |-----------------------|
4558 * |-----------------------|
4559 * | Stub Argument Var |
4560 * |-----------------------|
4561 * |Inlined PInvoke Frame V|
4562 * |-----------------------|
4564 * |-----------------------|
4565 * | Saved FP | <---- Frame pointer
4566 * |-----------------------|
4567 * | Stack arguments for |
4568 * | the next function |
4569 * |-----------------------| <---- SP
4576 * FP (R29 / x29) frames
4578 * |-----------------------|
4581 * +=======================+ <---- Caller's SP
4582 * | optional homed | // this is only needed if reg argument need to be homed, e.g., for varargs
4583 * | register arguments |
4584 * |-----------------------| <---- Virtual '0'
4585 * |Callee saved registers |
4587 * |-----------------------|
4588 * | PSPSym | // Only for frames with EH, which requires FP-based frames
4589 * |-----------------------|
4590 * | security object |
4591 * |-----------------------|
4593 * |-----------------------|
4594 * | possible GS cookie |
4595 * |-----------------------|
4597 * |-----------------------|
4598 * | possible GS cookie |
4599 * |-----------------------|
4601 * |-----------------------|
4602 * | Stub Argument Var |
4603 * |-----------------------|
4604 * |Inlined PInvoke Frame V|
4605 * |-----------------------|
4607 * |-----------------------|
4608 * | Saved FP | <---- Frame pointer
4609 * |-----------------------|
4611 * |-----------------------|
4612 * | Stack arguments for |
4613 * | the next function |
4614 * |-----------------------| <---- Ambient SP
4621 * Doing this all in one pass is 'hard'. So instead we do it in 2 basic passes:
4622 * 1. Assign all the offsets relative to the Virtual '0'. Offsets above (the
4623 * incoming arguments) are positive. Offsets below (everything else) are
4624 * negative. This pass also calcuates the total frame size (between Caller's
4625 * SP/return address and the Ambient SP).
4626 * 2. Figure out where to place the frame pointer, and then adjust the offsets
4627 * as needed for the final stack size and whether the offset is frame pointer
4628 * relative or stack pointer relative.
4633 void Compiler::lvaAssignFrameOffsets(FrameLayoutState curState)
4635 noway_assert((lvaDoneFrameLayout < curState) || (curState == REGALLOC_FRAME_LAYOUT));
4637 lvaDoneFrameLayout = curState;
4643 printf("*************** In lvaAssignFrameOffsets");
4644 if (curState == INITIAL_FRAME_LAYOUT)
4646 printf("(INITIAL_FRAME_LAYOUT)");
4648 else if (curState == PRE_REGALLOC_FRAME_LAYOUT)
4650 printf("(PRE_REGALLOC_FRAME_LAYOUT)");
4652 else if (curState == REGALLOC_FRAME_LAYOUT)
4654 printf("(REGALLOC_FRAME_LAYOUT)");
4656 else if (curState == TENTATIVE_FRAME_LAYOUT)
4658 printf("(TENTATIVE_FRAME_LAYOUT)");
4660 else if (curState == FINAL_FRAME_LAYOUT)
4662 printf("(FINAL_FRAME_LAYOUT)");
4666 printf("(UNKNOWN)");
4673 #if FEATURE_FIXED_OUT_ARGS
4674 assert(lvaOutgoingArgSpaceVar != BAD_VAR_NUM);
4675 #endif // FEATURE_FIXED_OUT_ARGS
4677 /*-------------------------------------------------------------------------
4679 * First process the arguments.
4681 *-------------------------------------------------------------------------
4684 lvaAssignVirtualFrameOffsetsToArgs();
4686 /*-------------------------------------------------------------------------
4688 * Now compute stack offsets for any variables that don't live in registers
4690 *-------------------------------------------------------------------------
4693 lvaAssignVirtualFrameOffsetsToLocals();
4697 /*-------------------------------------------------------------------------
4699 * Now patch the offsets
4701 *-------------------------------------------------------------------------
4704 lvaFixVirtualFrameOffsets();
4706 // Modify the stack offset for fields of promoted structs.
4707 lvaAssignFrameOffsetsToPromotedStructs();
4709 /*-------------------------------------------------------------------------
4713 *-------------------------------------------------------------------------
4716 // If it's not the final frame layout, then it's just an estimate. This means
4717 // we're allowed to once again write to these variables, even if we've read
4718 // from them to make tentative code generation or frame layout decisions.
4719 if (curState < FINAL_FRAME_LAYOUT)
4721 codeGen->resetFramePointerUsedWritePhase();
4725 /*****************************************************************************
4726 * lvaFixVirtualFrameOffsets() : Now that everything has a virtual offset,
4727 * determine the final value for the frame pointer (if needed) and then
4728 * adjust all the offsets appropriately.
4730 * This routine fixes virtual offset to be relative to frame pointer or SP
4731 * based on whether varDsc->lvFramePointerBased is true or false respectively.
4733 void Compiler::lvaFixVirtualFrameOffsets()
4737 #if FEATURE_EH_FUNCLETS && defined(_TARGET_AMD64_)
4738 if (lvaPSPSym != BAD_VAR_NUM)
4740 // We need to fix the offset of the PSPSym so there is no padding between it and the outgoing argument space.
4741 // Without this code, lvaAlignFrame might have put the padding lower than the PSPSym, which would be between
4742 // the PSPSym and the outgoing argument space.
4743 varDsc = &lvaTable[lvaPSPSym];
4744 assert(varDsc->lvFramePointerBased); // We always access it RBP-relative.
4745 assert(!varDsc->lvMustInit); // It is never "must init".
4746 varDsc->lvStkOffs = codeGen->genCallerSPtoInitialSPdelta() + lvaLclSize(lvaOutgoingArgSpaceVar);
4750 // The delta to be added to virtual offset to adjust it relative to frame pointer or SP
4753 #ifdef _TARGET_XARCH_
4754 delta += REGSIZE_BYTES; // pushed PC (return address) for x86/x64
4756 if (codeGen->doubleAlignOrFramePointerUsed())
4758 delta += REGSIZE_BYTES; // pushed EBP (frame pointer)
4762 if (!codeGen->isFramePointerUsed())
4764 // pushed registers, return address, and padding
4765 delta += codeGen->genTotalFrameSize();
4767 #if defined(_TARGET_ARM_)
4770 // We set FP to be after LR, FP
4771 delta += 2 * REGSIZE_BYTES;
4773 #elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
4777 delta += codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta();
4779 #endif //_TARGET_AMD64_
4782 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4784 bool doAssignStkOffs = true;
4786 // Can't be relative to EBP unless we have an EBP
4787 noway_assert(!varDsc->lvFramePointerBased || codeGen->doubleAlignOrFramePointerUsed());
4789 // Is this a non-param promoted struct field?
4790 // if so then set doAssignStkOffs to false.
4792 if (varDsc->lvIsStructField && !varDsc->lvIsParam)
4794 LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl];
4795 lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);
4797 if (promotionType == PROMOTION_TYPE_DEPENDENT)
4799 doAssignStkOffs = false; // Assigned later in lvaAssignFrameOffsetsToPromotedStructs()
4803 if (!varDsc->lvOnFrame)
4805 if (!varDsc->lvIsParam
4806 #if !defined(_TARGET_AMD64_)
4807 || (varDsc->lvIsRegArg
4808 #if defined(_TARGET_ARM_) && defined(PROFILING_SUPPORTED)
4809 && compIsProfilerHookNeeded() &&
4810 !lvaIsPreSpilled(lclNum, codeGen->regSet.rsMaskPreSpillRegs(false)) // We need assign stack offsets
4811 // for prespilled arguments
4814 #endif // !defined(_TARGET_AMD64_)
4817 doAssignStkOffs = false; // Not on frame or an incomming stack arg
4821 if (doAssignStkOffs)
4823 varDsc->lvStkOffs += delta;
4826 if (genDoubleAlign() && !codeGen->isFramePointerUsed())
4828 if (varDsc->lvFramePointerBased)
4830 varDsc->lvStkOffs -= delta;
4832 // We need to re-adjust the offsets of the parameters so they are EBP
4833 // relative rather than stack/frame pointer relative
4835 varDsc->lvStkOffs += (2 * TARGET_POINTER_SIZE); // return address and pushed EBP
4837 noway_assert(varDsc->lvStkOffs >= FIRST_ARG_STACK_OFFS);
4841 // On System V environments the stkOffs could be 0 for params passed in registers.
4842 assert(codeGen->isFramePointerUsed() ||
4843 varDsc->lvStkOffs >= 0); // Only EBP relative references can have negative offsets
4847 assert(codeGen->regSet.tmpAllFree());
4848 for (TempDsc* temp = codeGen->regSet.tmpListBeg(); temp != nullptr; temp = codeGen->regSet.tmpListNxt(temp))
4850 temp->tdAdjustTempOffs(delta);
4853 lvaCachedGenericContextArgOffs += delta;
4855 #if FEATURE_FIXED_OUT_ARGS
4857 if (lvaOutgoingArgSpaceVar != BAD_VAR_NUM)
4859 varDsc = &lvaTable[lvaOutgoingArgSpaceVar];
4860 varDsc->lvStkOffs = 0;
4861 varDsc->lvFramePointerBased = false;
4862 varDsc->lvMustInit = false;
4865 #endif // FEATURE_FIXED_OUT_ARGS
4869 bool Compiler::lvaIsPreSpilled(unsigned lclNum, regMaskTP preSpillMask)
4871 const LclVarDsc& desc = lvaTable[lclNum];
4872 return desc.lvIsRegArg && (preSpillMask & genRegMask(desc.lvArgReg));
4874 #endif // _TARGET_ARM_
4876 /*****************************************************************************
4877 * lvaUpdateArgsWithInitialReg() : For each argument variable descriptor, update
4878 * its current register with the initial register as assigned by LSRA.
4880 void Compiler::lvaUpdateArgsWithInitialReg()
4887 for (unsigned lclNum = 0; lclNum < info.compArgsCount; lclNum++)
4889 LclVarDsc* varDsc = lvaTable + lclNum;
4891 if (varDsc->lvPromotedStruct())
4893 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
4895 unsigned fieldVarNum = varDsc->lvFieldLclStart;
4896 varDsc = lvaTable + fieldVarNum;
4899 noway_assert(varDsc->lvIsParam);
4901 if (varDsc->lvIsRegCandidate())
4903 varDsc->lvRegNum = varDsc->lvArgInitReg;
4908 /*****************************************************************************
4909 * lvaAssignVirtualFrameOffsetsToArgs() : Assign virtual stack offsets to the
4910 * arguments, and implicit arguments (this ptr, return buffer, generics,
4913 void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
4915 unsigned lclNum = 0;
4917 #ifdef UNIX_AMD64_ABI
4918 int callerArgOffset = 0;
4919 #endif // UNIX_AMD64_ABI
4922 Assign stack offsets to arguments (in reverse order of passing).
4924 This means that if we pass arguments left->right, we start at
4925 the end of the list and work backwards, for right->left we start
4926 with the first argument and move forward.
4928 This is all relative to our Virtual '0'
4931 if (Target::g_tgtArgOrder == Target::ARG_ORDER_L2R)
4933 argOffs = compArgSize;
4936 /* Update the argOffs to reflect arguments that are passed in registers */
4938 noway_assert(codeGen->intRegState.rsCalleeRegArgCount <= MAX_REG_ARG);
4939 noway_assert(compArgSize >= codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES);
4942 argOffs -= codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES;
4945 // Update the arg initial register locations.
4946 lvaUpdateArgsWithInitialReg();
4948 /* Is there a "this" argument? */
4950 if (!info.compIsStatic)
4952 noway_assert(lclNum == info.compThisArg);
4953 #ifndef _TARGET_X86_
4955 lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
4956 #endif // _TARGET_X86_
4960 /* if we have a hidden buffer parameter, that comes here */
4962 if (info.compRetBuffArg != BAD_VAR_NUM)
4964 noway_assert(lclNum == info.compRetBuffArg);
4965 noway_assert(lvaTable[lclNum].lvIsRegArg);
4966 #ifndef _TARGET_X86_
4968 lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
4969 #endif // _TARGET_X86_
4973 #if USER_ARGS_COME_LAST
4975 //@GENERICS: extra argument for instantiation info
4976 if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
4978 noway_assert(lclNum == (unsigned)info.compTypeCtxtArg);
4979 argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
4980 argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
4983 if (info.compIsVarArgs)
4985 argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
4986 argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
4989 #endif // USER_ARGS_COME_LAST
4991 CORINFO_ARG_LIST_HANDLE argLst = info.compMethodInfo->args.args;
4992 unsigned argSigLen = info.compMethodInfo->args.numArgs;
4996 // struct_n { int; int; ... n times };
4998 // Consider signature:
5000 // Foo (float a,double b,float c,double d,float e,double f,float g,double h,
5001 // float i,double j,float k,double l,struct_3 m) { }
5003 // Basically the signature is: (all float regs full, 1 double, struct_3);
5005 // The double argument occurs before pre spill in the argument iteration and
5006 // computes an argOffset of 0. struct_3 offset becomes 8. This is wrong.
5007 // Because struct_3 is prespilled and double occurs after prespill.
5008 // The correct offsets are double = 16 (aligned stk), struct_3 = 0..12,
5009 // Offset 12 will be skipped for double alignment of double.
5011 // Another example is (struct_2, all float regs full, double, struct_2);
5012 // Here, notice the order is similarly messed up because of 2 pre-spilled
5016 // ARG_INDEX(i) > ARG_INDEX(j) DOES NOT IMPLY |ARG_OFFSET(i)| > |ARG_OFFSET(j)|
5018 // Therefore, we'll do a two pass offset calculation, one that considers pre-spill
5019 // and the next, stack args.
5022 unsigned argLcls = 0;
5024 // Take care of pre spill registers first.
5025 regMaskTP preSpillMask = codeGen->regSet.rsMaskPreSpillRegs(false);
5026 regMaskTP tempMask = RBM_NONE;
5027 for (unsigned i = 0, preSpillLclNum = lclNum; i < argSigLen; ++i, ++preSpillLclNum)
5029 if (lvaIsPreSpilled(preSpillLclNum, preSpillMask))
5031 unsigned argSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
5032 argOffs = lvaAssignVirtualFrameOffsetToArg(preSpillLclNum, argSize, argOffs);
5035 // Early out if we can. If size is 8 and base reg is 2, then the mask is 0x1100
5036 tempMask |= ((((1 << (roundUp(argSize, TARGET_POINTER_SIZE) / REGSIZE_BYTES))) - 1)
5037 << lvaTable[preSpillLclNum].lvArgReg);
5038 if (tempMask == preSpillMask)
5040 // We won't encounter more pre-spilled registers,
5041 // so don't bother iterating further.
5045 argLst = info.compCompHnd->getArgNext(argLst);
5048 // Take care of non pre-spilled stack arguments.
5049 argLst = info.compMethodInfo->args.args;
5050 for (unsigned i = 0, stkLclNum = lclNum; i < argSigLen; ++i, ++stkLclNum)
5052 if (!lvaIsPreSpilled(stkLclNum, preSpillMask))
5055 lvaAssignVirtualFrameOffsetToArg(stkLclNum, eeGetArgSize(argLst, &info.compMethodInfo->args), argOffs);
5058 argLst = info.compCompHnd->getArgNext(argLst);
5062 #else // !_TARGET_ARM_
5063 for (unsigned i = 0; i < argSigLen; i++)
5065 unsigned argumentSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
5067 #ifdef UNIX_AMD64_ABI
5068 // On the stack frame the homed arg always takes a full number of slots
5069 // for proper stack alignment. Make sure the real struct size is properly rounded up.
5070 argumentSize = roundUp(argumentSize, TARGET_POINTER_SIZE);
5071 #endif // UNIX_AMD64_ABI
5074 lvaAssignVirtualFrameOffsetToArg(lclNum++, argumentSize, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5075 argLst = info.compCompHnd->getArgNext(argLst);
5077 #endif // !_TARGET_ARM_
5079 #if !USER_ARGS_COME_LAST
5081 //@GENERICS: extra argument for instantiation info
5082 if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
5084 noway_assert(lclNum == (unsigned)info.compTypeCtxtArg);
5085 argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
5086 argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5089 if (info.compIsVarArgs)
5091 argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
5092 argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5095 #endif // USER_ARGS_COME_LAST
5098 #ifdef UNIX_AMD64_ABI
5100 // lvaAssignVirtualFrameOffsetToArg() : Assign virtual stack offsets to an
5101 // individual argument, and return the offset for the next argument.
5102 // Note: This method only calculates the initial offset of the stack passed/spilled arguments
5103 // (if any - the RA might decide to spill(home on the stack) register passed arguments, if rarely used.)
5104 // The final offset is calculated in lvaFixVirtualFrameOffsets method. It accounts for FP existance,
5105 // ret address slot, stack frame padding, alloca instructions, etc.
5106 // Note: This is the implementation for UNIX_AMD64 System V platforms.
5108 int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum,
5110 int argOffs UNIX_AMD64_ABI_ONLY_ARG(int* callerArgOffset))
5112 noway_assert(lclNum < info.compArgsCount);
5113 noway_assert(argSize);
5115 if (Target::g_tgtArgOrder == Target::ARG_ORDER_L2R)
5120 unsigned fieldVarNum = BAD_VAR_NUM;
5122 noway_assert(lclNum < lvaCount);
5123 LclVarDsc* varDsc = lvaTable + lclNum;
5125 if (varDsc->lvPromotedStruct())
5127 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
5128 fieldVarNum = varDsc->lvFieldLclStart;
5130 lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
5132 if (promotionType == PROMOTION_TYPE_INDEPENDENT)
5134 lclNum = fieldVarNum;
5135 noway_assert(lclNum < lvaCount);
5136 varDsc = lvaTable + lclNum;
5137 assert(varDsc->lvIsStructField);
5141 noway_assert(varDsc->lvIsParam);
5143 if (varDsc->lvIsRegArg)
5145 // Argument is passed in a register, don't count it
5146 // when updating the current offset on the stack.
5148 if (varDsc->lvOnFrame)
5150 // The offset for args needs to be set only for the stack homed arguments for System V.
5151 varDsc->lvStkOffs = argOffs;
5155 varDsc->lvStkOffs = 0;
5160 // For Windows AMD64 there are 4 slots for the register passed arguments on the top of the caller's stack.
5161 // This is where they are always homed. So, they can be accessed with positive offset.
5162 // On System V platforms, if the RA decides to home a register passed arg on the stack, it creates a stack
5163 // location on the callee stack (like any other local var.) In such a case, the register passed, stack homed
5164 // arguments are accessed using negative offsets and the stack passed arguments are accessed using positive
5165 // offset (from the caller's stack.)
5166 // For System V platforms if there is no frame pointer the caller stack parameter offset should include the
5167 // callee allocated space. If frame register is used, the callee allocated space should not be included for
5168 // accessing the caller stack parameters. The last two requirements are met in lvaFixVirtualFrameOffsets
5169 // method, which fixes the offsets, based on frame pointer existence, existence of alloca instructions, ret
5170 // address pushed, ets.
5172 varDsc->lvStkOffs = *callerArgOffset;
5173 // Structs passed on stack could be of size less than TARGET_POINTER_SIZE.
5174 // Make sure they get at least TARGET_POINTER_SIZE on the stack - this is required for alignment.
5175 if (argSize > TARGET_POINTER_SIZE)
5177 *callerArgOffset += (int)roundUp(argSize, TARGET_POINTER_SIZE);
5181 *callerArgOffset += TARGET_POINTER_SIZE;
5185 // For struct promoted parameters we need to set the offsets for both LclVars.
5187 // For a dependent promoted struct we also assign the struct fields stack offset
5188 if (varDsc->lvPromotedStruct())
5190 lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
5192 if (promotionType == PROMOTION_TYPE_DEPENDENT)
5194 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
5196 assert(fieldVarNum == varDsc->lvFieldLclStart);
5197 lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
5200 // For an independent promoted struct field we also assign the parent struct stack offset
5201 else if (varDsc->lvIsStructField)
5203 noway_assert(varDsc->lvParentLcl < lvaCount);
5204 lvaTable[varDsc->lvParentLcl].lvStkOffs = varDsc->lvStkOffs;
5207 if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L && !varDsc->lvIsRegArg)
5215 #else // !UNIX_AMD64_ABI
5218 // lvaAssignVirtualFrameOffsetToArg() : Assign virtual stack offsets to an
5219 // individual argument, and return the offset for the next argument.
5220 // Note: This method only calculates the initial offset of the stack passed/spilled arguments
5221 // (if any - the RA might decide to spill(home on the stack) register passed arguments, if rarely used.)
5222 // The final offset is calculated in lvaFixVirtualFrameOffsets method. It accounts for FP existance,
5223 // ret address slot, stack frame padding, alloca instructions, etc.
5224 // Note: This implementation for all the platforms but UNIX_AMD64 OSs (System V 64 bit.)
5225 int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum,
5227 int argOffs UNIX_AMD64_ABI_ONLY_ARG(int* callerArgOffset))
5229 noway_assert(lclNum < info.compArgsCount);
5230 noway_assert(argSize);
5232 if (Target::g_tgtArgOrder == Target::ARG_ORDER_L2R)
5237 unsigned fieldVarNum = BAD_VAR_NUM;
5239 noway_assert(lclNum < lvaCount);
5240 LclVarDsc* varDsc = lvaTable + lclNum;
5242 if (varDsc->lvPromotedStruct())
5244 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
5245 fieldVarNum = varDsc->lvFieldLclStart;
5247 lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
5249 if (promotionType == PROMOTION_TYPE_INDEPENDENT)
5251 lclNum = fieldVarNum;
5252 noway_assert(lclNum < lvaCount);
5253 varDsc = lvaTable + lclNum;
5254 assert(varDsc->lvIsStructField);
5258 noway_assert(varDsc->lvIsParam);
5260 if (varDsc->lvIsRegArg)
5262 /* Argument is passed in a register, don't count it
5263 * when updating the current offset on the stack */
5264 CLANG_FORMAT_COMMENT_ANCHOR;
5266 #if !defined(_TARGET_ARMARCH_)
5268 // TODO: Remove this noway_assert and replace occurrences of TARGET_POINTER_SIZE with argSize
5269 // Also investigate why we are incrementing argOffs for X86 as this seems incorrect
5271 noway_assert(argSize == TARGET_POINTER_SIZE);
5275 #if defined(_TARGET_X86_)
5276 argOffs += TARGET_POINTER_SIZE;
5277 #elif defined(_TARGET_AMD64_)
5278 // Register arguments on AMD64 also takes stack space. (in the backing store)
5279 varDsc->lvStkOffs = argOffs;
5280 argOffs += TARGET_POINTER_SIZE;
5281 #elif defined(_TARGET_ARM64_)
5282 // Register arguments on ARM64 only take stack space when they have a frame home.
5283 // Unless on windows and in a vararg method.
5284 #if FEATURE_ARG_SPLIT
5285 if (this->info.compIsVarArgs)
5287 if (varDsc->lvType == TYP_STRUCT && varDsc->lvOtherArgReg >= MAX_REG_ARG && varDsc->lvOtherArgReg != REG_NA)
5289 // This is a split struct. It will account for an extra (8 bytes)
5291 varDsc->lvStkOffs += TARGET_POINTER_SIZE;
5292 argOffs += TARGET_POINTER_SIZE;
5295 #endif // FEATURE_ARG_SPLIT
5297 #elif defined(_TARGET_ARM_)
5298 // On ARM we spill the registers in codeGen->regSet.rsMaskPreSpillRegArg
5299 // in the prolog, so we have to fill in lvStkOffs here
5301 regMaskTP regMask = genRegMask(varDsc->lvArgReg);
5302 if (codeGen->regSet.rsMaskPreSpillRegArg & regMask)
5304 // Signature: void foo(struct_8, int, struct_4)
5305 // ------- CALLER SP -------
5307 // r2 int - not prespilled, but added for alignment. argOffs should skip this.
5310 // -------------------------
5311 // If we added alignment we need to fix argOffs for all registers above alignment.
5312 if (codeGen->regSet.rsMaskPreSpillAlign != RBM_NONE)
5314 assert(genCountBits(codeGen->regSet.rsMaskPreSpillAlign) == 1);
5315 // Is register beyond the alignment pos?
5316 if (regMask > codeGen->regSet.rsMaskPreSpillAlign)
5318 // Increment argOffs just once for the _first_ register after alignment pos
5319 // in the prespill mask.
5320 if (!BitsBetween(codeGen->regSet.rsMaskPreSpillRegArg, regMask,
5321 codeGen->regSet.rsMaskPreSpillAlign))
5323 argOffs += TARGET_POINTER_SIZE;
5328 switch (varDsc->lvType)
5331 if (!varDsc->lvStructDoubleAlign)
5341 // Let's assign offsets to arg1, a double in r2. argOffs has to be 4 not 8.
5343 // ------- CALLER SP -------
5345 // r2 double -- argOffs = 4, but it doesn't need to be skipped, because there is no skipping.
5346 // r1 VACookie -- argOffs = 0
5347 // -------------------------
5349 // Consider argOffs as if it accounts for number of prespilled registers before the current
5350 // register. In the above example, for r2, it is r1 that is prespilled, but since r1 is
5351 // accounted for by argOffs being 4, there should have been no skipping. Instead, if we didn't
5352 // assign r1 to any variable, then argOffs would still be 0 which implies it is not accounting
5353 // for r1, equivalently r1 is skipped.
5355 // If prevRegsSize is unaccounted for by a corresponding argOffs, we must have skipped a register.
5357 genCountBits(codeGen->regSet.rsMaskPreSpillRegArg & (regMask - 1)) * TARGET_POINTER_SIZE;
5358 if (argOffs < prevRegsSize)
5360 // We must align up the argOffset to a multiple of 8 to account for skipped registers.
5361 argOffs = roundUp((unsigned)argOffs, 2 * TARGET_POINTER_SIZE);
5363 // We should've skipped only a single register.
5364 assert(argOffs == prevRegsSize);
5369 // No alignment of argOffs required
5372 varDsc->lvStkOffs = argOffs;
5376 #error Unsupported or unset target architecture
5381 #if defined(_TARGET_ARM_)
5382 // Dev11 Bug 42817: incorrect codegen for DrawFlatCheckBox causes A/V in WinForms
5384 // Here we have method with a signature (int a1, struct a2, struct a3, int a4, int a5).
5385 // Struct parameter 'a2' is 16-bytes with no alignment requirements;
5386 // it uses r1,r2,r3 and [OutArg+0] when passed.
5387 // Struct parameter 'a3' is 16-bytes that is required to be double aligned;
5388 // the caller skips [OutArg+4] and starts the argument at [OutArg+8].
5389 // Thus the caller generates the correct code to pass the arguments.
5390 // When generating code to receive the arguments we set codeGen->regSet.rsMaskPreSpillRegArg to [r1,r2,r3]
5391 // and spill these three registers as the first instruction in the prolog.
5392 // Then when we layout the arguments' stack offsets we have an argOffs 0 which
5393 // points at the location that we spilled r1 into the stack. For this first
5394 // struct we take the lvIsRegArg path above with "codeGen->regSet.rsMaskPreSpillRegArg &" matching.
5395 // Next when we calculate the argOffs for the second 16-byte struct we have an argOffs
5396 // of 16, which appears to be aligned properly so we don't skip a stack slot.
5398 // To fix this we must recover the actual OutArg offset by subtracting off the
5399 // sizeof of the PreSpill register args.
5400 // Then we align this offset to a multiple of 8 and add back the sizeof
5401 // of the PreSpill register args.
5403 // Dev11 Bug 71767: failure of assert(sizeofPreSpillRegArgs <= argOffs)
5405 // We have a method with 'this' passed in r0, RetBuf arg in r1, VarArgs cookie
5406 // in r2. The first user arg is a 144 byte struct with double alignment required,
5407 // r3 is skipped, and the struct is passed on the stack. However, 'r3' is added
5408 // to the codeGen->regSet.rsMaskPreSpillRegArg mask by the VarArgs cookie code, since we need to
5409 // home all the potential varargs arguments in registers, even if we don't have
5410 // signature type information for the variadic arguments. However, due to alignment,
5411 // we have skipped a register that doesn't have a corresponding symbol. Make up
5412 // for that by increasing argOffs here.
5415 int sizeofPreSpillRegArgs = genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true)) * REGSIZE_BYTES;
5417 if (argOffs < sizeofPreSpillRegArgs)
5419 // This can only happen if we skipped the last register spot because current stk arg
5420 // is a struct requiring alignment or a pre-spill alignment was required because the
5421 // first reg arg needed alignment.
5423 // Example 1: First Stk Argument requiring alignment in vararg case (same as above comment.)
5424 // Signature (int a0, int a1, int a2, struct {long} a3, ...)
5426 // stk arg a3 --> argOffs here will be 12 (r0-r2) but pre-spill will be 16.
5427 // ---- Caller SP ----
5428 // r3 --> Stack slot is skipped in this case.
5433 // Example 2: First Reg Argument requiring alignment in no-vararg case.
5434 // Signature (struct {long} a0, struct {int} a1, int a2, int a3)
5436 // stk arg --> argOffs here will be 12 {r0-r2} but pre-spill will be 16.
5437 // ---- Caller SP ----
5438 // r3 int a2 --> pushed (not pre-spilled) for alignment of a0 by lvaInitUserArgs.
5439 // r2 struct { int } a1
5440 // r0-r1 struct { long } a0
5441 CLANG_FORMAT_COMMENT_ANCHOR;
5443 #ifdef PROFILING_SUPPORTED
5444 // On Arm under profiler, r0-r3 are always prespilled on stack.
5445 // It is possible to have methods that accept only HFAs as parameters e.g. Signature(struct hfa1, struct
5446 // hfa2), in which case hfa1 and hfa2 will be en-registered in co-processor registers and will have an
5447 // argument offset less than size of preSpill.
5449 // For this reason the following conditions are asserted when not under profiler.
5450 if (!compIsProfilerHookNeeded())
5453 bool cond = ((info.compIsVarArgs || opts.compUseSoftFP) &&
5454 // Does cur stk arg require double alignment?
5455 ((varDsc->lvType == TYP_STRUCT && varDsc->lvStructDoubleAlign) ||
5456 (varDsc->lvType == TYP_DOUBLE) || (varDsc->lvType == TYP_LONG))) ||
5457 // Did first reg arg require alignment?
5458 (codeGen->regSet.rsMaskPreSpillAlign & genRegMask(REG_ARG_LAST));
5461 noway_assert(sizeofPreSpillRegArgs <=
5462 argOffs + TARGET_POINTER_SIZE); // at most one register of alignment
5464 argOffs = sizeofPreSpillRegArgs;
5467 noway_assert(argOffs >= sizeofPreSpillRegArgs);
5468 int argOffsWithoutPreSpillRegArgs = argOffs - sizeofPreSpillRegArgs;
5470 switch (varDsc->lvType)
5473 if (!varDsc->lvStructDoubleAlign)
5480 // We must align up the argOffset to a multiple of 8
5482 roundUp((unsigned)argOffsWithoutPreSpillRegArgs, 2 * TARGET_POINTER_SIZE) + sizeofPreSpillRegArgs;
5486 // No alignment of argOffs required
5489 #endif // _TARGET_ARM_
5491 varDsc->lvStkOffs = argOffs;
5494 // For struct promoted parameters we need to set the offsets for both LclVars.
5496 // For a dependent promoted struct we also assign the struct fields stack offset
5497 CLANG_FORMAT_COMMENT_ANCHOR;
5499 #if !defined(_TARGET_64BIT_)
5500 if ((varDsc->TypeGet() == TYP_LONG) && varDsc->lvPromoted)
5502 noway_assert(varDsc->lvFieldCnt == 2);
5503 fieldVarNum = varDsc->lvFieldLclStart;
5504 lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
5505 lvaTable[fieldVarNum + 1].lvStkOffs = varDsc->lvStkOffs + genTypeSize(TYP_INT);
5508 #endif // !defined(_TARGET_64BIT_)
5509 if (varDsc->lvPromotedStruct())
5511 lvaPromotionType promotionType = lvaGetPromotionType(varDsc);
5513 if (promotionType == PROMOTION_TYPE_DEPENDENT)
5515 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
5517 assert(fieldVarNum == varDsc->lvFieldLclStart);
5518 lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
5521 // For an independent promoted struct field we also assign the parent struct stack offset
5522 else if (varDsc->lvIsStructField)
5524 noway_assert(varDsc->lvParentLcl < lvaCount);
5525 lvaTable[varDsc->lvParentLcl].lvStkOffs = varDsc->lvStkOffs;
5528 if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L && !varDsc->lvIsRegArg)
5535 #endif // !UNIX_AMD64_ABI
5537 /*****************************************************************************
5538 * lvaAssignVirtualFrameOffsetsToLocals() : Assign virtual stack offsets to
5539 * locals, temps, and anything else. These will all be negative offsets
5540 * (stack grows down) relative to the virtual '0'/return address
5542 void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
5545 // codeGen->isFramePointerUsed is set in regalloc phase. Initialize it to a guess for pre-regalloc layout.
5546 if (lvaDoneFrameLayout <= PRE_REGALLOC_FRAME_LAYOUT)
5548 codeGen->setFramePointerUsed(codeGen->isFramePointerRequired());
5551 #ifdef _TARGET_XARCH_
5552 // On x86/amd64, the return address has already been pushed by the call instruction in the caller.
5553 stkOffs -= TARGET_POINTER_SIZE; // return address;
5555 // TODO-AMD64-CQ: for X64 eventually this should be pushed with all the other
5556 // calleeregs. When you fix this, you'll also need to fix
5557 // the assert at the bottom of this method
5558 if (codeGen->doubleAlignOrFramePointerUsed())
5560 stkOffs -= REGSIZE_BYTES;
5562 #endif //_TARGET_XARCH_
5564 int preSpillSize = 0;
5565 bool mustDoubleAlign = false;
5568 mustDoubleAlign = true;
5569 preSpillSize = genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true)) * REGSIZE_BYTES;
5570 #else // !_TARGET_ARM_
5572 if (genDoubleAlign())
5574 mustDoubleAlign = true; // X86 only
5577 #endif // !_TARGET_ARM_
5579 #ifdef _TARGET_ARM64_
5580 // If the frame pointer is used, then we'll save FP/LR at the bottom of the stack.
5581 // Otherwise, we won't store FP, and we'll store LR at the top, with the other callee-save
5582 // registers (if any).
5584 int initialStkOffs = 0;
5585 if (info.compIsVarArgs)
5587 // For varargs we always save all of the integer register arguments
5588 // so that they are contiguous with the incoming stack arguments.
5589 initialStkOffs = MAX_REG_ARG * REGSIZE_BYTES;
5590 stkOffs -= initialStkOffs;
5593 if (isFramePointerUsed())
5595 // Subtract off FP and LR.
5596 assert(compCalleeRegsPushed >= 2);
5597 stkOffs -= (compCalleeRegsPushed - 2) * REGSIZE_BYTES;
5601 stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES;
5604 #else // !_TARGET_ARM64_
5605 stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES;
5606 #endif // !_TARGET_ARM64_
5608 compLclFrameSize = 0;
5610 #ifdef _TARGET_AMD64_
5611 // In case of Amd64 compCalleeRegsPushed includes float regs (Xmm6-xmm15) that
5612 // need to be pushed. But Amd64 doesn't support push/pop of xmm registers.
5613 // Instead we need to allocate space for them on the stack and save them in prolog.
5614 // Therefore, we consider xmm registers being saved while computing stack offsets
5615 // but space for xmm registers is considered part of compLclFrameSize.
5617 // 1) We need to save the entire 128-bits of xmm register to stack, since amd64
5618 // prolog unwind codes allow encoding of an instruction that stores the entire xmm reg
5619 // at an offset relative to SP
5620 // 2) We adjust frame size so that SP is aligned at 16-bytes after pushing integer registers.
5621 // This means while saving the first xmm register to its allocated stack location we might
5622 // have to skip 8-bytes. The reason for padding is to use efficient "movaps" to save/restore
5623 // xmm registers to/from stack to match Jit64 codegen. Without the aligning on 16-byte
5624 // boundary we would have to use movups when offset turns out unaligned. Movaps is more
5625 // performant than movups.
5626 unsigned calleeFPRegsSavedSize = genCountBits(compCalleeFPRegsSavedMask) * XMM_REGSIZE_BYTES;
5627 if (calleeFPRegsSavedSize > 0 && ((stkOffs % XMM_REGSIZE_BYTES) != 0))
5629 // Take care of alignment
5630 int alignPad = (int)AlignmentPad((unsigned)-stkOffs, XMM_REGSIZE_BYTES);
5631 stkOffs -= alignPad;
5632 lvaIncrementFrameSize(alignPad);
5635 stkOffs -= calleeFPRegsSavedSize;
5636 lvaIncrementFrameSize(calleeFPRegsSavedSize);
5638 // Quirk for VS debug-launch scenario to work
5639 if (compVSQuirkStackPaddingNeeded > 0)
5644 printf("\nAdding VS quirk stack padding of %d bytes between save-reg area and locals\n",
5645 compVSQuirkStackPaddingNeeded);
5649 stkOffs -= compVSQuirkStackPaddingNeeded;
5650 lvaIncrementFrameSize(compVSQuirkStackPaddingNeeded);
5652 #endif //_TARGET_AMD64_
5654 #if FEATURE_EH_FUNCLETS && defined(_TARGET_ARMARCH_)
5655 if (lvaPSPSym != BAD_VAR_NUM)
5657 // On ARM/ARM64, if we need a PSPSym, allocate it first, before anything else, including
5658 // padding (so we can avoid computing the same padding in the funclet
5659 // frame). Note that there is no special padding requirement for the PSPSym.
5660 noway_assert(codeGen->isFramePointerUsed()); // We need an explicit frame pointer
5661 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaPSPSym, TARGET_POINTER_SIZE, stkOffs);
5663 #endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARMARCH_)
5665 if (mustDoubleAlign)
5667 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
5669 // Allocate a pointer sized stack slot, since we may need to double align here
5670 // when lvaDoneFrameLayout == FINAL_FRAME_LAYOUT
5672 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5673 stkOffs -= TARGET_POINTER_SIZE;
5675 // If we have any TYP_LONG, TYP_DOUBLE or double aligned structs
5676 // then we need to allocate a second pointer sized stack slot,
5677 // since we may need to double align that LclVar when we see it
5678 // in the loop below. We will just always do this so that the
5679 // offsets that we calculate for the stack frame will always
5680 // be greater (or equal) to what they can be in the final layout.
5682 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5683 stkOffs -= TARGET_POINTER_SIZE;
5685 else // FINAL_FRAME_LAYOUT
5687 if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
5689 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5690 stkOffs -= TARGET_POINTER_SIZE;
5692 // We should now have a double-aligned (stkOffs+preSpillSize)
5693 noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
5697 if (lvaMonAcquired != BAD_VAR_NUM)
5699 // This var must go first, in what is called the 'frame header' for EnC so that it is
5700 // preserved when remapping occurs. See vm\eetwain.cpp for detailed comment specifying frame
5701 // layout requirements for EnC to work.
5702 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaMonAcquired, lvaLclSize(lvaMonAcquired), stkOffs);
5705 if (opts.compNeedSecurityCheck)
5707 #ifdef JIT32_GCENCODER
5708 /* This can't work without an explicit frame, so make sure */
5709 noway_assert(codeGen->isFramePointerUsed());
5711 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaSecurityObject, TARGET_POINTER_SIZE, stkOffs);
5714 #ifdef JIT32_GCENCODER
5715 if (lvaLocAllocSPvar != BAD_VAR_NUM)
5717 noway_assert(codeGen->isFramePointerUsed()); // else offsets of locals of frameless methods will be incorrect
5718 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaLocAllocSPvar, TARGET_POINTER_SIZE, stkOffs);
5720 #endif // JIT32_GCENCODER
5722 if (lvaReportParamTypeArg())
5724 #ifdef JIT32_GCENCODER
5725 noway_assert(codeGen->isFramePointerUsed());
5727 // For CORINFO_CALLCONV_PARAMTYPE (if needed)
5728 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5729 stkOffs -= TARGET_POINTER_SIZE;
5730 lvaCachedGenericContextArgOffs = stkOffs;
5732 #ifndef JIT32_GCENCODER
5733 else if (lvaKeepAliveAndReportThis())
5735 // When "this" is also used as generic context arg.
5736 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5737 stkOffs -= TARGET_POINTER_SIZE;
5738 lvaCachedGenericContextArgOffs = stkOffs;
5742 #if !FEATURE_EH_FUNCLETS
5743 /* If we need space for slots for shadow SP, reserve it now */
5744 if (ehNeedsShadowSPslots())
5746 noway_assert(codeGen->isFramePointerUsed()); // else offsets of locals of frameless methods will be incorrect
5747 if (!lvaReportParamTypeArg())
5749 #ifndef JIT32_GCENCODER
5750 if (!lvaKeepAliveAndReportThis())
5753 // In order to keep the gc info encoding smaller, the VM assumes that all methods with EH
5754 // have also saved space for a ParamTypeArg, so we need to do that here
5755 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
5756 stkOffs -= TARGET_POINTER_SIZE;
5759 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaShadowSPslotsVar, lvaLclSize(lvaShadowSPslotsVar), stkOffs);
5761 #endif // !FEATURE_EH_FUNCLETS
5763 if (compGSReorderStackLayout)
5765 assert(getNeedsGSSecurityCookie());
5766 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaGSSecurityCookie, lvaLclSize(lvaGSSecurityCookie), stkOffs);
5770 If we're supposed to track lifetimes of pointer temps, we'll
5771 assign frame offsets in the following order:
5773 non-pointer local variables (also untracked pointer variables)
5774 pointer local variables
5781 ALLOC_NON_PTRS = 0x1, // assign offsets to non-ptr
5782 ALLOC_PTRS = 0x2, // Second pass, assign offsets to tracked ptrs
5783 ALLOC_UNSAFE_BUFFERS = 0x4,
5784 ALLOC_UNSAFE_BUFFERS_WITH_PTRS = 0x8
5786 UINT alloc_order[5];
5788 unsigned int cur = 0;
5790 if (compGSReorderStackLayout)
5792 noway_assert(getNeedsGSSecurityCookie());
5794 if (codeGen->isFramePointerUsed())
5796 alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS;
5797 alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS_WITH_PTRS;
5801 bool tempsAllocated = false;
5803 if (lvaTempsHaveLargerOffsetThanVars() && !codeGen->isFramePointerUsed())
5805 // Because we want the temps to have a larger offset than locals
5806 // and we're not using a frame pointer, we have to place the temps
5807 // above the vars. Otherwise we place them after the vars (at the
5808 // bottom of the frame).
5809 noway_assert(!tempsAllocated);
5810 stkOffs = lvaAllocateTemps(stkOffs, mustDoubleAlign);
5811 tempsAllocated = true;
5814 alloc_order[cur++] = ALLOC_NON_PTRS;
5816 if (opts.compDbgEnC)
5818 /* We will use just one pass, and assign offsets to all variables */
5819 alloc_order[cur - 1] |= ALLOC_PTRS;
5820 noway_assert(compGSReorderStackLayout == false);
5824 alloc_order[cur++] = ALLOC_PTRS;
5827 if (!codeGen->isFramePointerUsed() && compGSReorderStackLayout)
5829 alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS_WITH_PTRS;
5830 alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS;
5833 alloc_order[cur] = 0;
5835 noway_assert(cur < _countof(alloc_order));
5837 // Force first pass to happen
5838 UINT assignMore = 0xFFFFFFFF;
5839 bool have_LclVarDoubleAlign = false;
5841 for (cur = 0; alloc_order[cur]; cur++)
5843 if ((assignMore & alloc_order[cur]) == 0)
5853 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
5855 /* Ignore field locals of the promotion type PROMOTION_TYPE_FIELD_DEPENDENT.
5856 In other words, we will not calculate the "base" address of the struct local if
5857 the promotion type is PROMOTION_TYPE_FIELD_DEPENDENT.
5859 if (lvaIsFieldOfDependentlyPromotedStruct(varDsc))
5864 #if FEATURE_FIXED_OUT_ARGS
5865 // The scratch mem is used for the outgoing arguments, and it must be absolutely last
5866 if (lclNum == lvaOutgoingArgSpaceVar)
5872 bool allocateOnFrame = varDsc->lvOnFrame;
5874 if (varDsc->lvRegister && (lvaDoneFrameLayout == REGALLOC_FRAME_LAYOUT) &&
5875 ((varDsc->TypeGet() != TYP_LONG) || (varDsc->lvOtherReg != REG_STK)))
5877 allocateOnFrame = false;
5880 /* Ignore variables that are not on the stack frame */
5882 if (!allocateOnFrame)
5884 /* For EnC, all variables have to be allocated space on the
5885 stack, even though they may actually be enregistered. This
5886 way, the frame layout can be directly inferred from the
5890 if (!opts.compDbgEnC)
5894 else if (lclNum >= info.compLocalsCount)
5895 { // ignore temps for EnC
5899 else if (lvaGSSecurityCookie == lclNum && getNeedsGSSecurityCookie())
5901 continue; // This is allocated outside of this loop.
5904 // These need to be located as the very first variables (highest memory address)
5905 // and so they have already been assigned an offset
5907 #if FEATURE_EH_FUNCLETS
5908 lclNum == lvaPSPSym ||
5910 lclNum == lvaShadowSPslotsVar ||
5911 #endif // FEATURE_EH_FUNCLETS
5912 #ifdef JIT32_GCENCODER
5913 lclNum == lvaLocAllocSPvar ||
5914 #endif // JIT32_GCENCODER
5915 lclNum == lvaSecurityObject)
5917 assert(varDsc->lvStkOffs != BAD_STK_OFFS);
5921 if (lclNum == lvaMonAcquired)
5926 // This should be low on the stack. Hence, it will be assigned later.
5927 if (lclNum == lvaStubArgumentVar)
5929 #ifdef JIT32_GCENCODER
5930 noway_assert(codeGen->isFramePointerUsed());
5935 // This should be low on the stack. Hence, it will be assigned later.
5936 if (lclNum == lvaInlinedPInvokeFrameVar)
5938 noway_assert(codeGen->isFramePointerUsed());
5942 if (varDsc->lvIsParam)
5944 #if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI)
5946 // On Windows AMD64 we can use the caller-reserved stack area that is already setup
5947 assert(varDsc->lvStkOffs != BAD_STK_OFFS);
5950 #else // !_TARGET_AMD64_
5952 // A register argument that is not enregistered ends up as
5953 // a local variable which will need stack frame space.
5955 if (!varDsc->lvIsRegArg)
5960 #ifdef _TARGET_ARM64_
5961 if (info.compIsVarArgs && varDsc->lvArgReg != theFixedRetBuffArgNum())
5963 // Stack offset to varargs (parameters) should point to home area which will be preallocated.
5965 -initialStkOffs + genMapIntRegNumToRegArgNum(varDsc->GetArgReg()) * REGSIZE_BYTES;
5972 // On ARM we spill the registers in codeGen->regSet.rsMaskPreSpillRegArg
5973 // in the prolog, thus they don't need stack frame space.
5975 if ((codeGen->regSet.rsMaskPreSpillRegs(false) & genRegMask(varDsc->lvArgReg)) != 0)
5977 assert(varDsc->lvStkOffs != BAD_STK_OFFS);
5982 #endif // !_TARGET_AMD64_
5985 /* Make sure the type is appropriate */
5987 if (varDsc->lvIsUnsafeBuffer && compGSReorderStackLayout)
5989 if (varDsc->lvIsPtr)
5991 if ((alloc_order[cur] & ALLOC_UNSAFE_BUFFERS_WITH_PTRS) == 0)
5993 assignMore |= ALLOC_UNSAFE_BUFFERS_WITH_PTRS;
5999 if ((alloc_order[cur] & ALLOC_UNSAFE_BUFFERS) == 0)
6001 assignMore |= ALLOC_UNSAFE_BUFFERS;
6006 else if (varTypeIsGC(varDsc->TypeGet()) && varDsc->lvTracked)
6008 if ((alloc_order[cur] & ALLOC_PTRS) == 0)
6010 assignMore |= ALLOC_PTRS;
6016 if ((alloc_order[cur] & ALLOC_NON_PTRS) == 0)
6018 assignMore |= ALLOC_NON_PTRS;
6023 /* Need to align the offset? */
6025 if (mustDoubleAlign && (varDsc->lvType == TYP_DOUBLE // Align doubles for ARM and x86
6027 || varDsc->lvType == TYP_LONG // Align longs for ARM
6029 #ifndef _TARGET_64BIT_
6030 || varDsc->lvStructDoubleAlign // Align when lvStructDoubleAlign is true
6031 #endif // !_TARGET_64BIT_
6034 noway_assert((compLclFrameSize % TARGET_POINTER_SIZE) == 0);
6036 if ((lvaDoneFrameLayout != FINAL_FRAME_LAYOUT) && !have_LclVarDoubleAlign)
6038 // If this is the first TYP_LONG, TYP_DOUBLE or double aligned struct
6039 // then we have seen in this loop then we allocate a pointer sized
6040 // stack slot since we may need to double align this LclVar
6041 // when lvaDoneFrameLayout == FINAL_FRAME_LAYOUT
6043 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6044 stkOffs -= TARGET_POINTER_SIZE;
6048 if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
6050 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6051 stkOffs -= TARGET_POINTER_SIZE;
6054 // We should now have a double-aligned (stkOffs+preSpillSize)
6055 noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
6058 // Remember that we had to double align a LclVar
6059 have_LclVarDoubleAlign = true;
6062 // Reserve the stack space for this variable
6063 stkOffs = lvaAllocLocalAndSetVirtualOffset(lclNum, lvaLclSize(lclNum), stkOffs);
6064 #ifdef _TARGET_ARM64_
6065 // If we have an incoming register argument that has a struct promoted field
6066 // then we need to copy the lvStkOff (the stack home) from the reg arg to the field lclvar
6068 if (varDsc->lvIsRegArg && varDsc->lvPromotedStruct())
6070 noway_assert(varDsc->lvFieldCnt == 1); // We only handle one field here
6072 unsigned fieldVarNum = varDsc->lvFieldLclStart;
6073 lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
6075 #endif // _TARGET_ARM64_
6077 // If we have an incoming register argument that has a promoted long
6078 // then we need to copy the lvStkOff (the stack home) from the reg arg to the field lclvar
6080 if (varDsc->lvIsRegArg && varDsc->lvPromoted)
6082 assert(varTypeIsLong(varDsc) && (varDsc->lvFieldCnt == 2));
6084 unsigned fieldVarNum = varDsc->lvFieldLclStart;
6085 lvaTable[fieldVarNum].lvStkOffs = varDsc->lvStkOffs;
6086 lvaTable[fieldVarNum + 1].lvStkOffs = varDsc->lvStkOffs + 4;
6088 #endif // _TARGET_ARM_
6092 if (getNeedsGSSecurityCookie() && !compGSReorderStackLayout)
6094 // LOCALLOC used, but we have no unsafe buffer. Allocated cookie last, close to localloc buffer.
6095 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaGSSecurityCookie, lvaLclSize(lvaGSSecurityCookie), stkOffs);
6098 if (tempsAllocated == false)
6100 /*-------------------------------------------------------------------------
6104 *-------------------------------------------------------------------------
6106 stkOffs = lvaAllocateTemps(stkOffs, mustDoubleAlign);
6109 /*-------------------------------------------------------------------------
6111 * Now do some final stuff
6113 *-------------------------------------------------------------------------
6116 // lvaInlinedPInvokeFrameVar and lvaStubArgumentVar need to be assigned last
6117 // Important: The stack walker depends on lvaStubArgumentVar immediately
6118 // following lvaInlinedPInvokeFrameVar in the frame.
6120 if (lvaStubArgumentVar != BAD_VAR_NUM)
6122 #ifdef JIT32_GCENCODER
6123 noway_assert(codeGen->isFramePointerUsed());
6125 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaStubArgumentVar, lvaLclSize(lvaStubArgumentVar), stkOffs);
6128 if (lvaInlinedPInvokeFrameVar != BAD_VAR_NUM)
6130 noway_assert(codeGen->isFramePointerUsed());
6132 lvaAllocLocalAndSetVirtualOffset(lvaInlinedPInvokeFrameVar, lvaLclSize(lvaInlinedPInvokeFrameVar), stkOffs);
6135 if (mustDoubleAlign)
6137 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6139 // Allocate a pointer sized stack slot, since we may need to double align here
6140 // when lvaDoneFrameLayout == FINAL_FRAME_LAYOUT
6142 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6143 stkOffs -= TARGET_POINTER_SIZE;
6145 if (have_LclVarDoubleAlign)
6147 // If we have any TYP_LONG, TYP_DOUBLE or double aligned structs
6148 // the we need to allocate a second pointer sized stack slot,
6149 // since we may need to double align the last LclVar that we saw
6150 // in the loop above. We do this so that the offsets that we
6151 // calculate for the stack frame are always greater than they will
6152 // be in the final layout.
6154 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6155 stkOffs -= TARGET_POINTER_SIZE;
6158 else // FINAL_FRAME_LAYOUT
6160 if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
6162 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6163 stkOffs -= TARGET_POINTER_SIZE;
6165 // We should now have a double-aligned (stkOffs+preSpillSize)
6166 noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
6170 #if FEATURE_EH_FUNCLETS && defined(_TARGET_AMD64_)
6171 if (lvaPSPSym != BAD_VAR_NUM)
6173 // On AMD64, if we need a PSPSym, allocate it last, immediately above the outgoing argument
6174 // space. Any padding will be higher on the stack than this
6175 // (including the padding added by lvaAlignFrame()).
6176 noway_assert(codeGen->isFramePointerUsed()); // We need an explicit frame pointer
6177 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaPSPSym, TARGET_POINTER_SIZE, stkOffs);
6179 #endif // FEATURE_EH_FUNCLETS && defined(_TARGET_AMD64_)
6181 #ifdef _TARGET_ARM64_
6182 if (isFramePointerUsed())
6184 // Create space for saving FP and LR.
6185 stkOffs -= 2 * REGSIZE_BYTES;
6187 #endif // _TARGET_ARM64_
6189 #if FEATURE_FIXED_OUT_ARGS
6190 if (lvaOutgoingArgSpaceSize > 0)
6192 #if defined(_TARGET_AMD64_) && !defined(UNIX_AMD64_ABI) // No 4 slots for outgoing params on System V.
6193 noway_assert(lvaOutgoingArgSpaceSize >= (4 * TARGET_POINTER_SIZE));
6195 noway_assert((lvaOutgoingArgSpaceSize % TARGET_POINTER_SIZE) == 0);
6197 // Give it a value so we can avoid asserts in CHK builds.
6198 // Since this will always use an SP relative offset of zero
6199 // at the end of lvaFixVirtualFrameOffsets, it will be set to absolute '0'
6201 stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaOutgoingArgSpaceVar, lvaLclSize(lvaOutgoingArgSpaceVar), stkOffs);
6203 #endif // FEATURE_FIXED_OUT_ARGS
6205 // compLclFrameSize equals our negated virtual stack offset minus the pushed registers and return address
6206 // and the pushed frame pointer register which for some strange reason isn't part of 'compCalleeRegsPushed'.
6207 int pushedCount = compCalleeRegsPushed;
6209 #ifdef _TARGET_ARM64_
6210 if (info.compIsVarArgs)
6212 pushedCount += MAX_REG_ARG;
6216 #ifdef _TARGET_XARCH_
6217 if (codeGen->doubleAlignOrFramePointerUsed())
6219 pushedCount += 1; // pushed EBP (frame pointer)
6221 pushedCount += 1; // pushed PC (return address)
6224 noway_assert(compLclFrameSize == (unsigned)-(stkOffs + (pushedCount * (int)TARGET_POINTER_SIZE)));
6227 int Compiler::lvaAllocLocalAndSetVirtualOffset(unsigned lclNum, unsigned size, int stkOffs)
6229 noway_assert(lclNum != BAD_VAR_NUM);
6231 #ifdef _TARGET_64BIT_
6232 // Before final frame layout, assume the worst case, that every >=8 byte local will need
6233 // maximum padding to be aligned. This is because we generate code based on the stack offset
6234 // computed during tentative frame layout. These offsets cannot get bigger during final
6235 // frame layout, as that would possibly require different code generation (for example,
6236 // using a 4-byte offset instead of a 1-byte offset in an instruction). The offsets can get
6237 // smaller. It is possible there is different alignment at the point locals are allocated
6238 // between tentative and final frame layout which would introduce padding between locals
6239 // and thus increase the offset (from the stack pointer) of one of the locals. Hence the
6240 // need to assume the worst alignment before final frame layout.
6241 // We could probably improve this by sorting all the objects by alignment,
6242 // such that all 8 byte objects are together, 4 byte objects are together, etc., which
6243 // would require at most one alignment padding per group.
6245 // TYP_SIMD structs locals have alignment preference given by getSIMDTypeAlignment() for
6246 // better performance.
6247 if ((size >= 8) && ((lvaDoneFrameLayout != FINAL_FRAME_LAYOUT) || ((stkOffs % 8) != 0)
6248 #if defined(FEATURE_SIMD) && ALIGN_SIMD_TYPES
6249 || lclVarIsSIMDType(lclNum)
6253 // Note that stack offsets are negative or equal to zero
6254 assert(stkOffs <= 0);
6256 // alignment padding
6258 #if defined(FEATURE_SIMD) && ALIGN_SIMD_TYPES
6259 if (lclVarIsSIMDType(lclNum) && !lvaIsImplicitByRefLocal(lclNum))
6261 int alignment = getSIMDTypeAlignment(lvaTable[lclNum].lvType);
6263 if (stkOffs % alignment != 0)
6265 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6267 pad = alignment - 1;
6268 // Note that all the objects will probably be misaligned, but we'll fix that in final layout.
6272 pad = alignment + (stkOffs % alignment); // +1 to +(alignment-1) bytes
6277 #endif // FEATURE_SIMD && ALIGN_SIMD_TYPES
6279 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6282 // Note that all the objects will probably be misaligned, but we'll fix that in final layout.
6286 pad = 8 + (stkOffs % 8); // +1 to +7 bytes
6289 // Will the pad ever be anything except 4? Do we put smaller-than-4-sized objects on the stack?
6290 lvaIncrementFrameSize(pad);
6297 gtDispLclVar(lclNum, /*pad*/ false);
6298 printf(", size=%d, stkOffs=%c0x%x, pad=%d\n", size, stkOffs < 0 ? '-' : '+',
6299 stkOffs < 0 ? -stkOffs : stkOffs, pad);
6303 #endif // _TARGET_64BIT_
6305 /* Reserve space on the stack by bumping the frame size */
6307 lvaIncrementFrameSize(size);
6309 lvaTable[lclNum].lvStkOffs = stkOffs;
6315 gtDispLclVar(lclNum, /*pad*/ false);
6316 printf(", size=%d, stkOffs=%c0x%x\n", size, stkOffs < 0 ? '-' : '+', stkOffs < 0 ? -stkOffs : stkOffs);
6323 #ifdef _TARGET_AMD64_
6324 /*****************************************************************************
6325 * lvaIsCalleeSavedIntRegCountEven() : returns true if the number of integer registers
6326 * pushed onto stack is even including RBP if used as frame pointer
6328 * Note that this excludes return address (PC) pushed by caller. To know whether
6329 * the SP offset after pushing integer registers is aligned, we need to take
6330 * negation of this routine.
6332 bool Compiler::lvaIsCalleeSavedIntRegCountEven()
6334 unsigned regsPushed = compCalleeRegsPushed + (codeGen->isFramePointerUsed() ? 1 : 0);
6335 return (regsPushed % (16 / REGSIZE_BYTES)) == 0;
6337 #endif //_TARGET_AMD64_
6339 /*****************************************************************************
6340 * lvaAlignFrame() : After allocating everything on the frame, reserve any
6341 * extra space needed to keep the frame aligned
6343 void Compiler::lvaAlignFrame()
6345 #if defined(_TARGET_AMD64_)
6347 // Leaf frames do not need full alignment, but the unwind info is smaller if we
6348 // are at least 8 byte aligned (and we assert as much)
6349 if ((compLclFrameSize % 8) != 0)
6351 lvaIncrementFrameSize(8 - (compLclFrameSize % 8));
6353 else if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6355 // If we are not doing final layout, we don't know the exact value of compLclFrameSize
6356 // and thus do not know how much we will need to add in order to be aligned.
6357 // We add 8 so compLclFrameSize is still a multiple of 8.
6358 lvaIncrementFrameSize(8);
6360 assert((compLclFrameSize % 8) == 0);
6362 // Ensure that the stack is always 16-byte aligned by grabbing an unused QWORD
6363 // if needed, but off by 8 because of the return value.
6364 // And don't forget that compCalleeRegsPused does *not* include RBP if we are
6365 // using it as the frame pointer.
6367 bool regPushedCountAligned = lvaIsCalleeSavedIntRegCountEven();
6368 bool lclFrameSizeAligned = (compLclFrameSize % 16) == 0;
6370 // If this isn't the final frame layout, assume we have to push an extra QWORD
6371 // Just so the offsets are true upper limits.
6372 CLANG_FORMAT_COMMENT_ANCHOR;
6374 #ifdef UNIX_AMD64_ABI
6375 // The compNeedToAlignFrame flag is indicating if there is a need to align the frame.
6376 // On AMD64-Windows, if there are calls, 4 slots for the outgoing ars are allocated, except for
6377 // FastTailCall. This slots makes the frame size non-zero, so alignment logic will be called.
6378 // On AMD64-Unix, there are no such slots. There is a possibility to have calls in the method with frame size of 0.
6379 // The frame alignment logic won't kick in. This flags takes care of the AMD64-Unix case by remembering that there
6380 // are calls and making sure the frame alignment logic is executed.
6381 bool stackNeedsAlignment = (compLclFrameSize != 0 || opts.compNeedToAlignFrame);
6382 #else // !UNIX_AMD64_ABI
6383 bool stackNeedsAlignment = compLclFrameSize != 0;
6384 #endif // !UNIX_AMD64_ABI
6385 if ((!codeGen->isFramePointerUsed() && (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)) ||
6386 (stackNeedsAlignment && (regPushedCountAligned == lclFrameSizeAligned)))
6388 lvaIncrementFrameSize(REGSIZE_BYTES);
6391 #elif defined(_TARGET_ARM64_)
6393 // The stack on ARM64 must be 16 byte aligned.
6395 // First, align up to 8.
6396 if ((compLclFrameSize % 8) != 0)
6398 lvaIncrementFrameSize(8 - (compLclFrameSize % 8));
6400 else if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6402 // If we are not doing final layout, we don't know the exact value of compLclFrameSize
6403 // and thus do not know how much we will need to add in order to be aligned.
6404 // We add 8 so compLclFrameSize is still a multiple of 8.
6405 lvaIncrementFrameSize(8);
6407 assert((compLclFrameSize % 8) == 0);
6409 // Ensure that the stack is always 16-byte aligned by grabbing an unused QWORD
6411 bool regPushedCountAligned = (compCalleeRegsPushed % (16 / REGSIZE_BYTES)) == 0;
6412 bool lclFrameSizeAligned = (compLclFrameSize % 16) == 0;
6414 // If this isn't the final frame layout, assume we have to push an extra QWORD
6415 // Just so the offsets are true upper limits.
6416 if ((lvaDoneFrameLayout != FINAL_FRAME_LAYOUT) || (regPushedCountAligned != lclFrameSizeAligned))
6418 lvaIncrementFrameSize(REGSIZE_BYTES);
6421 #elif defined(_TARGET_ARM_)
6423 // Ensure that stack offsets will be double-aligned by grabbing an unused DWORD if needed.
6425 bool lclFrameSizeAligned = (compLclFrameSize % sizeof(double)) == 0;
6426 bool regPushedCountAligned = ((compCalleeRegsPushed + genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true))) %
6427 (sizeof(double) / TARGET_POINTER_SIZE)) == 0;
6429 if (regPushedCountAligned != lclFrameSizeAligned)
6431 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6434 #elif defined(_TARGET_X86_)
6437 if (genDoubleAlign())
6439 // Double Frame Alignement for x86 is handled in Compiler::lvaAssignVirtualFrameOffsetsToLocals()
6441 if (compLclFrameSize == 0)
6443 // This can only happen with JitStress=1 or JitDoubleAlign=2
6444 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6449 if (STACK_ALIGN > REGSIZE_BYTES)
6451 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6453 // If we are not doing final layout, we don't know the exact value of compLclFrameSize
6454 // and thus do not know how much we will need to add in order to be aligned.
6455 // We add the maximum pad that we could ever have (which is 12)
6456 lvaIncrementFrameSize(STACK_ALIGN - REGSIZE_BYTES);
6459 // Align the stack with STACK_ALIGN value.
6460 int adjustFrameSize = compLclFrameSize;
6461 #if defined(UNIX_X86_ABI)
6462 bool isEbpPushed = codeGen->isFramePointerUsed();
6464 isEbpPushed |= genDoubleAlign();
6466 // we need to consider spilled register(s) plus return address and/or EBP
6467 int adjustCount = compCalleeRegsPushed + 1 + (isEbpPushed ? 1 : 0);
6468 adjustFrameSize += (adjustCount * REGSIZE_BYTES) % STACK_ALIGN;
6470 if ((adjustFrameSize % STACK_ALIGN) != 0)
6472 lvaIncrementFrameSize(STACK_ALIGN - (adjustFrameSize % STACK_ALIGN));
6477 NYI("TARGET specific lvaAlignFrame");
6478 #endif // !_TARGET_AMD64_
6481 /*****************************************************************************
6482 * lvaAssignFrameOffsetsToPromotedStructs() : Assign offsets to fields
6483 * within a promoted struct (worker for lvaAssignFrameOffsets).
6485 void Compiler::lvaAssignFrameOffsetsToPromotedStructs()
6487 LclVarDsc* varDsc = lvaTable;
6488 for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++, varDsc++)
6490 // For promoted struct fields that are params, we will
6491 // assign their offsets in lvaAssignVirtualFrameOffsetToArg().
6492 // This is not true for the System V systems since there is no
6493 // outgoing args space. Assign the dependently promoted fields properly.
6495 if (varDsc->lvIsStructField
6496 #ifndef UNIX_AMD64_ABI
6497 #if !defined(_TARGET_ARM_)
6498 // ARM: lo/hi parts of a promoted long arg need to be updated.
6500 // For System V platforms there is no outgoing args space.
6501 // A register passed struct arg is homed on the stack in a separate local var.
6502 // The offset of these structs is already calculated in lvaAssignVirtualFrameOffsetToArg methos.
6503 // Make sure the code below is not executed for these structs and the offset is not changed.
6504 && !varDsc->lvIsParam
6505 #endif // !defined(_TARGET_ARM_)
6506 #endif // !UNIX_AMD64_ABI
6509 LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl];
6510 lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);
6512 if (promotionType == PROMOTION_TYPE_INDEPENDENT)
6514 // The stack offset for these field locals must have been calculated
6515 // by the normal frame offset assignment.
6520 noway_assert(promotionType == PROMOTION_TYPE_DEPENDENT);
6521 noway_assert(varDsc->lvOnFrame);
6522 if (parentvarDsc->lvOnFrame)
6524 varDsc->lvStkOffs = parentvarDsc->lvStkOffs + varDsc->lvFldOffset;
6528 varDsc->lvOnFrame = false;
6529 noway_assert(varDsc->lvRefCnt() == 0);
6536 /*****************************************************************************
6537 * lvaAllocateTemps() : Assign virtual offsets to temps (always negative).
6539 int Compiler::lvaAllocateTemps(int stkOffs, bool mustDoubleAlign)
6541 unsigned spillTempSize = 0;
6543 if (lvaDoneFrameLayout == FINAL_FRAME_LAYOUT)
6545 int preSpillSize = 0;
6547 preSpillSize = genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true)) * TARGET_POINTER_SIZE;
6551 bool assignPtrs = true;
6553 /* Allocate temps */
6555 if (TRACK_GC_TEMP_LIFETIMES)
6557 /* first pointers, then non-pointers in second pass */
6563 /* Pointers and non-pointers together in single pass */
6568 assert(codeGen->regSet.tmpAllFree());
6572 for (TempDsc* temp = codeGen->regSet.tmpListBeg(); temp != nullptr; temp = codeGen->regSet.tmpListNxt(temp))
6574 var_types tempType = temp->tdTempType();
6577 /* Make sure the type is appropriate */
6579 if (!assignPtrs && varTypeIsGC(tempType))
6583 if (!assignNptr && !varTypeIsGC(tempType))
6588 size = temp->tdTempSize();
6590 /* Figure out and record the stack offset of the temp */
6592 /* Need to align the offset? */
6593 CLANG_FORMAT_COMMENT_ANCHOR;
6595 #ifdef _TARGET_64BIT_
6596 if (varTypeIsGC(tempType) && ((stkOffs % TARGET_POINTER_SIZE) != 0))
6598 // Calculate 'pad' as the number of bytes to align up 'stkOffs' to be a multiple of TARGET_POINTER_SIZE
6599 // In practice this is really just a fancy way of writing 4. (as all stack locations are at least 4-byte
6600 // aligned). Note stkOffs is always negative, so (stkOffs % TARGET_POINTER_SIZE) yields a negative
6603 int alignPad = (int)AlignmentPad((unsigned)-stkOffs, TARGET_POINTER_SIZE);
6605 spillTempSize += alignPad;
6606 lvaIncrementFrameSize(alignPad);
6607 stkOffs -= alignPad;
6609 noway_assert((stkOffs % TARGET_POINTER_SIZE) == 0);
6613 if (mustDoubleAlign && (tempType == TYP_DOUBLE)) // Align doubles for x86 and ARM
6615 noway_assert((compLclFrameSize % TARGET_POINTER_SIZE) == 0);
6617 if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
6619 spillTempSize += TARGET_POINTER_SIZE;
6620 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6621 stkOffs -= TARGET_POINTER_SIZE;
6623 // We should now have a double-aligned (stkOffs+preSpillSize)
6624 noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
6627 spillTempSize += size;
6628 lvaIncrementFrameSize(size);
6630 temp->tdSetTempOffs(stkOffs);
6633 // Only required for the ARM platform that we have an accurate estimate for the spillTempSize
6634 noway_assert(spillTempSize <= lvaGetMaxSpillTempSize());
6637 /* If we've only assigned some temps, go back and do the rest now */
6641 assignNptr = !assignNptr;
6642 assignPtrs = !assignPtrs;
6648 else // We haven't run codegen, so there are no Spill temps yet!
6650 unsigned size = lvaGetMaxSpillTempSize();
6652 lvaIncrementFrameSize(size);
6661 /*****************************************************************************
6663 * Dump the register a local is in right now. It is only the current location, since the location changes and it
6664 * is updated throughout code generation based on LSRA register assignments.
6667 void Compiler::lvaDumpRegLocation(unsigned lclNum)
6669 LclVarDsc* varDsc = lvaTable + lclNum;
6672 if (varDsc->TypeGet() == TYP_DOUBLE)
6674 // The assigned registers are `lvRegNum:RegNext(lvRegNum)`
6675 printf("%3s:%-3s ", getRegName(varDsc->lvRegNum), getRegName(REG_NEXT(varDsc->lvRegNum)));
6678 #endif // _TARGET_ARM_
6680 printf("%3s ", getRegName(varDsc->lvRegNum));
6684 /*****************************************************************************
6686 * Dump the frame location assigned to a local.
6687 * It's the home location, even though the variable doesn't always live
6688 * in its home location.
6691 void Compiler::lvaDumpFrameLocation(unsigned lclNum)
6697 offset = lvaFrameAddress(lclNum, compLocallocUsed, &baseReg, 0);
6700 offset = lvaFrameAddress(lclNum, &EBPbased);
6701 baseReg = EBPbased ? REG_FPBASE : REG_SPBASE;
6704 printf("[%2s%1s0x%02X] ", getRegName(baseReg), (offset < 0 ? "-" : "+"), (offset < 0 ? -offset : offset));
6707 /*****************************************************************************
6709 * dump a single lvaTable entry
6712 void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t refCntWtdWidth)
6714 LclVarDsc* varDsc = lvaTable + lclNum;
6715 var_types type = varDsc->TypeGet();
6717 if (curState == INITIAL_FRAME_LAYOUT)
6720 gtDispLclVar(lclNum);
6722 printf(" %7s ", varTypeName(type));
6723 if (genTypeSize(type) == 0)
6725 #if FEATURE_FIXED_OUT_ARGS
6726 if (lclNum == lvaOutgoingArgSpaceVar)
6728 // Since lvaOutgoingArgSpaceSize is a PhasedVar we can't read it for Dumping until
6729 // after we set it to something.
6730 if (lvaOutgoingArgSpaceSize.HasFinalValue())
6732 // A PhasedVar<T> can't be directly used as an arg to a variadic function
6733 unsigned value = lvaOutgoingArgSpaceSize;
6734 printf("(%2d) ", value);
6738 printf("(na) "); // The value hasn't yet been determined
6742 #endif // FEATURE_FIXED_OUT_ARGS
6744 printf("(%2d) ", lvaLclSize(lclNum));
6750 if (varDsc->lvRefCnt() == 0)
6752 // Print this with a special indicator that the variable is unused. Even though the
6753 // variable itself is unused, it might be a struct that is promoted, so seeing it
6754 // can be useful when looking at the promoted struct fields. It's also weird to see
6755 // missing var numbers if these aren't printed.
6758 #if FEATURE_FIXED_OUT_ARGS
6759 // Since lvaOutgoingArgSpaceSize is a PhasedVar we can't read it for Dumping until
6760 // after we set it to something.
6761 else if ((lclNum == lvaOutgoingArgSpaceVar) && lvaOutgoingArgSpaceSize.HasFinalValue() &&
6762 (lvaOutgoingArgSpaceSize == 0))
6764 // Similar to above; print this anyway.
6767 #endif // FEATURE_FIXED_OUT_ARGS
6773 gtDispLclVar(lclNum);
6775 printf("[V%02u", lclNum);
6776 if (varDsc->lvTracked)
6778 printf(",T%02u]", varDsc->lvVarIndex);
6785 printf(" (%3u,%*s)", varDsc->lvRefCnt(), (int)refCntWtdWidth, refCntWtd2str(varDsc->lvRefCntWtd()));
6787 printf(" %7s ", varTypeName(type));
6788 if (genTypeSize(type) == 0)
6790 printf("(%2d) ", lvaLclSize(lclNum));
6797 // The register or stack location field is 11 characters wide.
6798 if (varDsc->lvRefCnt() == 0)
6800 printf("zero-ref ");
6802 else if (varDsc->lvRegister != 0)
6804 // It's always a register, and always in the same register.
6805 lvaDumpRegLocation(lclNum);
6807 else if (varDsc->lvOnFrame == 0)
6809 printf("registers ");
6813 // For RyuJIT backend, it might be in a register part of the time, but it will definitely have a stack home
6814 // location. Otherwise, it's always on the stack.
6815 if (lvaDoneFrameLayout != NO_FRAME_LAYOUT)
6817 lvaDumpFrameLocation(lclNum);
6822 if (varDsc->lvIsHfaRegArg())
6824 if (varDsc->lvHfaTypeIsFloat())
6826 printf(" (enregistered HFA: float) ");
6830 printf(" (enregistered HFA: double)");
6834 if (varDsc->lvDoNotEnregister)
6836 printf(" do-not-enreg[");
6837 if (varDsc->lvAddrExposed)
6841 if (varTypeIsStruct(varDsc))
6845 if (varDsc->lvVMNeedsStackAddr)
6849 if (varDsc->lvLiveInOutOfHndlr)
6853 if (varDsc->lvLclFieldExpr)
6857 if (varDsc->lvLclBlockOpAddr)
6861 if (varDsc->lvLiveAcrossUCall)
6865 if (varDsc->lvIsMultiRegArg)
6869 if (varDsc->lvIsMultiRegRet)
6873 #ifdef JIT32_GCENCODER
6874 if (varDsc->lvPinned)
6876 #endif // JIT32_GCENCODER
6880 if (varDsc->lvIsMultiRegArg)
6882 printf(" multireg-arg");
6884 if (varDsc->lvIsMultiRegRet)
6886 printf(" multireg-ret");
6888 if (varDsc->lvMustInit)
6890 printf(" must-init");
6892 if (varDsc->lvAddrExposed)
6894 printf(" addr-exposed");
6896 if (varDsc->lvHasLdAddrOp)
6898 printf(" ld-addr-op");
6900 if (varDsc->lvVerTypeInfo.IsThisPtr())
6904 if (varDsc->lvPinned)
6908 if (varDsc->lvStackByref)
6910 printf(" stack-byref");
6912 if (varDsc->lvClassHnd != nullptr)
6914 printf(" class-hnd");
6916 if (varDsc->lvClassIsExact)
6920 #ifndef _TARGET_64BIT_
6921 if (varDsc->lvStructDoubleAlign)
6922 printf(" double-align");
6923 #endif // !_TARGET_64BIT_
6924 if (varDsc->lvOverlappingFields)
6926 printf(" overlapping-fields");
6929 if (compGSReorderStackLayout && !varDsc->lvRegister)
6931 if (varDsc->lvIsPtr)
6935 if (varDsc->lvIsUnsafeBuffer)
6937 printf(" unsafe-buffer");
6940 if (varDsc->lvIsStructField)
6942 LclVarDsc* parentvarDsc = &lvaTable[varDsc->lvParentLcl];
6943 #if !defined(_TARGET_64BIT_)
6944 if (varTypeIsLong(parentvarDsc))
6946 bool isLo = (lclNum == parentvarDsc->lvFieldLclStart);
6947 printf(" V%02u.%s(offs=0x%02x)", varDsc->lvParentLcl, isLo ? "lo" : "hi", isLo ? 0 : genTypeSize(TYP_INT));
6950 #endif // !defined(_TARGET_64BIT_)
6952 CORINFO_CLASS_HANDLE typeHnd = parentvarDsc->lvVerTypeInfo.GetClassHandle();
6953 CORINFO_FIELD_HANDLE fldHnd = info.compCompHnd->getFieldInClass(typeHnd, varDsc->lvFldOrdinal);
6955 printf(" V%02u.%s(offs=0x%02x)", varDsc->lvParentLcl, eeGetFieldName(fldHnd), varDsc->lvFldOffset);
6957 lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);
6958 // We should never have lvIsStructField set if it is a reg-sized non-field-addressed struct.
6959 assert(!varDsc->lvRegStruct);
6960 switch (promotionType)
6962 case PROMOTION_TYPE_NONE:
6965 case PROMOTION_TYPE_DEPENDENT:
6968 case PROMOTION_TYPE_INDEPENDENT:
6975 if (varDsc->lvReason != nullptr)
6977 printf(" \"%s\"", varDsc->lvReason);
6983 /*****************************************************************************
6988 void Compiler::lvaTableDump(FrameLayoutState curState)
6990 if (curState == NO_FRAME_LAYOUT)
6992 curState = lvaDoneFrameLayout;
6993 if (curState == NO_FRAME_LAYOUT)
6995 // Still no layout? Could be a bug, but just display the initial layout
6996 curState = INITIAL_FRAME_LAYOUT;
7000 if (curState == INITIAL_FRAME_LAYOUT)
7002 printf("; Initial");
7004 else if (curState == PRE_REGALLOC_FRAME_LAYOUT)
7006 printf("; Pre-RegAlloc");
7008 else if (curState == REGALLOC_FRAME_LAYOUT)
7010 printf("; RegAlloc");
7012 else if (curState == TENTATIVE_FRAME_LAYOUT)
7014 printf("; Tentative");
7016 else if (curState == FINAL_FRAME_LAYOUT)
7022 printf("UNKNOWN FrameLayoutState!");
7026 printf(" local variable assignments\n");
7032 // Figure out some sizes, to help line things up
7034 size_t refCntWtdWidth = 6; // Use 6 as the minimum width
7036 if (curState != INITIAL_FRAME_LAYOUT) // don't need this info for INITIAL_FRAME_LAYOUT
7038 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
7040 size_t width = strlen(refCntWtd2str(varDsc->lvRefCntWtd()));
7041 if (width > refCntWtdWidth)
7043 refCntWtdWidth = width;
7048 // Do the actual output
7050 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
7052 lvaDumpEntry(lclNum, curState, refCntWtdWidth);
7055 //-------------------------------------------------------------------------
7056 // Display the code-gen temps
7058 assert(codeGen->regSet.tmpAllFree());
7059 for (TempDsc* temp = codeGen->regSet.tmpListBeg(); temp != nullptr; temp = codeGen->regSet.tmpListNxt(temp))
7061 printf("; TEMP_%02u %26s%*s%7s -> ", -temp->tdTempNum(), " ", refCntWtdWidth, " ",
7062 varTypeName(temp->tdTempType()));
7063 int offset = temp->tdTempOffs();
7064 printf(" [%2s%1s0x%02X]\n", isFramePointerUsed() ? STR_FPBASE : STR_SPBASE, (offset < 0 ? "-" : "+"),
7065 (offset < 0 ? -offset : offset));
7068 if (curState >= TENTATIVE_FRAME_LAYOUT)
7071 printf("; Lcl frame size = %d\n", compLclFrameSize);
7076 /*****************************************************************************
7078 * Conservatively estimate the layout of the stack frame.
7080 * This function is only used before final frame layout. It conservatively estimates the
7081 * number of callee-saved registers that must be saved, then calls lvaAssignFrameOffsets().
7082 * To do final frame layout, the callee-saved registers are known precisely, so
7083 * lvaAssignFrameOffsets() is called directly.
7085 * Returns the (conservative, that is, overly large) estimated size of the frame,
7086 * including the callee-saved registers. This is only used by the emitter during code
7087 * generation when estimating the size of the offset of instructions accessing temps,
7088 * and only if temps have a larger offset than variables.
7091 unsigned Compiler::lvaFrameSize(FrameLayoutState curState)
7093 assert(curState < FINAL_FRAME_LAYOUT);
7097 /* Layout the stack frame conservatively.
7098 Assume all callee-saved registers are spilled to stack */
7100 compCalleeRegsPushed = CNT_CALLEE_SAVED;
7102 #if defined(_TARGET_ARMARCH_)
7103 if (compFloatingPointUsed)
7104 compCalleeRegsPushed += CNT_CALLEE_SAVED_FLOAT;
7106 compCalleeRegsPushed++; // we always push LR. See genPushCalleeSavedRegisters
7107 #elif defined(_TARGET_AMD64_)
7108 if (compFloatingPointUsed)
7110 compCalleeFPRegsSavedMask = RBM_FLT_CALLEE_SAVED;
7114 compCalleeFPRegsSavedMask = RBM_NONE;
7119 if (genDoubleAlign())
7121 // X86 only - account for extra 4-byte pad that may be created by "and esp, -8" instruction
7122 compCalleeRegsPushed++;
7126 #ifdef _TARGET_XARCH_
7127 // Since FP/EBP is included in the SAVED_REG_MAXSZ we need to
7128 // subtract 1 register if codeGen->isFramePointerUsed() is true.
7129 if (codeGen->isFramePointerUsed())
7131 compCalleeRegsPushed--;
7135 lvaAssignFrameOffsets(curState);
7137 unsigned calleeSavedRegMaxSz = CALLEE_SAVED_REG_MAXSZ;
7138 #if defined(_TARGET_ARMARCH_)
7139 if (compFloatingPointUsed)
7141 calleeSavedRegMaxSz += CALLEE_SAVED_FLOAT_MAXSZ;
7143 calleeSavedRegMaxSz += REGSIZE_BYTES; // we always push LR. See genPushCalleeSavedRegisters
7146 result = compLclFrameSize + calleeSavedRegMaxSz;
7150 //------------------------------------------------------------------------
7151 // lvaGetSPRelativeOffset: Given a variable, return the offset of that
7152 // variable in the frame from the stack pointer. This number will be positive,
7153 // since the stack pointer must be at a lower address than everything on the
7156 // This can't be called for localloc functions, since the stack pointer
7157 // varies, and thus there is no fixed offset to a variable from the stack pointer.
7160 // varNum - the variable number
7165 int Compiler::lvaGetSPRelativeOffset(unsigned varNum)
7167 assert(!compLocallocUsed);
7168 assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7169 assert(varNum < lvaCount);
7170 const LclVarDsc* varDsc = lvaTable + varNum;
7171 assert(varDsc->lvOnFrame);
7172 int spRelativeOffset;
7174 if (varDsc->lvFramePointerBased)
7176 // The stack offset is relative to the frame pointer, so convert it to be
7177 // relative to the stack pointer (which makes no sense for localloc functions).
7178 spRelativeOffset = varDsc->lvStkOffs + codeGen->genSPtoFPdelta();
7182 spRelativeOffset = varDsc->lvStkOffs;
7185 assert(spRelativeOffset >= 0);
7186 return spRelativeOffset;
7189 /*****************************************************************************
7191 * Return the caller-SP-relative stack offset of a local/parameter.
7192 * Requires the local to be on the stack and frame layout to be complete.
7195 int Compiler::lvaGetCallerSPRelativeOffset(unsigned varNum)
7197 assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7198 assert(varNum < lvaCount);
7199 LclVarDsc* varDsc = lvaTable + varNum;
7200 assert(varDsc->lvOnFrame);
7202 return lvaToCallerSPRelativeOffset(varDsc->lvStkOffs, varDsc->lvFramePointerBased);
7205 int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased)
7207 assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7211 offset += codeGen->genCallerSPtoFPdelta();
7215 offset += codeGen->genCallerSPtoInitialSPdelta();
7221 /*****************************************************************************
7223 * Return the Initial-SP-relative stack offset of a local/parameter.
7224 * Requires the local to be on the stack and frame layout to be complete.
7227 int Compiler::lvaGetInitialSPRelativeOffset(unsigned varNum)
7229 assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7230 assert(varNum < lvaCount);
7231 LclVarDsc* varDsc = lvaTable + varNum;
7232 assert(varDsc->lvOnFrame);
7234 return lvaToInitialSPRelativeOffset(varDsc->lvStkOffs, varDsc->lvFramePointerBased);
7237 // Given a local variable offset, and whether that offset is frame-pointer based, return its offset from Initial-SP.
7238 // This is used, for example, to figure out the offset of the frame pointer from Initial-SP.
7239 int Compiler::lvaToInitialSPRelativeOffset(unsigned offset, bool isFpBased)
7241 assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7242 #ifdef _TARGET_AMD64_
7245 // Currently, the frame starts by pushing ebp, ebp points to the saved ebp
7246 // (so we have ebp pointer chaining). Add the fixed-size frame size plus the
7247 // size of the callee-saved regs (not including ebp itself) to find Initial-SP.
7249 assert(codeGen->isFramePointerUsed());
7250 offset += codeGen->genSPtoFPdelta();
7254 // The offset is correct already!
7256 #else // !_TARGET_AMD64_
7257 NYI("lvaToInitialSPRelativeOffset");
7258 #endif // !_TARGET_AMD64_
7263 /*****************************************************************************/
7266 /*****************************************************************************
7267 * Pick a padding size at "random" for the local.
7268 * 0 means that it should not be converted to a GT_LCL_FLD
7271 static unsigned LCL_FLD_PADDING(unsigned lclNum)
7273 // Convert every 2nd variable
7279 // Pick a padding size at "random"
7280 unsigned size = lclNum % 7;
7285 /*****************************************************************************
7287 * Callback for fgWalkAllTreesPre()
7288 * Convert as many GT_LCL_VAR's to GT_LCL_FLD's
7293 The stress mode does 2 passes.
7295 In the first pass we will mark the locals where we CAN't apply the stress mode.
7296 In the second pass we will do the appropiate morphing wherever we've not determined we can't do it.
7298 Compiler::fgWalkResult Compiler::lvaStressLclFldCB(GenTree** pTree, fgWalkData* data)
7300 GenTree* tree = *pTree;
7301 genTreeOps oper = tree->OperGet();
7311 if (tree->gtOp.gtOp1->gtOper != GT_LCL_VAR)
7313 return WALK_CONTINUE;
7315 lcl = tree->gtOp.gtOp1;
7319 return WALK_CONTINUE;
7322 Compiler* pComp = ((lvaStressLclFldArgs*)data->pCallbackData)->m_pCompiler;
7323 bool bFirstPass = ((lvaStressLclFldArgs*)data->pCallbackData)->m_bFirstPass;
7324 noway_assert(lcl->gtOper == GT_LCL_VAR);
7325 unsigned lclNum = lcl->gtLclVarCommon.gtLclNum;
7326 var_types type = lcl->TypeGet();
7327 LclVarDsc* varDsc = &pComp->lvaTable[lclNum];
7329 if (varDsc->lvNoLclFldStress)
7331 // Already determined we can't do anything for this var
7332 return WALK_SKIP_SUBTREES;
7337 // Ignore arguments and temps
7338 if (varDsc->lvIsParam || lclNum >= pComp->info.compLocalsCount)
7340 varDsc->lvNoLclFldStress = true;
7341 return WALK_SKIP_SUBTREES;
7344 // Fix for lcl_fld stress mode
7345 if (varDsc->lvKeepType)
7347 varDsc->lvNoLclFldStress = true;
7348 return WALK_SKIP_SUBTREES;
7351 // Can't have GC ptrs in TYP_BLK.
7352 if (!varTypeIsArithmetic(type))
7354 varDsc->lvNoLclFldStress = true;
7355 return WALK_SKIP_SUBTREES;
7358 // Weed out "small" types like TYP_BYTE as we don't mark the GT_LCL_VAR
7359 // node with the accurate small type. If we bash lvaTable[].lvType,
7360 // then there will be no indication that it was ever a small type.
7361 var_types varType = varDsc->TypeGet();
7362 if (varType != TYP_BLK && genTypeSize(varType) != genTypeSize(genActualType(varType)))
7364 varDsc->lvNoLclFldStress = true;
7365 return WALK_SKIP_SUBTREES;
7368 // Offset some of the local variable by a "random" non-zero amount
7369 unsigned padding = LCL_FLD_PADDING(lclNum);
7372 varDsc->lvNoLclFldStress = true;
7373 return WALK_SKIP_SUBTREES;
7379 noway_assert(varDsc->lvType == lcl->gtType || varDsc->lvType == TYP_BLK);
7380 var_types varType = varDsc->TypeGet();
7382 // Calculate padding
7383 unsigned padding = LCL_FLD_PADDING(lclNum);
7385 #ifdef _TARGET_ARMARCH_
7386 // We need to support alignment requirements to access memory on ARM ARCH
7387 unsigned alignment = 1;
7388 pComp->codeGen->InferOpSizeAlign(lcl, &alignment);
7389 alignment = roundUp(alignment, TARGET_POINTER_SIZE);
7390 padding = roundUp(padding, alignment);
7391 #endif // _TARGET_ARMARCH_
7393 // Change the variable to a TYP_BLK
7394 if (varType != TYP_BLK)
7396 varDsc->lvExactSize = roundUp(padding + pComp->lvaLclSize(lclNum), TARGET_POINTER_SIZE);
7397 varDsc->lvType = TYP_BLK;
7398 pComp->lvaSetVarAddrExposed(lclNum);
7401 tree->gtFlags |= GTF_GLOB_REF;
7403 /* Now morph the tree appropriately */
7404 if (oper == GT_LCL_VAR)
7406 /* Change lclVar(lclNum) to lclFld(lclNum,padding) */
7408 tree->ChangeOper(GT_LCL_FLD);
7409 tree->gtLclFld.gtLclOffs = padding;
7413 /* Change addr(lclVar) to addr(lclVar)+padding */
7415 noway_assert(oper == GT_ADDR);
7416 GenTree* paddingTree = pComp->gtNewIconNode(padding);
7417 GenTree* newAddr = pComp->gtNewOperNode(GT_ADD, tree->gtType, tree, paddingTree);
7421 lcl->gtType = TYP_BLK;
7425 return WALK_SKIP_SUBTREES;
7428 /*****************************************************************************/
7430 void Compiler::lvaStressLclFld()
7432 if (!compStressCompile(STRESS_LCL_FLDS, 5))
7437 lvaStressLclFldArgs Args;
7438 Args.m_pCompiler = this;
7439 Args.m_bFirstPass = true;
7442 fgWalkAllTreesPre(lvaStressLclFldCB, &Args);
7445 Args.m_bFirstPass = false;
7446 fgWalkAllTreesPre(lvaStressLclFldCB, &Args);
7451 /*****************************************************************************
7453 * A little routine that displays a local variable bitset.
7454 * 'set' is mask of variables that have to be displayed
7455 * 'allVars' is the complete set of interesting variables (blank space is
7456 * inserted if its corresponding bit is not in 'set').
7460 void Compiler::lvaDispVarSet(VARSET_VALARG_TP set)
7462 VARSET_TP allVars(VarSetOps::MakeEmpty(this));
7463 lvaDispVarSet(set, allVars);
7466 void Compiler::lvaDispVarSet(VARSET_VALARG_TP set, VARSET_VALARG_TP allVars)
7470 bool needSpace = false;
7472 for (unsigned index = 0; index < lvaTrackedCount; index++)
7474 if (VarSetOps::IsMember(this, set, index))
7479 /* Look for the matching variable */
7481 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
7483 if ((varDsc->lvVarIndex == index) && varDsc->lvTracked)
7498 printf("V%02u", lclNum);
7500 else if (VarSetOps::IsMember(this, allVars, index))