Sync may31 release/8.0-tizen (#510)
[platform/upstream/dotnet/runtime.git] / src / coreclr / jit / lclvars.cpp
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
4 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XX                                                                           XX
7 XX                           LclVarsInfo                                     XX
8 XX                                                                           XX
9 XX   The variables to be used by the code generator.                         XX
10 XX                                                                           XX
11 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
13 */
14
15 #include "jitpch.h"
16 #ifdef _MSC_VER
17 #pragma hdrstop
18 #endif
19
20 #include "emit.h"
21 #include "registerargconvention.h"
22 #include "jitstd/algorithm.h"
23 #include "patchpointinfo.h"
24
25 /*****************************************************************************/
26
27 #ifdef DEBUG
28 #if DOUBLE_ALIGN
29 /* static */
30 unsigned Compiler::s_lvaDoubleAlignedProcsCount = 0;
31 #endif
32 #endif
33
34 /*****************************************************************************/
35
36 void Compiler::lvaInit()
37 {
38     /* We haven't allocated stack variables yet */
39     lvaRefCountState = RCS_INVALID;
40
41     lvaGenericsContextInUse = false;
42
43     lvaTrackedToVarNumSize = 0;
44     lvaTrackedToVarNum     = nullptr;
45
46     lvaTrackedFixed = false; // false: We can still add new tracked variables
47
48     lvaDoneFrameLayout = NO_FRAME_LAYOUT;
49 #if !defined(FEATURE_EH_FUNCLETS)
50     lvaShadowSPslotsVar = BAD_VAR_NUM;
51 #endif // !FEATURE_EH_FUNCLETS
52     lvaInlinedPInvokeFrameVar = BAD_VAR_NUM;
53     lvaReversePInvokeFrameVar = BAD_VAR_NUM;
54 #if FEATURE_FIXED_OUT_ARGS
55     lvaOutgoingArgSpaceVar  = BAD_VAR_NUM;
56     lvaOutgoingArgSpaceSize = PhasedVar<unsigned>();
57 #endif // FEATURE_FIXED_OUT_ARGS
58 #ifdef JIT32_GCENCODER
59     lvaLocAllocSPvar = BAD_VAR_NUM;
60 #endif // JIT32_GCENCODER
61     lvaNewObjArrayArgs  = BAD_VAR_NUM;
62     lvaGSSecurityCookie = BAD_VAR_NUM;
63 #ifdef TARGET_X86
64     lvaVarargsBaseOfStkArgs = BAD_VAR_NUM;
65 #endif // TARGET_X86
66     lvaVarargsHandleArg = BAD_VAR_NUM;
67     lvaStubArgumentVar  = BAD_VAR_NUM;
68     lvaArg0Var          = BAD_VAR_NUM;
69     lvaMonAcquired      = BAD_VAR_NUM;
70     lvaRetAddrVar       = BAD_VAR_NUM;
71
72     lvaInlineeReturnSpillTemp = BAD_VAR_NUM;
73
74     gsShadowVarInfo = nullptr;
75 #if defined(FEATURE_EH_FUNCLETS)
76     lvaPSPSym = BAD_VAR_NUM;
77 #endif
78 #if FEATURE_SIMD
79     lvaSIMDInitTempVarNum = BAD_VAR_NUM;
80 #endif // FEATURE_SIMD
81     lvaCurEpoch = 0;
82
83 #if defined(DEBUG) && defined(TARGET_XARCH)
84     lvaReturnSpCheck = BAD_VAR_NUM;
85 #endif
86
87 #if defined(DEBUG) && defined(TARGET_X86)
88     lvaCallSpCheck = BAD_VAR_NUM;
89 #endif
90
91     structPromotionHelper = new (this, CMK_Generic) StructPromotionHelper(this);
92 }
93
94 /*****************************************************************************/
95
96 void Compiler::lvaInitTypeRef()
97 {
98
99     /* x86 args look something like this:
100         [this ptr] [hidden return buffer] [declared arguments]* [generic context] [var arg cookie]
101
102        x64 is closer to the native ABI:
103         [this ptr] [hidden return buffer] [generic context] [var arg cookie] [declared arguments]*
104         (Note: prior to .NET Framework 4.5.1 for Windows 8.1 (but not .NET Framework 4.5.1 "downlevel"),
105         the "hidden return buffer" came before the "this ptr". Now, the "this ptr" comes first. This
106         is different from the C++ order, where the "hidden return buffer" always comes first.)
107
108        ARM and ARM64 are the same as the current x64 convention:
109         [this ptr] [hidden return buffer] [generic context] [var arg cookie] [declared arguments]*
110
111        Key difference:
112            The var arg cookie and generic context are swapped with respect to the user arguments
113     */
114
115     /* Set compArgsCount and compLocalsCount */
116
117     info.compArgsCount = info.compMethodInfo->args.numArgs;
118
119     // Is there a 'this' pointer
120
121     if (!info.compIsStatic)
122     {
123         info.compArgsCount++;
124     }
125     else
126     {
127         info.compThisArg = BAD_VAR_NUM;
128     }
129
130     info.compILargsCount = info.compArgsCount;
131
132     // Initialize "compRetNativeType" (along with "compRetTypeDesc"):
133     //
134     //  1. For structs returned via a return buffer, or in multiple registers, make it TYP_STRUCT.
135     //  2. For structs returned in a single register, make it the corresponding primitive type.
136     //  3. For primitives, leave it as-is. Note this makes it "incorrect" for soft-FP conventions.
137     //
138     ReturnTypeDesc retTypeDesc;
139     retTypeDesc.InitializeReturnType(this, info.compRetType, info.compMethodInfo->args.retTypeClass, info.compCallConv);
140
141     compRetTypeDesc         = retTypeDesc;
142     unsigned returnRegCount = retTypeDesc.GetReturnRegCount();
143     bool     hasRetBuffArg  = false;
144     if (returnRegCount > 1)
145     {
146         info.compRetNativeType = varTypeIsMultiReg(info.compRetType) ? info.compRetType : TYP_STRUCT;
147     }
148     else if (returnRegCount == 1)
149     {
150         info.compRetNativeType = retTypeDesc.GetReturnRegType(0);
151     }
152     else
153     {
154         hasRetBuffArg          = info.compRetType != TYP_VOID;
155         info.compRetNativeType = hasRetBuffArg ? TYP_STRUCT : TYP_VOID;
156     }
157
158     // Do we have a RetBuffArg?
159     if (hasRetBuffArg)
160     {
161         info.compArgsCount++;
162     }
163     else
164     {
165         info.compRetBuffArg = BAD_VAR_NUM;
166     }
167
168     /* There is a 'hidden' cookie pushed last when the
169        calling convention is varargs */
170
171     if (info.compIsVarArgs)
172     {
173         info.compArgsCount++;
174     }
175
176     // Is there an extra parameter used to pass instantiation info to
177     // shared generic methods and shared generic struct instance methods?
178     if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
179     {
180         info.compArgsCount++;
181     }
182     else
183     {
184         info.compTypeCtxtArg = BAD_VAR_NUM;
185     }
186
187     lvaCount = info.compLocalsCount = info.compArgsCount + info.compMethodInfo->locals.numArgs;
188
189     info.compILlocalsCount = info.compILargsCount + info.compMethodInfo->locals.numArgs;
190
191     /* Now allocate the variable descriptor table */
192
193     if (compIsForInlining())
194     {
195         lvaTable    = impInlineInfo->InlinerCompiler->lvaTable;
196         lvaCount    = impInlineInfo->InlinerCompiler->lvaCount;
197         lvaTableCnt = impInlineInfo->InlinerCompiler->lvaTableCnt;
198
199         // No more stuff needs to be done.
200         return;
201     }
202
203     lvaTableCnt = lvaCount * 2;
204
205     if (lvaTableCnt < 16)
206     {
207         lvaTableCnt = 16;
208     }
209
210     lvaTable         = getAllocator(CMK_LvaTable).allocate<LclVarDsc>(lvaTableCnt);
211     size_t tableSize = lvaTableCnt * sizeof(*lvaTable);
212     memset((void*)lvaTable, 0, tableSize);
213     for (unsigned i = 0; i < lvaTableCnt; i++)
214     {
215         new (&lvaTable[i], jitstd::placement_t()) LclVarDsc(); // call the constructor.
216     }
217
218     //-------------------------------------------------------------------------
219     // Count the arguments and initialize the respective lvaTable[] entries
220     //
221     // First the implicit arguments
222     //-------------------------------------------------------------------------
223
224     InitVarDscInfo varDscInfo;
225 #ifdef TARGET_X86
226     // x86 unmanaged calling conventions limit the number of registers supported
227     // for accepting arguments. As a result, we need to modify the number of registers
228     // when we emit a method with an unmanaged calling convention.
229     switch (info.compCallConv)
230     {
231         case CorInfoCallConvExtension::Thiscall:
232             // In thiscall the this parameter goes into a register.
233             varDscInfo.Init(lvaTable, hasRetBuffArg, 1, 0);
234             break;
235         case CorInfoCallConvExtension::C:
236         case CorInfoCallConvExtension::Stdcall:
237         case CorInfoCallConvExtension::CMemberFunction:
238         case CorInfoCallConvExtension::StdcallMemberFunction:
239             varDscInfo.Init(lvaTable, hasRetBuffArg, 0, 0);
240             break;
241         case CorInfoCallConvExtension::Managed:
242         case CorInfoCallConvExtension::Fastcall:
243         case CorInfoCallConvExtension::FastcallMemberFunction:
244         default:
245             varDscInfo.Init(lvaTable, hasRetBuffArg, MAX_REG_ARG, MAX_FLOAT_REG_ARG);
246             break;
247     }
248 #else
249     varDscInfo.Init(lvaTable, hasRetBuffArg, MAX_REG_ARG, MAX_FLOAT_REG_ARG);
250 #endif
251
252     lvaInitArgs(&varDscInfo);
253
254     //-------------------------------------------------------------------------
255     // Finally the local variables
256     //-------------------------------------------------------------------------
257
258     unsigned                varNum    = varDscInfo.varNum;
259     LclVarDsc*              varDsc    = varDscInfo.varDsc;
260     CORINFO_ARG_LIST_HANDLE localsSig = info.compMethodInfo->locals.args;
261
262 #if defined(TARGET_ARM) || defined(TARGET_RISCV64)
263     compHasSplitParam = varDscInfo.hasSplitParam;
264 #endif // TARGET_ARM || TARGET_RISCV64
265
266     for (unsigned i = 0; i < info.compMethodInfo->locals.numArgs;
267          i++, varNum++, varDsc++, localsSig = info.compCompHnd->getArgNext(localsSig))
268     {
269         CORINFO_CLASS_HANDLE typeHnd;
270         CorInfoTypeWithMod   corInfoTypeWithMod =
271             info.compCompHnd->getArgType(&info.compMethodInfo->locals, localsSig, &typeHnd);
272         CorInfoType corInfoType = strip(corInfoTypeWithMod);
273
274         lvaInitVarDsc(varDsc, varNum, corInfoType, typeHnd, localsSig, &info.compMethodInfo->locals);
275
276         if ((corInfoTypeWithMod & CORINFO_TYPE_MOD_PINNED) != 0)
277         {
278             if ((corInfoType == CORINFO_TYPE_CLASS) || (corInfoType == CORINFO_TYPE_BYREF))
279             {
280                 JITDUMP("Setting lvPinned for V%02u\n", varNum);
281                 varDsc->lvPinned = 1;
282
283                 if (opts.IsOSR())
284                 {
285                     // OSR method may not see any references to the pinned local,
286                     // but must still report it in GC info.
287                     //
288                     varDsc->lvImplicitlyReferenced = 1;
289                 }
290             }
291             else
292             {
293                 JITDUMP("Ignoring pin for non-GC type V%02u\n", varNum);
294             }
295         }
296
297         varDsc->lvOnFrame = true; // The final home for this local variable might be our local stack frame
298
299         if (corInfoType == CORINFO_TYPE_CLASS)
300         {
301             CORINFO_CLASS_HANDLE clsHnd = info.compCompHnd->getArgClass(&info.compMethodInfo->locals, localsSig);
302             lvaSetClass(varNum, clsHnd);
303         }
304     }
305
306     if ( // If there already exist unsafe buffers, don't mark more structs as unsafe
307         // as that will cause them to be placed along with the real unsafe buffers,
308         // unnecessarily exposing them to overruns. This can affect GS tests which
309         // intentionally do buffer-overruns.
310         !getNeedsGSSecurityCookie() &&
311         // GS checks require the stack to be re-ordered, which can't be done with EnC
312         !opts.compDbgEnC && compStressCompile(STRESS_UNSAFE_BUFFER_CHECKS, 25))
313     {
314         setNeedsGSSecurityCookie();
315         compGSReorderStackLayout = true;
316
317         for (unsigned i = 0; i < lvaCount; i++)
318         {
319             if ((lvaTable[i].lvType == TYP_STRUCT) && compStressCompile(STRESS_GENERIC_VARN, 60))
320             {
321                 lvaTable[i].lvIsUnsafeBuffer = true;
322             }
323         }
324     }
325
326     // If this is an OSR method, mark all the OSR locals.
327     //
328     // Do this before we add the GS Cookie Dummy or Outgoing args to the locals
329     // so we don't have to do special checks to exclude them.
330     //
331     if (opts.IsOSR())
332     {
333         for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++)
334         {
335             LclVarDsc* const varDsc = lvaGetDesc(lclNum);
336             varDsc->lvIsOSRLocal    = true;
337
338             if (info.compPatchpointInfo->IsExposed(lclNum))
339             {
340                 JITDUMP("-- V%02u is OSR exposed\n", lclNum);
341                 varDsc->lvIsOSRExposedLocal = true;
342
343                 // Ensure that ref counts for exposed OSR locals take into account
344                 // that some of the refs might be in the Tier0 parts of the method
345                 // that get trimmed away.
346                 //
347                 varDsc->lvImplicitlyReferenced = 1;
348             }
349         }
350     }
351
352     if (getNeedsGSSecurityCookie())
353     {
354         // Ensure that there will be at least one stack variable since
355         // we require that the GSCookie does not have a 0 stack offset.
356         unsigned   dummy         = lvaGrabTempWithImplicitUse(false DEBUGARG("GSCookie dummy"));
357         LclVarDsc* gsCookieDummy = lvaGetDesc(dummy);
358         gsCookieDummy->lvType    = TYP_INT;
359         gsCookieDummy->lvIsTemp  = true; // It is not alive at all, set the flag to prevent zero-init.
360         lvaSetVarDoNotEnregister(dummy DEBUGARG(DoNotEnregisterReason::VMNeedsStackAddr));
361     }
362
363     // Allocate the lvaOutgoingArgSpaceVar now because we can run into problems in the
364     // emitter when the varNum is greater that 32767 (see emitLclVarAddr::initLclVarAddr)
365     lvaAllocOutgoingArgSpaceVar();
366
367 #ifdef DEBUG
368     if (verbose)
369     {
370         lvaTableDump(INITIAL_FRAME_LAYOUT);
371     }
372 #endif
373 }
374
375 /*****************************************************************************/
376 void Compiler::lvaInitArgs(InitVarDscInfo* varDscInfo)
377 {
378     compArgSize = 0;
379
380 #if defined(TARGET_ARM) && defined(PROFILING_SUPPORTED)
381     // Prespill all argument regs on to stack in case of Arm when under profiler.
382     if (compIsProfilerHookNeeded())
383     {
384         codeGen->regSet.rsMaskPreSpillRegArg |= RBM_ARG_REGS;
385     }
386 #endif
387
388     //----------------------------------------------------------------------
389
390     /* Is there a "this" pointer ? */
391     lvaInitThisPtr(varDscInfo);
392
393     unsigned numUserArgsToSkip = 0;
394     unsigned numUserArgs       = info.compMethodInfo->args.numArgs;
395 #if !defined(TARGET_ARM)
396     if (TargetOS::IsWindows && callConvIsInstanceMethodCallConv(info.compCallConv))
397     {
398         // If we are a native instance method, handle the first user arg
399         // (the unmanaged this parameter) and then handle the hidden
400         // return buffer parameter.
401         assert(numUserArgs >= 1);
402         lvaInitUserArgs(varDscInfo, 0, 1);
403         numUserArgsToSkip++;
404         numUserArgs--;
405
406         lvaInitRetBuffArg(varDscInfo, false);
407     }
408     else
409 #endif
410     {
411         /* If we have a hidden return-buffer parameter, that comes here */
412         lvaInitRetBuffArg(varDscInfo, true);
413     }
414
415 //======================================================================
416
417 #if USER_ARGS_COME_LAST
418     //@GENERICS: final instantiation-info argument for shared generic methods
419     // and shared generic struct instance methods
420     lvaInitGenericsCtxt(varDscInfo);
421
422     /* If the method is varargs, process the varargs cookie */
423     lvaInitVarArgsHandle(varDscInfo);
424 #endif
425
426     //-------------------------------------------------------------------------
427     // Now walk the function signature for the explicit user arguments
428     //-------------------------------------------------------------------------
429     lvaInitUserArgs(varDscInfo, numUserArgsToSkip, numUserArgs);
430 #if !USER_ARGS_COME_LAST
431     //@GENERICS: final instantiation-info argument for shared generic methods
432     // and shared generic struct instance methods
433     lvaInitGenericsCtxt(varDscInfo);
434
435     /* If the method is varargs, process the varargs cookie */
436     lvaInitVarArgsHandle(varDscInfo);
437 #endif
438
439     //----------------------------------------------------------------------
440
441     // We have set info.compArgsCount in compCompile()
442     noway_assert(varDscInfo->varNum == info.compArgsCount);
443     assert(varDscInfo->intRegArgNum <= MAX_REG_ARG);
444
445     codeGen->intRegState.rsCalleeRegArgCount   = varDscInfo->intRegArgNum;
446     codeGen->floatRegState.rsCalleeRegArgCount = varDscInfo->floatRegArgNum;
447
448 #if FEATURE_FASTTAILCALL
449     // Save the stack usage information
450     // We can get register usage information using codeGen->intRegState and
451     // codeGen->floatRegState
452     info.compArgStackSize = varDscInfo->stackArgSize;
453 #endif // FEATURE_FASTTAILCALL
454
455     // The total argument size must be aligned.
456     noway_assert((compArgSize % TARGET_POINTER_SIZE) == 0);
457
458 #ifdef TARGET_X86
459     /* We can not pass more than 2^16 dwords as arguments as the "ret"
460        instruction can only pop 2^16 arguments. Could be handled correctly
461        but it will be very difficult for fully interruptible code */
462
463     if (compArgSize != (size_t)(unsigned short)compArgSize)
464         IMPL_LIMITATION("Too many arguments for the \"ret\" instruction to pop");
465 #endif
466 }
467
468 /*****************************************************************************/
469 void Compiler::lvaInitThisPtr(InitVarDscInfo* varDscInfo)
470 {
471     LclVarDsc* varDsc = varDscInfo->varDsc;
472     if (!info.compIsStatic)
473     {
474         varDsc->lvIsParam = 1;
475         varDsc->lvIsPtr   = 1;
476
477         lvaArg0Var = info.compThisArg = varDscInfo->varNum;
478         noway_assert(info.compThisArg == 0);
479
480         if (eeIsValueClass(info.compClassHnd))
481         {
482             varDsc->lvType = TYP_BYREF;
483         }
484         else
485         {
486             varDsc->lvType = TYP_REF;
487             lvaSetClass(varDscInfo->varNum, info.compClassHnd);
488         }
489
490         varDsc->lvIsRegArg = 1;
491         noway_assert(varDscInfo->intRegArgNum == 0);
492
493         varDsc->SetArgReg(genMapRegArgNumToRegNum(varDscInfo->allocRegArg(TYP_INT), varDsc->TypeGet()));
494 #if FEATURE_MULTIREG_ARGS
495         varDsc->SetOtherArgReg(REG_NA);
496 #endif
497         varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
498
499 #ifdef DEBUG
500         if (verbose)
501         {
502             printf("'this'    passed in register %s\n", getRegName(varDsc->GetArgReg()));
503         }
504 #endif
505         compArgSize += TARGET_POINTER_SIZE;
506
507         varDscInfo->varNum++;
508         varDscInfo->varDsc++;
509     }
510 }
511
512 /*****************************************************************************/
513 void Compiler::lvaInitRetBuffArg(InitVarDscInfo* varDscInfo, bool useFixedRetBufReg)
514 {
515     if (varDscInfo->hasRetBufArg)
516     {
517         info.compRetBuffArg = varDscInfo->varNum;
518
519         LclVarDsc* varDsc  = varDscInfo->varDsc;
520         varDsc->lvType     = TYP_BYREF;
521         varDsc->lvIsParam  = 1;
522         varDsc->lvIsRegArg = 0;
523
524         if (useFixedRetBufReg && hasFixedRetBuffReg())
525         {
526             varDsc->lvIsRegArg = 1;
527             varDsc->SetArgReg(theFixedRetBuffReg());
528         }
529         else if (varDscInfo->canEnreg(TYP_INT))
530         {
531             varDsc->lvIsRegArg     = 1;
532             unsigned retBuffArgNum = varDscInfo->allocRegArg(TYP_INT);
533             varDsc->SetArgReg(genMapIntRegArgNumToRegNum(retBuffArgNum));
534         }
535
536 #if FEATURE_MULTIREG_ARGS
537         varDsc->SetOtherArgReg(REG_NA);
538 #endif
539         varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
540
541         assert(!varDsc->lvIsRegArg || isValidIntArgReg(varDsc->GetArgReg()));
542
543 #ifdef DEBUG
544         if (varDsc->lvIsRegArg && verbose)
545         {
546             printf("'__retBuf'  passed in register %s\n", getRegName(varDsc->GetArgReg()));
547         }
548 #endif
549
550         /* Update the total argument size, count and varDsc */
551
552         compArgSize += TARGET_POINTER_SIZE;
553         varDscInfo->varNum++;
554         varDscInfo->varDsc++;
555     }
556 }
557
558 //-----------------------------------------------------------------------------
559 // lvaInitUserArgs:
560 //     Initialize local var descriptions for incoming user arguments
561 //
562 // Arguments:
563 //    varDscInfo     - the local var descriptions
564 //    skipArgs       - the number of user args to skip processing.
565 //    takeArgs       - the number of user args to process (after skipping skipArgs number of args)
566 //
567 void Compiler::lvaInitUserArgs(InitVarDscInfo* varDscInfo, unsigned skipArgs, unsigned takeArgs)
568 {
569 //-------------------------------------------------------------------------
570 // Walk the function signature for the explicit arguments
571 //-------------------------------------------------------------------------
572
573 #if defined(TARGET_X86)
574     // Only (some of) the implicit args are enregistered for varargs
575     if (info.compIsVarArgs)
576     {
577         varDscInfo->maxIntRegArgNum = varDscInfo->intRegArgNum;
578     }
579 #elif defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)
580     // On System V type environment the float registers are not indexed together with the int ones.
581     varDscInfo->floatRegArgNum = varDscInfo->intRegArgNum;
582 #endif // TARGET*
583
584     CORINFO_ARG_LIST_HANDLE argLst = info.compMethodInfo->args.args;
585
586     const unsigned argSigLen = info.compMethodInfo->args.numArgs;
587
588     // We will process at most takeArgs arguments from the signature after skipping skipArgs arguments
589     const int64_t numUserArgs = min(takeArgs, (argSigLen - (int64_t)skipArgs));
590
591     // If there are no user args or less than skipArgs args, return here since there's no work to do.
592     if (numUserArgs <= 0)
593     {
594         return;
595     }
596
597 #ifdef TARGET_ARM
598     regMaskTP doubleAlignMask = RBM_NONE;
599 #endif // TARGET_ARM
600
601     // Skip skipArgs arguments from the signature.
602     for (unsigned i = 0; i < skipArgs; i++, argLst = info.compCompHnd->getArgNext(argLst))
603     {
604         ;
605     }
606
607     // Process each user arg.
608     for (unsigned i = 0; i < numUserArgs;
609          i++, varDscInfo->varNum++, varDscInfo->varDsc++, argLst = info.compCompHnd->getArgNext(argLst))
610     {
611         LclVarDsc*           varDsc  = varDscInfo->varDsc;
612         CORINFO_CLASS_HANDLE typeHnd = nullptr;
613
614         CorInfoTypeWithMod corInfoType = info.compCompHnd->getArgType(&info.compMethodInfo->args, argLst, &typeHnd);
615         varDsc->lvIsParam              = 1;
616
617         lvaInitVarDsc(varDsc, varDscInfo->varNum, strip(corInfoType), typeHnd, argLst, &info.compMethodInfo->args);
618
619         if (strip(corInfoType) == CORINFO_TYPE_CLASS)
620         {
621             CORINFO_CLASS_HANDLE clsHnd = info.compCompHnd->getArgClass(&info.compMethodInfo->args, argLst);
622             lvaSetClass(varDscInfo->varNum, clsHnd);
623         }
624
625         // For ARM, ARM64, LOONGARCH64, RISCV64 and AMD64 varargs, all arguments go in integer registers
626         var_types argType = mangleVarArgsType(varDsc->TypeGet());
627
628         var_types origArgType = argType;
629
630         // ARM softfp calling convention should affect only the floating point arguments.
631         // Otherwise there appear too many surplus pre-spills and other memory operations
632         // with the associated locations .
633         bool     isSoftFPPreSpill = opts.compUseSoftFP && varTypeIsFloating(varDsc->TypeGet());
634         unsigned argSize          = eeGetArgSize(argLst, &info.compMethodInfo->args);
635         unsigned cSlots =
636             (argSize + TARGET_POINTER_SIZE - 1) / TARGET_POINTER_SIZE; // the total number of slots of this argument
637         bool      isHfaArg = false;
638         var_types hfaType  = TYP_UNDEF;
639
640         // Methods that use VarArg or SoftFP cannot have HFA arguments except
641         // Native varargs on arm64 unix use the regular calling convention.
642         if (((TargetOS::IsUnix && TargetArchitecture::IsArm64) || !info.compIsVarArgs) && !opts.compUseSoftFP)
643         {
644             // If the argType is a struct, then check if it is an HFA
645             if (varTypeIsStruct(argType))
646             {
647                 // hfaType is set to float, double, or SIMD type if it is an HFA, otherwise TYP_UNDEF
648                 hfaType  = GetHfaType(typeHnd);
649                 isHfaArg = varTypeIsValidHfaType(hfaType);
650             }
651         }
652         else if (info.compIsVarArgs)
653         {
654             // Currently native varargs is not implemented on non windows targets.
655             //
656             // Note that some targets like Arm64 Unix should not need much work as
657             // the ABI is the same. While other targets may only need small changes
658             // such as amd64 Unix, which just expects RAX to pass numFPArguments.
659             if (TargetOS::IsUnix)
660             {
661                 NYI("InitUserArgs for Vararg callee is not yet implemented on non Windows targets.");
662             }
663         }
664
665         if (isHfaArg)
666         {
667             // We have an HFA argument, so from here on out treat the type as a float, double, or vector.
668             // The original struct type is available by using origArgType.
669             // We also update the cSlots to be the number of float/double/vector fields in the HFA.
670             argType = hfaType; // TODO-Cleanup: remove this assignment and mark `argType` as const.
671             varDsc->SetHfaType(hfaType);
672             cSlots = varDsc->lvHfaSlots();
673         }
674         // The number of slots that must be enregistered if we are to consider this argument enregistered.
675         // This is normally the same as cSlots, since we normally either enregister the entire object,
676         // or none of it. For structs on ARM, however, we only need to enregister a single slot to consider
677         // it enregistered, as long as we can split the rest onto the stack.
678         unsigned cSlotsToEnregister = cSlots;
679
680 #if defined(TARGET_ARM64)
681
682         if (compFeatureArgSplit())
683         {
684             // On arm64 Windows we will need to properly handle the case where a >8byte <=16byte
685             // struct is split between register r7 and virtual stack slot s[0]
686             // We will only do this for calls to vararg methods on Windows Arm64
687             //
688             // !!This does not affect the normal arm64 calling convention or Unix Arm64!!
689             if (this->info.compIsVarArgs && argType == TYP_STRUCT)
690             {
691                 if (varDscInfo->canEnreg(TYP_INT, 1) &&     // The beginning of the struct can go in a register
692                     !varDscInfo->canEnreg(TYP_INT, cSlots)) // The end of the struct can't fit in a register
693                 {
694                     cSlotsToEnregister = 1; // Force the split
695                 }
696             }
697         }
698
699 #endif // defined(TARGET_ARM64)
700
701 #ifdef TARGET_ARM
702         // On ARM we pass the first 4 words of integer arguments and non-HFA structs in registers.
703         // But we pre-spill user arguments in varargs methods and structs.
704         //
705         unsigned cAlign;
706         bool     preSpill = info.compIsVarArgs || isSoftFPPreSpill;
707
708         switch (origArgType)
709         {
710             case TYP_STRUCT:
711                 assert(varDsc->lvSize() == argSize);
712                 cAlign = varDsc->lvStructDoubleAlign ? 2 : 1;
713
714                 // HFA arguments go on the stack frame. They don't get spilled in the prolog like struct
715                 // arguments passed in the integer registers but get homed immediately after the prolog.
716                 if (!isHfaArg)
717                 {
718                     // TODO-Arm32-Windows: vararg struct should be forced to split like
719                     // ARM64 above.
720                     cSlotsToEnregister = 1; // HFAs must be totally enregistered or not, but other structs can be split.
721                     preSpill           = true;
722                 }
723                 break;
724
725             case TYP_DOUBLE:
726             case TYP_LONG:
727                 cAlign = 2;
728                 break;
729
730             default:
731                 cAlign = 1;
732                 break;
733         }
734
735         if (isRegParamType(argType))
736         {
737             compArgSize += varDscInfo->alignReg(argType, cAlign) * REGSIZE_BYTES;
738         }
739
740         if (argType == TYP_STRUCT)
741         {
742             // Are we going to split the struct between registers and stack? We can do that as long as
743             // no floating-point arguments have been put on the stack.
744             //
745             // From the ARM Procedure Call Standard:
746             // Rule C.5: "If the NCRN is less than r4 **and** the NSAA is equal to the SP,"
747             // then split the argument between registers and stack. Implication: if something
748             // has already been spilled to the stack, then anything that would normally be
749             // split between the core registers and the stack will be put on the stack.
750             // Anything that follows will also be on the stack. However, if something from
751             // floating point regs has been spilled to the stack, we can still use r0-r3 until they are full.
752
753             if (varDscInfo->canEnreg(TYP_INT, 1) &&       // The beginning of the struct can go in a register
754                 !varDscInfo->canEnreg(TYP_INT, cSlots) && // The end of the struct can't fit in a register
755                 varDscInfo->existAnyFloatStackArgs())     // There's at least one stack-based FP arg already
756             {
757                 varDscInfo->setAllRegArgUsed(TYP_INT); // Prevent all future use of integer registers
758                 preSpill = false;                      // This struct won't be prespilled, since it will go on the stack
759             }
760         }
761
762         if (preSpill)
763         {
764             for (unsigned ix = 0; ix < cSlots; ix++)
765             {
766                 if (!varDscInfo->canEnreg(TYP_INT, ix + 1))
767                 {
768                     break;
769                 }
770                 regMaskTP regMask = genMapArgNumToRegMask(varDscInfo->regArgNum(TYP_INT) + ix, TYP_INT);
771                 if (cAlign == 2)
772                 {
773                     doubleAlignMask |= regMask;
774                 }
775                 codeGen->regSet.rsMaskPreSpillRegArg |= regMask;
776             }
777         }
778 #else // !TARGET_ARM
779
780 #if defined(UNIX_AMD64_ABI)
781         SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
782         if (varTypeIsStruct(argType))
783         {
784             assert(typeHnd != nullptr);
785             eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc);
786             if (structDesc.passedInRegisters)
787             {
788                 unsigned intRegCount   = 0;
789                 unsigned floatRegCount = 0;
790
791                 for (unsigned int i = 0; i < structDesc.eightByteCount; i++)
792                 {
793                     if (structDesc.IsIntegralSlot(i))
794                     {
795                         intRegCount++;
796                     }
797                     else if (structDesc.IsSseSlot(i))
798                     {
799                         floatRegCount++;
800                     }
801                     else
802                     {
803                         assert(false && "Invalid eightbyte classification type.");
804                         break;
805                     }
806                 }
807
808                 if (intRegCount != 0 && !varDscInfo->canEnreg(TYP_INT, intRegCount))
809                 {
810                     structDesc.passedInRegisters = false; // No register to enregister the eightbytes.
811                 }
812
813                 if (floatRegCount != 0 && !varDscInfo->canEnreg(TYP_FLOAT, floatRegCount))
814                 {
815                     structDesc.passedInRegisters = false; // No register to enregister the eightbytes.
816                 }
817             }
818         }
819 #endif // UNIX_AMD64_ABI
820 #endif // !TARGET_ARM
821
822         // The final home for this incoming register might be our local stack frame.
823         // For System V platforms the final home will always be on the local stack frame.
824         varDsc->lvOnFrame = true;
825
826         bool canPassArgInRegisters = false;
827
828 #if defined(UNIX_AMD64_ABI)
829         if (varTypeIsStruct(argType))
830         {
831             canPassArgInRegisters = structDesc.passedInRegisters;
832         }
833         else
834 #elif defined(TARGET_X86)
835         if (varTypeIsStruct(argType) && isTrivialPointerSizedStruct(typeHnd))
836         {
837             canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, cSlotsToEnregister);
838         }
839         else
840 #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
841         uint32_t  floatFlags          = STRUCT_NO_FLOAT_FIELD;
842         var_types argRegTypeInStruct1 = TYP_UNKNOWN;
843         var_types argRegTypeInStruct2 = TYP_UNKNOWN;
844
845         if ((strip(corInfoType) == CORINFO_TYPE_VALUECLASS) && (argSize <= MAX_PASS_MULTIREG_BYTES))
846         {
847 #if defined(TARGET_LOONGARCH64)
848             floatFlags = info.compCompHnd->getLoongArch64PassStructInRegisterFlags(typeHnd);
849 #else
850             floatFlags = info.compCompHnd->getRISCV64PassStructInRegisterFlags(typeHnd);
851 #endif
852         }
853
854         if ((floatFlags & STRUCT_HAS_FLOAT_FIELDS_MASK) != 0)
855         {
856             assert(varTypeIsStruct(argType));
857             int floatNum = 0;
858             if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_ONE) != 0)
859             {
860                 assert(argSize <= 8);
861                 assert(varDsc->lvExactSize() <= argSize);
862
863                 floatNum              = 1;
864                 canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1);
865
866                 argRegTypeInStruct1 = (varDsc->lvExactSize() == 8) ? TYP_DOUBLE : TYP_FLOAT;
867             }
868             else if ((floatFlags & STRUCT_FLOAT_FIELD_ONLY_TWO) != 0)
869             {
870                 floatNum              = 2;
871                 canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 2);
872
873                 argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT;
874                 argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT;
875             }
876             else if ((floatFlags & STRUCT_FLOAT_FIELD_FIRST) != 0)
877             {
878                 floatNum              = 1;
879                 canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1);
880                 canPassArgInRegisters = canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1);
881
882                 argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT;
883                 argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT;
884             }
885             else if ((floatFlags & STRUCT_FLOAT_FIELD_SECOND) != 0)
886             {
887                 floatNum              = 1;
888                 canPassArgInRegisters = varDscInfo->canEnreg(TYP_DOUBLE, 1);
889                 canPassArgInRegisters = canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1);
890
891                 argRegTypeInStruct1 = (floatFlags & STRUCT_FIRST_FIELD_SIZE_IS8) ? TYP_LONG : TYP_INT;
892                 argRegTypeInStruct2 = (floatFlags & STRUCT_SECOND_FIELD_SIZE_IS8) ? TYP_DOUBLE : TYP_FLOAT;
893             }
894
895             assert((floatNum == 1) || (floatNum == 2));
896
897             if (!canPassArgInRegisters)
898             {
899                 // On LoongArch64, if there aren't any remaining floating-point registers to pass the argument,
900                 // integer registers (if any) are used instead.
901                 canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister);
902
903                 argRegTypeInStruct1 = TYP_UNKNOWN;
904                 argRegTypeInStruct2 = TYP_UNKNOWN;
905
906                 if (cSlotsToEnregister == 2)
907                 {
908                     if (!canPassArgInRegisters && varDscInfo->canEnreg(TYP_I_IMPL, 1))
909                     {
910                         // Here a struct-arg which needs two registers but only one integer register available,
911                         // it has to be split.
912                         argRegTypeInStruct1   = TYP_I_IMPL;
913                         canPassArgInRegisters = true;
914                     }
915                 }
916             }
917         }
918         else
919 #endif // defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
920         {
921             canPassArgInRegisters = varDscInfo->canEnreg(argType, cSlotsToEnregister);
922 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
923             // On LoongArch64 and RISCV64, if there aren't any remaining floating-point registers to pass the
924             // argument,
925             // integer registers (if any) are used instead.
926             if (!canPassArgInRegisters && varTypeIsFloating(argType))
927             {
928                 canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, cSlotsToEnregister);
929                 argType               = canPassArgInRegisters ? TYP_I_IMPL : argType;
930             }
931             if (!canPassArgInRegisters && (cSlots > 1))
932             {
933                 // If a struct-arg which needs two registers but only one integer register available,
934                 // it has to be split.
935                 canPassArgInRegisters = varDscInfo->canEnreg(TYP_I_IMPL, 1);
936                 argRegTypeInStruct1   = canPassArgInRegisters ? TYP_I_IMPL : TYP_UNKNOWN;
937             }
938 #endif
939         }
940
941         if (canPassArgInRegisters)
942         {
943             /* Another register argument */
944
945             // Allocate the registers we need. allocRegArg() returns the first argument register number of the set.
946             // For non-HFA structs, we still "try" to enregister the whole thing; it will just max out if splitting
947             // to the stack happens.
948             unsigned firstAllocatedRegArgNum = 0;
949
950 #if FEATURE_MULTIREG_ARGS
951             varDsc->SetOtherArgReg(REG_NA);
952 #endif // FEATURE_MULTIREG_ARGS
953
954 #if defined(UNIX_AMD64_ABI)
955             unsigned  secondAllocatedRegArgNum = 0;
956             var_types firstEightByteType       = TYP_UNDEF;
957             var_types secondEightByteType      = TYP_UNDEF;
958
959             if (varTypeIsStruct(argType))
960             {
961                 if (structDesc.eightByteCount >= 1)
962                 {
963                     firstEightByteType      = GetEightByteType(structDesc, 0);
964                     firstAllocatedRegArgNum = varDscInfo->allocRegArg(firstEightByteType, 1);
965                 }
966             }
967             else
968 #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
969             unsigned secondAllocatedRegArgNum = 0;
970             if (argRegTypeInStruct1 != TYP_UNKNOWN)
971             {
972                 firstAllocatedRegArgNum = varDscInfo->allocRegArg(argRegTypeInStruct1, 1);
973             }
974             else
975 #endif // defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
976             {
977                 firstAllocatedRegArgNum = varDscInfo->allocRegArg(argType, cSlots);
978             }
979
980             if (isHfaArg)
981             {
982                 // We need to save the fact that this HFA is enregistered
983                 // Note that we can have HVAs of SIMD types even if we are not recognizing intrinsics.
984                 // In that case, we won't have normalized the vector types on the varDsc, so if we have a single vector
985                 // register, we need to set the type now. Otherwise, later we'll assume this is passed by reference.
986                 if (varDsc->lvHfaSlots() != 1)
987                 {
988                     varDsc->lvIsMultiRegArg = true;
989                 }
990             }
991
992             varDsc->lvIsRegArg = 1;
993
994 #if FEATURE_MULTIREG_ARGS
995 #ifdef TARGET_ARM64
996             if (argType == TYP_STRUCT)
997             {
998                 varDsc->SetArgReg(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, TYP_I_IMPL));
999                 if (cSlots == 2)
1000                 {
1001                     varDsc->SetOtherArgReg(genMapRegArgNumToRegNum(firstAllocatedRegArgNum + 1, TYP_I_IMPL));
1002                     varDsc->lvIsMultiRegArg = true;
1003                 }
1004             }
1005 #elif defined(UNIX_AMD64_ABI)
1006             if (varTypeIsStruct(argType))
1007             {
1008                 varDsc->SetArgReg(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, firstEightByteType));
1009
1010                 // If there is a second eightbyte, get a register for it too and map the arg to the reg number.
1011                 if (structDesc.eightByteCount >= 2)
1012                 {
1013                     secondEightByteType      = GetEightByteType(structDesc, 1);
1014                     secondAllocatedRegArgNum = varDscInfo->allocRegArg(secondEightByteType, 1);
1015                     varDsc->lvIsMultiRegArg  = true;
1016                 }
1017
1018                 if (secondEightByteType != TYP_UNDEF)
1019                 {
1020                     varDsc->SetOtherArgReg(genMapRegArgNumToRegNum(secondAllocatedRegArgNum, secondEightByteType));
1021                 }
1022             }
1023 #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1024             if (argType == TYP_STRUCT)
1025             {
1026                 if (argRegTypeInStruct1 != TYP_UNKNOWN)
1027                 {
1028                     varDsc->SetArgReg(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argRegTypeInStruct1));
1029                     varDsc->lvIs4Field1 = (genTypeSize(argRegTypeInStruct1) == 4) ? 1 : 0;
1030                     if (argRegTypeInStruct2 != TYP_UNKNOWN)
1031                     {
1032                         secondAllocatedRegArgNum = varDscInfo->allocRegArg(argRegTypeInStruct2, 1);
1033                         varDsc->SetOtherArgReg(genMapRegArgNumToRegNum(secondAllocatedRegArgNum, argRegTypeInStruct2));
1034                         varDsc->lvIs4Field2 = (genTypeSize(argRegTypeInStruct2) == 4) ? 1 : 0;
1035                     }
1036                     else if (cSlots > 1)
1037                     {
1038                         // Here a struct-arg which needs two registers but only one integer register available,
1039                         // it has to be split. But we reserved extra 8-bytes for the whole struct.
1040                         varDsc->lvIsSplit = 1;
1041                         varDsc->SetOtherArgReg(REG_STK);
1042                         varDscInfo->setAllRegArgUsed(argRegTypeInStruct1);
1043 #if FEATURE_FASTTAILCALL
1044                         varDscInfo->stackArgSize += TARGET_POINTER_SIZE;
1045 #endif
1046 #ifdef TARGET_RISCV64
1047                         varDscInfo->hasSplitParam = true;
1048 #endif
1049                     }
1050                 }
1051                 else
1052                 {
1053                     varDsc->SetArgReg(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, TYP_I_IMPL));
1054                     if (cSlots == 2)
1055                     {
1056                         varDsc->SetOtherArgReg(genMapRegArgNumToRegNum(firstAllocatedRegArgNum + 1, TYP_I_IMPL));
1057                     }
1058                 }
1059             }
1060 #else  // ARM32
1061             if (varTypeIsStruct(argType))
1062             {
1063                 varDsc->SetArgReg(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, TYP_I_IMPL));
1064             }
1065 #endif // ARM32
1066             else
1067 #endif // FEATURE_MULTIREG_ARGS
1068             {
1069                 varDsc->SetArgReg(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argType));
1070             }
1071
1072 #ifdef TARGET_ARM
1073             if (varDsc->TypeGet() == TYP_LONG)
1074             {
1075                 varDsc->SetOtherArgReg(genMapRegArgNumToRegNum(firstAllocatedRegArgNum + 1, TYP_INT));
1076             }
1077
1078 #if FEATURE_FASTTAILCALL
1079             // Check if arg was split between registers and stack.
1080             if (varTypeUsesIntReg(argType))
1081             {
1082                 unsigned firstRegArgNum = genMapIntRegNumToRegArgNum(varDsc->GetArgReg());
1083                 unsigned lastRegArgNum  = firstRegArgNum + cSlots - 1;
1084                 if (lastRegArgNum >= varDscInfo->maxIntRegArgNum)
1085                 {
1086                     assert(varDscInfo->stackArgSize == 0);
1087                     unsigned numEnregistered = varDscInfo->maxIntRegArgNum - firstRegArgNum;
1088                     varDsc->SetStackOffset(-(int)numEnregistered * REGSIZE_BYTES);
1089                     varDscInfo->stackArgSize += (cSlots - numEnregistered) * REGSIZE_BYTES;
1090                     varDscInfo->hasSplitParam = true;
1091                     JITDUMP("set user arg V%02u offset to %d\n", varDscInfo->varNum, varDsc->GetStackOffset());
1092                 }
1093             }
1094 #endif
1095 #endif // TARGET_ARM
1096
1097 #ifdef DEBUG
1098             if (verbose)
1099             {
1100                 printf("Arg #%u    passed in register(s) ", varDscInfo->varNum);
1101
1102 #if defined(UNIX_AMD64_ABI)
1103                 if (varTypeIsStruct(argType))
1104                 {
1105                     // Print both registers, just to be clear
1106                     if (firstEightByteType == TYP_UNDEF)
1107                     {
1108                         printf("firstEightByte: <not used>");
1109                     }
1110                     else
1111                     {
1112                         printf("firstEightByte: %s",
1113                                getRegName(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, firstEightByteType)));
1114                     }
1115
1116                     if (secondEightByteType == TYP_UNDEF)
1117                     {
1118                         printf(", secondEightByte: <not used>");
1119                     }
1120                     else
1121                     {
1122                         printf(", secondEightByte: %s",
1123                                getRegName(genMapRegArgNumToRegNum(secondAllocatedRegArgNum, secondEightByteType)));
1124                     }
1125                 }
1126                 else
1127 #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1128                 if (varTypeIsStruct(argType))
1129                 {
1130                     if (argRegTypeInStruct1 == TYP_UNKNOWN)
1131                     {
1132                         printf("first: <not used>");
1133                     }
1134                     else
1135                     {
1136                         printf("first: %s",
1137                                getRegName(genMapRegArgNumToRegNum(firstAllocatedRegArgNum, argRegTypeInStruct1)));
1138                     }
1139                     if (argRegTypeInStruct2 == TYP_UNKNOWN)
1140                     {
1141                         printf(", second: <not used>");
1142                     }
1143                     else
1144                     {
1145                         printf(", second: %s",
1146                                getRegName(genMapRegArgNumToRegNum(secondAllocatedRegArgNum, argRegTypeInStruct2)));
1147                     }
1148                 }
1149                 else
1150 #endif // UNIX_AMD64_ABI, TARGET_LOONGARCH64, TARGET_RISCV64
1151                 {
1152                     assert(varTypeUsesFloatReg(argType) || varTypeUsesIntReg(argType));
1153
1154                     bool     isFloat   = varTypeUsesFloatReg(argType);
1155                     unsigned regArgNum = genMapRegNumToRegArgNum(varDsc->GetArgReg(), argType);
1156
1157                     for (unsigned ix = 0; ix < cSlots; ix++, regArgNum++)
1158                     {
1159                         if (ix > 0)
1160                         {
1161                             printf(",");
1162                         }
1163
1164                         if (!isFloat && (regArgNum >= varDscInfo->maxIntRegArgNum)) // a struct has been split between
1165                                                                                     // registers and stack
1166                         {
1167                             printf(" stack slots:%d", cSlots - ix);
1168                             break;
1169                         }
1170
1171 #ifdef TARGET_ARM
1172                         if (isFloat)
1173                         {
1174                             // Print register size prefix
1175                             if (argType == TYP_DOUBLE)
1176                             {
1177                                 // Print both registers, just to be clear
1178                                 printf("%s/%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType)),
1179                                        getRegName(genMapRegArgNumToRegNum(regArgNum + 1, argType)));
1180
1181                                 // doubles take 2 slots
1182                                 assert(ix + 1 < cSlots);
1183                                 ++ix;
1184                                 ++regArgNum;
1185                             }
1186                             else
1187                             {
1188                                 printf("%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType)));
1189                             }
1190                         }
1191                         else
1192 #endif // TARGET_ARM
1193                         {
1194                             printf("%s", getRegName(genMapRegArgNumToRegNum(regArgNum, argType)));
1195                         }
1196                     }
1197                 }
1198                 printf("\n");
1199             }
1200 #endif    // DEBUG
1201         } // end if (canPassArgInRegisters)
1202         else
1203         {
1204 #if defined(TARGET_ARM)
1205             varDscInfo->setAllRegArgUsed(argType);
1206
1207             if (varTypeUsesFloatReg(argType))
1208             {
1209                 varDscInfo->setAnyFloatStackArgs();
1210             }
1211             else
1212             {
1213                 assert(varTypeUsesIntReg(argType));
1214             }
1215
1216 #elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1217
1218             // If we needed to use the stack in order to pass this argument then
1219             // record the fact that we have used up any remaining registers of this 'type'
1220             // This prevents any 'backfilling' from occurring on ARM64/LoongArch64.
1221             //
1222             varDscInfo->setAllRegArgUsed(argType);
1223
1224 #endif // TARGET_XXX
1225
1226 #if FEATURE_FASTTAILCALL
1227 #ifdef TARGET_ARM
1228             unsigned argAlignment = cAlign * TARGET_POINTER_SIZE;
1229 #else
1230             unsigned argAlignment = eeGetArgSizeAlignment(origArgType, (hfaType == TYP_FLOAT));
1231             // We expect the following rounding operation to be a noop on all
1232             // ABIs except ARM (where we have 8-byte aligned args) and macOS
1233             // ARM64 (that allows to pack multiple smaller parameters in a
1234             // single stack slot).
1235             assert(compMacOsArm64Abi() || ((varDscInfo->stackArgSize % argAlignment) == 0));
1236 #endif
1237             varDscInfo->stackArgSize = roundUp(varDscInfo->stackArgSize, argAlignment);
1238
1239             JITDUMP("set user arg V%02u offset to %u\n", varDscInfo->varNum, varDscInfo->stackArgSize);
1240             varDsc->SetStackOffset(varDscInfo->stackArgSize);
1241             varDscInfo->stackArgSize += argSize;
1242 #endif // FEATURE_FASTTAILCALL
1243         }
1244
1245 #ifdef UNIX_AMD64_ABI
1246         // The arg size is returning the number of bytes of the argument. For a struct it could return a size not a
1247         // multiple of TARGET_POINTER_SIZE. The stack allocated space should always be multiple of TARGET_POINTER_SIZE,
1248         // so round it up.
1249         compArgSize += roundUp(argSize, TARGET_POINTER_SIZE);
1250 #else  // !UNIX_AMD64_ABI
1251         compArgSize += argSize;
1252 #endif // !UNIX_AMD64_ABI
1253         if (info.compIsVarArgs || isSoftFPPreSpill)
1254         {
1255 #if defined(TARGET_X86)
1256             varDsc->SetStackOffset(compArgSize);
1257 #else  // !TARGET_X86
1258             // TODO-CQ: We shouldn't have to go as far as to declare these
1259             // address-exposed -- DoNotEnregister should suffice.
1260
1261             lvaSetVarAddrExposed(varDscInfo->varNum DEBUGARG(AddressExposedReason::TOO_CONSERVATIVE));
1262 #endif // !TARGET_X86
1263         }
1264     }
1265
1266     compArgSize = GetOutgoingArgByteSize(compArgSize);
1267
1268 #ifdef TARGET_ARM
1269     if (doubleAlignMask != RBM_NONE)
1270     {
1271         assert(RBM_ARG_REGS == 0xF);
1272         assert((doubleAlignMask & RBM_ARG_REGS) == doubleAlignMask);
1273         if (doubleAlignMask != RBM_NONE && doubleAlignMask != RBM_ARG_REGS)
1274         {
1275             // 'double aligned types' can begin only at r0 or r2 and we always expect at least two registers to be used
1276             // Note that in rare cases, we can have double-aligned structs of 12 bytes (if specified explicitly with
1277             // attributes)
1278             assert((doubleAlignMask == 0b0011) || (doubleAlignMask == 0b1100) ||
1279                    (doubleAlignMask == 0b0111) /* || 0b1111 is if'ed out */);
1280
1281             // Now if doubleAlignMask is xyz1 i.e., the struct starts in r0, and we prespill r2 or r3
1282             // but not both, then the stack would be misaligned for r0. So spill both
1283             // r2 and r3.
1284             //
1285             // ; +0 --- caller SP double aligned ----
1286             // ; -4 r2    r3
1287             // ; -8 r1    r1
1288             // ; -c r0    r0   <-- misaligned.
1289             // ; callee saved regs
1290             bool startsAtR0 = (doubleAlignMask & 1) == 1;
1291             bool r2XorR3    = ((codeGen->regSet.rsMaskPreSpillRegArg & RBM_R2) == 0) !=
1292                            ((codeGen->regSet.rsMaskPreSpillRegArg & RBM_R3) == 0);
1293             if (startsAtR0 && r2XorR3)
1294             {
1295                 codeGen->regSet.rsMaskPreSpillAlign =
1296                     (~codeGen->regSet.rsMaskPreSpillRegArg & ~doubleAlignMask) & RBM_ARG_REGS;
1297             }
1298         }
1299     }
1300 #endif // TARGET_ARM
1301 }
1302
1303 /*****************************************************************************/
1304 void Compiler::lvaInitGenericsCtxt(InitVarDscInfo* varDscInfo)
1305 {
1306     //@GENERICS: final instantiation-info argument for shared generic methods
1307     // and shared generic struct instance methods
1308     if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
1309     {
1310         info.compTypeCtxtArg = varDscInfo->varNum;
1311
1312         LclVarDsc* varDsc = varDscInfo->varDsc;
1313         varDsc->lvIsParam = 1;
1314         varDsc->lvType    = TYP_I_IMPL;
1315
1316         if (varDscInfo->canEnreg(TYP_I_IMPL))
1317         {
1318             /* Another register argument */
1319
1320             varDsc->lvIsRegArg = 1;
1321             varDsc->SetArgReg(genMapRegArgNumToRegNum(varDscInfo->regArgNum(TYP_INT), varDsc->TypeGet()));
1322 #if FEATURE_MULTIREG_ARGS
1323             varDsc->SetOtherArgReg(REG_NA);
1324 #endif
1325             varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
1326
1327             varDscInfo->intRegArgNum++;
1328
1329 #ifdef DEBUG
1330             if (verbose)
1331             {
1332                 printf("'GenCtxt'   passed in register %s\n", getRegName(varDsc->GetArgReg()));
1333             }
1334 #endif
1335         }
1336         else
1337         {
1338             // We need to mark these as being on the stack, as this is not done elsewhere in the case that canEnreg
1339             // returns false.
1340             varDsc->lvOnFrame = true;
1341 #if FEATURE_FASTTAILCALL
1342             varDsc->SetStackOffset(varDscInfo->stackArgSize);
1343             varDscInfo->stackArgSize += TARGET_POINTER_SIZE;
1344 #endif // FEATURE_FASTTAILCALL
1345         }
1346
1347         compArgSize += TARGET_POINTER_SIZE;
1348
1349 #if defined(TARGET_X86)
1350         if (info.compIsVarArgs)
1351             varDsc->SetStackOffset(compArgSize);
1352 #endif // TARGET_X86
1353
1354         varDscInfo->varNum++;
1355         varDscInfo->varDsc++;
1356     }
1357 }
1358
1359 /*****************************************************************************/
1360 void Compiler::lvaInitVarArgsHandle(InitVarDscInfo* varDscInfo)
1361 {
1362     if (info.compIsVarArgs)
1363     {
1364         lvaVarargsHandleArg = varDscInfo->varNum;
1365
1366         LclVarDsc* varDsc = varDscInfo->varDsc;
1367         varDsc->lvType    = TYP_I_IMPL;
1368         varDsc->lvIsParam = 1;
1369 #if defined(TARGET_X86)
1370         // Codegen will need it for x86 scope info.
1371         varDsc->lvImplicitlyReferenced = 1;
1372 #endif // TARGET_X86
1373         varDsc->lvHasLdAddrOp = 1;
1374
1375         lvaSetVarDoNotEnregister(lvaVarargsHandleArg DEBUGARG(DoNotEnregisterReason::VMNeedsStackAddr));
1376
1377         assert(mostRecentlyActivePhase == PHASE_PRE_IMPORT);
1378
1379         // TODO-Cleanup: this is preImportation phase, why do we try to work with regs here?
1380         // Should it be just deleted?
1381         if (varDscInfo->canEnreg(TYP_I_IMPL))
1382         {
1383             /* Another register argument */
1384
1385             unsigned varArgHndArgNum = varDscInfo->allocRegArg(TYP_I_IMPL);
1386
1387             varDsc->lvIsRegArg = 1;
1388             varDsc->SetArgReg(genMapRegArgNumToRegNum(varArgHndArgNum, TYP_I_IMPL));
1389 #if FEATURE_MULTIREG_ARGS
1390             varDsc->SetOtherArgReg(REG_NA);
1391 #endif
1392             varDsc->lvOnFrame = true; // The final home for this incoming register might be our local stack frame
1393 #ifdef TARGET_ARM
1394             // This has to be spilled right in front of the real arguments and we have
1395             // to pre-spill all the argument registers explicitly because we only have
1396             // have symbols for the declared ones, not any potential variadic ones.
1397             for (unsigned ix = varArgHndArgNum; ix < ArrLen(intArgMasks); ix++)
1398             {
1399                 codeGen->regSet.rsMaskPreSpillRegArg |= intArgMasks[ix];
1400             }
1401 #endif // TARGET_ARM
1402
1403 #ifdef DEBUG
1404             if (verbose)
1405             {
1406                 printf("'VarArgHnd' passed in register %s\n", getRegName(varDsc->GetArgReg()));
1407             }
1408 #endif // DEBUG
1409         }
1410         else
1411         {
1412             // We need to mark these as being on the stack, as this is not done elsewhere in the case that canEnreg
1413             // returns false.
1414             varDsc->lvOnFrame = true;
1415 #if FEATURE_FASTTAILCALL
1416             varDsc->SetStackOffset(varDscInfo->stackArgSize);
1417             varDscInfo->stackArgSize += TARGET_POINTER_SIZE;
1418 #endif // FEATURE_FASTTAILCALL
1419         }
1420
1421         /* Update the total argument size, count and varDsc */
1422
1423         compArgSize += TARGET_POINTER_SIZE;
1424
1425         varDscInfo->varNum++;
1426         varDscInfo->varDsc++;
1427
1428 #if defined(TARGET_X86)
1429         varDsc->SetStackOffset(compArgSize);
1430
1431         // Allocate a temp to point at the beginning of the args
1432
1433         lvaVarargsBaseOfStkArgs                  = lvaGrabTemp(false DEBUGARG("Varargs BaseOfStkArgs"));
1434         lvaTable[lvaVarargsBaseOfStkArgs].lvType = TYP_I_IMPL;
1435
1436 #endif // TARGET_X86
1437     }
1438 }
1439
1440 /*****************************************************************************/
1441 void Compiler::lvaInitVarDsc(LclVarDsc*              varDsc,
1442                              unsigned                varNum,
1443                              CorInfoType             corInfoType,
1444                              CORINFO_CLASS_HANDLE    typeHnd,
1445                              CORINFO_ARG_LIST_HANDLE varList,
1446                              CORINFO_SIG_INFO*       varSig)
1447 {
1448     noway_assert(varDsc == lvaGetDesc(varNum));
1449
1450     switch (corInfoType)
1451     {
1452         // Mark types that looks like a pointer for doing shadow-copying of
1453         // parameters if we have an unsafe buffer.
1454         // Note that this does not handle structs with pointer fields. Instead,
1455         // we rely on using the assign-groups/equivalence-groups in
1456         // gsFindVulnerableParams() to determine if a buffer-struct contains a
1457         // pointer. We could do better by having the EE determine this for us.
1458         // Note that we want to keep buffers without pointers at lower memory
1459         // addresses than buffers with pointers.
1460         case CORINFO_TYPE_PTR:
1461         case CORINFO_TYPE_BYREF:
1462         case CORINFO_TYPE_CLASS:
1463         case CORINFO_TYPE_STRING:
1464         case CORINFO_TYPE_VAR:
1465         case CORINFO_TYPE_REFANY:
1466             varDsc->lvIsPtr = 1;
1467             break;
1468         default:
1469             break;
1470     }
1471
1472     var_types type = JITtype2varType(corInfoType);
1473     if (varTypeIsFloating(type))
1474     {
1475         compFloatingPointUsed = true;
1476     }
1477
1478 #if FEATURE_IMPLICIT_BYREFS
1479     varDsc->lvIsImplicitByRef = 0;
1480 #endif // FEATURE_IMPLICIT_BYREFS
1481 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
1482     varDsc->lvIs4Field1 = 0;
1483     varDsc->lvIs4Field2 = 0;
1484     varDsc->lvIsSplit   = 0;
1485 #endif // TARGET_LOONGARCH64 || TARGET_RISCV64
1486
1487     // Set the lvType (before this point it is TYP_UNDEF).
1488
1489     if (GlobalJitOptions::compFeatureHfa)
1490     {
1491         varDsc->SetHfaType(TYP_UNDEF);
1492     }
1493     if ((varTypeIsStruct(type)))
1494     {
1495         lvaSetStruct(varNum, typeHnd, typeHnd != NO_CLASS_HANDLE);
1496         if (info.compIsVarArgs)
1497         {
1498             lvaSetStructUsedAsVarArg(varNum);
1499         }
1500     }
1501     else
1502     {
1503         varDsc->lvType = type;
1504     }
1505
1506     if (type == TYP_BOOL)
1507     {
1508         varDsc->lvIsBoolean = true;
1509     }
1510
1511 #ifdef DEBUG
1512     varDsc->SetStackOffset(BAD_STK_OFFS);
1513 #endif
1514
1515 #if FEATURE_MULTIREG_ARGS
1516     varDsc->SetOtherArgReg(REG_NA);
1517 #endif // FEATURE_MULTIREG_ARGS
1518 }
1519
1520 /*****************************************************************************
1521  * Returns our internal varNum for a given IL variable.
1522  * Asserts assume it is called after lvaTable[] has been set up.
1523  */
1524
1525 unsigned Compiler::compMapILvarNum(unsigned ILvarNum)
1526 {
1527     noway_assert(ILvarNum < info.compILlocalsCount || ILvarNum > unsigned(ICorDebugInfo::UNKNOWN_ILNUM));
1528
1529     unsigned varNum;
1530
1531     if (ILvarNum == (unsigned)ICorDebugInfo::VARARGS_HND_ILNUM)
1532     {
1533         // The varargs cookie is the last argument in lvaTable[]
1534         noway_assert(info.compIsVarArgs);
1535
1536         varNum = lvaVarargsHandleArg;
1537         noway_assert(lvaTable[varNum].lvIsParam);
1538     }
1539     else if (ILvarNum == (unsigned)ICorDebugInfo::RETBUF_ILNUM)
1540     {
1541         noway_assert(info.compRetBuffArg != BAD_VAR_NUM);
1542         varNum = info.compRetBuffArg;
1543     }
1544     else if (ILvarNum == (unsigned)ICorDebugInfo::TYPECTXT_ILNUM)
1545     {
1546         noway_assert(info.compTypeCtxtArg >= 0);
1547         varNum = unsigned(info.compTypeCtxtArg);
1548     }
1549     else if (ILvarNum < info.compILargsCount)
1550     {
1551         // Parameter
1552         varNum = compMapILargNum(ILvarNum);
1553         noway_assert(lvaTable[varNum].lvIsParam);
1554     }
1555     else if (ILvarNum < info.compILlocalsCount)
1556     {
1557         // Local variable
1558         unsigned lclNum = ILvarNum - info.compILargsCount;
1559         varNum          = info.compArgsCount + lclNum;
1560         noway_assert(!lvaTable[varNum].lvIsParam);
1561     }
1562     else
1563     {
1564         unreached();
1565     }
1566
1567     noway_assert(varNum < info.compLocalsCount);
1568     return varNum;
1569 }
1570
1571 /*****************************************************************************
1572  * Returns the IL variable number given our internal varNum.
1573  * Special return values are VARG_ILNUM, RETBUF_ILNUM, TYPECTXT_ILNUM.
1574  *
1575  * Returns UNKNOWN_ILNUM if it can't be mapped.
1576  */
1577
1578 unsigned Compiler::compMap2ILvarNum(unsigned varNum) const
1579 {
1580     if (compIsForInlining())
1581     {
1582         return impInlineInfo->InlinerCompiler->compMap2ILvarNum(varNum);
1583     }
1584
1585     noway_assert(varNum < lvaCount);
1586
1587     if (varNum == info.compRetBuffArg)
1588     {
1589         return (unsigned)ICorDebugInfo::RETBUF_ILNUM;
1590     }
1591
1592     // Is this a varargs function?
1593     if (info.compIsVarArgs && varNum == lvaVarargsHandleArg)
1594     {
1595         return (unsigned)ICorDebugInfo::VARARGS_HND_ILNUM;
1596     }
1597
1598     // We create an extra argument for the type context parameter
1599     // needed for shared generic code.
1600     if ((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) && varNum == (unsigned)info.compTypeCtxtArg)
1601     {
1602         return (unsigned)ICorDebugInfo::TYPECTXT_ILNUM;
1603     }
1604
1605 #if FEATURE_FIXED_OUT_ARGS
1606     if (varNum == lvaOutgoingArgSpaceVar)
1607     {
1608         return (unsigned)ICorDebugInfo::UNKNOWN_ILNUM; // Cannot be mapped
1609     }
1610 #endif // FEATURE_FIXED_OUT_ARGS
1611
1612     // Now mutate varNum to remove extra parameters from the count.
1613     if ((info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) && varNum > (unsigned)info.compTypeCtxtArg)
1614     {
1615         varNum--;
1616     }
1617
1618     if (info.compIsVarArgs && varNum > lvaVarargsHandleArg)
1619     {
1620         varNum--;
1621     }
1622
1623     /* Is there a hidden argument for the return buffer.
1624        Note that this code works because if the RetBuffArg is not present,
1625        compRetBuffArg will be BAD_VAR_NUM */
1626     if (info.compRetBuffArg != BAD_VAR_NUM && varNum > info.compRetBuffArg)
1627     {
1628         varNum--;
1629     }
1630
1631     if (varNum >= info.compLocalsCount)
1632     {
1633         return (unsigned)ICorDebugInfo::UNKNOWN_ILNUM; // Cannot be mapped
1634     }
1635
1636     return varNum;
1637 }
1638
1639 /*****************************************************************************
1640  * Returns true if variable "varNum" may be address-exposed.
1641  */
1642
1643 bool Compiler::lvaVarAddrExposed(unsigned varNum) const
1644 {
1645     const LclVarDsc* varDsc = lvaGetDesc(varNum);
1646     return varDsc->IsAddressExposed();
1647 }
1648
1649 /*****************************************************************************
1650  * Returns true iff variable "varNum" should not be enregistered (or one of several reasons).
1651  */
1652
1653 bool Compiler::lvaVarDoNotEnregister(unsigned varNum)
1654 {
1655     LclVarDsc* varDsc = lvaGetDesc(varNum);
1656     return varDsc->lvDoNotEnregister;
1657 }
1658
1659 //------------------------------------------------------------------------
1660 // lvInitializeDoNotEnregFlag: a helper to initialize `lvDoNotEnregister` flag
1661 //    for locals that were created before the compiler decided its optimization level.
1662 //
1663 // Assumptions:
1664 //    compEnregLocals() value is finalized and is set to false.
1665 //
1666 void Compiler::lvSetMinOptsDoNotEnreg()
1667 {
1668     JITDUMP("compEnregLocals() is false, setting doNotEnreg flag for all locals.");
1669     assert(!compEnregLocals());
1670     for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++)
1671     {
1672         lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::NoRegVars));
1673     }
1674 }
1675
1676 //------------------------------------------------------------------------
1677 // StructPromotionHelper constructor.
1678 //
1679 // Arguments:
1680 //   compiler - pointer to a compiler to get access to an allocator, compHandle etc.
1681 //
1682 Compiler::StructPromotionHelper::StructPromotionHelper(Compiler* compiler) : compiler(compiler), structPromotionInfo()
1683 {
1684 }
1685
1686 //--------------------------------------------------------------------------------------------
1687 // TryPromoteStructVar - promote struct var if it is possible and profitable.
1688 //
1689 // Arguments:
1690 //   lclNum - struct number to try.
1691 //
1692 // Return value:
1693 //   true if the struct var was promoted.
1694 //
1695 bool Compiler::StructPromotionHelper::TryPromoteStructVar(unsigned lclNum)
1696 {
1697     if (CanPromoteStructVar(lclNum))
1698     {
1699 #if 0
1700             // Often-useful debugging code: if you've narrowed down a struct-promotion problem to a single
1701             // method, this allows you to select a subset of the vars to promote (by 1-based ordinal number).
1702             static int structPromoVarNum = 0;
1703             structPromoVarNum++;
1704             if (atoi(getenv("structpromovarnumlo")) <= structPromoVarNum && structPromoVarNum <= atoi(getenv("structpromovarnumhi")))
1705 #endif // 0
1706         if (ShouldPromoteStructVar(lclNum))
1707         {
1708             PromoteStructVar(lclNum);
1709             return true;
1710         }
1711     }
1712     return false;
1713 }
1714
1715 //--------------------------------------------------------------------------------------------
1716 // CanPromoteStructType - checks if the struct type can be promoted.
1717 //
1718 // Arguments:
1719 //   typeHnd - struct handle to check.
1720 //
1721 // Return value:
1722 //   true if the struct type can be promoted.
1723 //
1724 // Notes:
1725 //   The last analyzed type is memorized to skip the check if we ask about the same time again next.
1726 //   However, it was not found profitable to memorize all analyzed types in a map.
1727 //
1728 //   The check initializes only necessary fields in lvaStructPromotionInfo,
1729 //   so if the promotion is rejected early than most fields will be uninitialized.
1730 //
1731 bool Compiler::StructPromotionHelper::CanPromoteStructType(CORINFO_CLASS_HANDLE typeHnd)
1732 {
1733     assert(typeHnd != nullptr);
1734     if (!compiler->eeIsValueClass(typeHnd))
1735     {
1736         // TODO-ObjectStackAllocation: Enable promotion of fields of stack-allocated objects.
1737         return false;
1738     }
1739
1740     if (structPromotionInfo.typeHnd == typeHnd)
1741     {
1742         // Asking for the same type of struct as the last time.
1743         // Nothing need to be done.
1744         // Fall through ...
1745         return structPromotionInfo.canPromote;
1746     }
1747
1748     // Analyze this type from scratch.
1749     structPromotionInfo = lvaStructPromotionInfo(typeHnd);
1750
1751 #if defined(FEATURE_SIMD)
1752     // getMaxVectorByteLength() represents the size of the largest primitive type that we can struct promote.
1753     const unsigned maxSize =
1754         MAX_NumOfFieldsInPromotableStruct * max(compiler->getMaxVectorByteLength(), sizeof(double));
1755 #else  // !FEATURE_SIMD
1756     // sizeof(double) represents the size of the largest primitive type that we can struct promote.
1757     const unsigned maxSize = MAX_NumOfFieldsInPromotableStruct * sizeof(double);
1758 #endif // !FEATURE_SIMD
1759
1760     // lvaStructFieldInfo.fldOffset is byte-sized and offsets start from 0, so the max size can be 256
1761     assert(static_cast<unsigned char>(maxSize - 1) == (maxSize - 1));
1762
1763     // lvaStructFieldInfo.fieldCnt is byte-sized
1764     assert(static_cast<unsigned char>(MAX_NumOfFieldsInPromotableStruct) == MAX_NumOfFieldsInPromotableStruct);
1765
1766     COMP_HANDLE compHandle = compiler->info.compCompHnd;
1767
1768     unsigned structSize = compHandle->getClassSize(typeHnd);
1769     if (structSize > maxSize)
1770     {
1771         return false; // struct is too large
1772     }
1773
1774     DWORD typeFlags = compHandle->getClassAttribs(typeHnd);
1775
1776     if (StructHasOverlappingFields(typeFlags))
1777     {
1778         return false;
1779     }
1780
1781     if (StructHasIndexableFields(typeFlags))
1782     {
1783         return false;
1784     }
1785
1786 #ifdef TARGET_ARM
1787     // On ARM, we have a requirement on the struct alignment; see below.
1788     unsigned structAlignment = roundUp(compHandle->getClassAlignmentRequirement(typeHnd), TARGET_POINTER_SIZE);
1789 #endif // TARGET_ARM
1790
1791     // At most 1 (root node) + (4 promoted fields) + (each could be a wrapped primitive)
1792     CORINFO_TYPE_LAYOUT_NODE treeNodes[1 + MAX_NumOfFieldsInPromotableStruct * 2];
1793     size_t                   numTreeNodes = ArrLen(treeNodes);
1794     GetTypeLayoutResult      result       = compHandle->getTypeLayout(typeHnd, treeNodes, &numTreeNodes);
1795
1796     if ((result != GetTypeLayoutResult::Success) || (numTreeNodes <= 1))
1797     {
1798         return false;
1799     }
1800
1801     assert(treeNodes[0].size == structSize);
1802
1803     structPromotionInfo.fieldCnt = 0;
1804
1805     unsigned fieldsSize = 0;
1806
1807     // Some notes on the following:
1808     // 1. At most MAX_NumOfFieldsInPromotableStruct fields can be promoted
1809     // 2. Recursive promotion is not enabled as the rest of the JIT cannot
1810     //    handle some of the patterns produced efficiently
1811     // 3. The exception to the above is structs wrapping primitive types; we do
1812     //    support promoting those, but only through one layer of nesting (as a
1813     //    quirk -- this can probably be relaxed).
1814
1815     for (size_t i = 1; i < numTreeNodes;)
1816     {
1817         if (structPromotionInfo.fieldCnt >= MAX_NumOfFieldsInPromotableStruct)
1818         {
1819             return false;
1820         }
1821
1822         const CORINFO_TYPE_LAYOUT_NODE& node = treeNodes[i];
1823         assert(node.parent == 0);
1824         lvaStructFieldInfo& promField = structPromotionInfo.fields[structPromotionInfo.fieldCnt];
1825         INDEBUG(promField.diagFldHnd = node.diagFieldHnd);
1826
1827         // Ensured by assertion on size above.
1828         assert(FitsIn<decltype(promField.fldOffset)>(node.offset));
1829         promField.fldOffset = (uint8_t)node.offset;
1830
1831         promField.fldOrdinal = structPromotionInfo.fieldCnt;
1832         promField.fldSize    = node.size;
1833
1834         structPromotionInfo.fieldCnt++;
1835
1836         if (node.type == CORINFO_TYPE_VALUECLASS)
1837         {
1838             var_types fldType = TryPromoteValueClassAsPrimitive(treeNodes, numTreeNodes, i);
1839             if (fldType == TYP_UNDEF)
1840             {
1841                 return false;
1842             }
1843
1844             promField.fldType        = fldType;
1845             promField.fldSIMDTypeHnd = node.simdTypeHnd;
1846             AdvanceSubTree(treeNodes, numTreeNodes, &i);
1847         }
1848         else
1849         {
1850             promField.fldType = JITtype2varType(node.type);
1851             i++;
1852         }
1853
1854         fieldsSize += promField.fldSize;
1855
1856         if ((promField.fldOffset % promField.fldSize) != 0)
1857         {
1858             // The code in Compiler::genPushArgList that reconstitutes
1859             // struct values on the stack from promoted fields expects
1860             // those fields to be at their natural alignment.
1861             return false;
1862         }
1863
1864         noway_assert(promField.fldOffset + promField.fldSize <= structSize);
1865
1866 #ifdef TARGET_ARM
1867         // On ARM, for struct types that don't use explicit layout, the alignment of the struct is
1868         // at least the max alignment of its fields.  We take advantage of this invariant in struct promotion,
1869         // so verify it here.
1870         if (promField.fldSize > structAlignment)
1871         {
1872             // Don't promote vars whose struct types violates the invariant.  (Alignment == size for primitives.)
1873             return false;
1874         }
1875 #endif // TARGET_ARM
1876     }
1877
1878     if (fieldsSize != treeNodes[0].size)
1879     {
1880         structPromotionInfo.containsHoles = true;
1881     }
1882
1883     structPromotionInfo.anySignificantPadding = treeNodes[0].hasSignificantPadding && structPromotionInfo.containsHoles;
1884
1885     // Cool, this struct is promotable.
1886
1887     structPromotionInfo.canPromote = true;
1888     return true;
1889 }
1890
1891 //--------------------------------------------------------------------------------------------
1892 // TryPromoteValueClassAsPrimitive - Attempt to promote a value type as a primitive type.
1893 //
1894 // Arguments:
1895 //   treeNodes    - Layout tree
1896 //   maxTreeNodes - Size of 'treeNodes'
1897 //   index        - Index of layout tree node corresponding to the value class
1898 //
1899 // Return value:
1900 //   Primitive type to promote the field as.
1901 //
1902 var_types Compiler::StructPromotionHelper::TryPromoteValueClassAsPrimitive(CORINFO_TYPE_LAYOUT_NODE* treeNodes,
1903                                                                            size_t                    maxTreeNodes,
1904                                                                            size_t                    index)
1905 {
1906     assert(index < maxTreeNodes);
1907     CORINFO_TYPE_LAYOUT_NODE& node = treeNodes[index];
1908     assert(node.type == CORINFO_TYPE_VALUECLASS);
1909
1910     if (node.simdTypeHnd != NO_CLASS_HANDLE)
1911     {
1912         const char* namespaceName = nullptr;
1913         const char* className = compiler->info.compCompHnd->getClassNameFromMetadata(node.simdTypeHnd, &namespaceName);
1914
1915 #ifdef FEATURE_SIMD
1916         if (compiler->isRuntimeIntrinsicsNamespace(namespaceName) || compiler->isNumericsNamespace(namespaceName))
1917         {
1918             unsigned    simdSize;
1919             CorInfoType simdBaseJitType = compiler->getBaseJitTypeAndSizeOfSIMDType(node.simdTypeHnd, &simdSize);
1920             // We will only promote fields of SIMD types that fit into a SIMD register.
1921             if (simdBaseJitType != CORINFO_TYPE_UNDEF)
1922             {
1923                 if (compiler->structSizeMightRepresentSIMDType(simdSize))
1924                 {
1925                     return compiler->getSIMDTypeForSize(simdSize);
1926                 }
1927             }
1928         }
1929 #endif
1930
1931 #ifdef TARGET_64BIT
1932         // TODO-Quirk: Vector64 is a SIMD type with one 64-bit field, so when
1933         // compiler->usesSIMDTypes() == false, it used to be promoted as a long
1934         // field.
1935         if (compiler->isRuntimeIntrinsicsNamespace(namespaceName) && (strcmp(className, "Vector64`1") == 0))
1936         {
1937             return TYP_LONG;
1938         }
1939 #endif
1940     }
1941
1942     // Check for a single primitive wrapper.
1943     if (node.numFields != 1)
1944     {
1945         return TYP_UNDEF;
1946     }
1947
1948     if (index + 1 >= maxTreeNodes)
1949     {
1950         return TYP_UNDEF;
1951     }
1952
1953     CORINFO_TYPE_LAYOUT_NODE& primNode = treeNodes[index + 1];
1954
1955     // Do not promote if the field is not a primitive.
1956     // TODO-CQ: We could likely permit recursive primitive wrappers here quite easily.
1957     if (primNode.type == CORINFO_TYPE_VALUECLASS)
1958     {
1959         return TYP_UNDEF;
1960     }
1961
1962     // Do not promote if the single field is not aligned at its natural boundary within
1963     // the struct field.
1964     if (primNode.offset != node.offset)
1965     {
1966         return TYP_UNDEF;
1967     }
1968
1969     // Insist this wrapped field occupies all of its parent storage.
1970     if (primNode.size != node.size)
1971     {
1972         JITDUMP("Promotion blocked: struct contains struct field with one field,"
1973                 " but that field is not the same size as its parent.\n");
1974         return TYP_UNDEF;
1975     }
1976
1977     // Only promote up to pointer sized fields.
1978     // TODO-CQ: Right now we only promote an actual SIMD typed field, which would cause
1979     // a nested SIMD type to fail promotion.
1980     if (primNode.size > TARGET_POINTER_SIZE)
1981     {
1982         JITDUMP("Promotion blocked: struct contains struct field with one field,"
1983                 " but that field has invalid size.\n");
1984         return TYP_UNDEF;
1985     }
1986
1987     if ((primNode.size != TARGET_POINTER_SIZE) && ((node.offset % primNode.size) != 0))
1988     {
1989         JITDUMP("Promotion blocked: struct contains struct field with one field,"
1990                 " but the outer struct offset %u is not a multiple of the inner field size %u.\n",
1991                 node.offset, primNode.size);
1992         return TYP_UNDEF;
1993     }
1994
1995     return JITtype2varType(primNode.type);
1996 }
1997
1998 //--------------------------------------------------------------------------------------------
1999 // AdvanceSubTree - Skip over a tree node and all its children.
2000 //
2001 // Arguments:
2002 //   treeNodes    - array of type layout nodes, stored in preorder.
2003 //   maxTreeNodes - size of 'treeNodes'
2004 //   index        - [in, out] Index pointing to root of subtree to skip.
2005 //
2006 // Remarks:
2007 //   Requires the tree nodes to be stored in preorder (as guaranteed by getTypeLayout).
2008 //
2009 void Compiler::StructPromotionHelper::AdvanceSubTree(CORINFO_TYPE_LAYOUT_NODE* treeNodes,
2010                                                      size_t                    maxTreeNodes,
2011                                                      size_t*                   index)
2012 {
2013     size_t parIndex = *index;
2014     (*index)++;
2015     while ((*index < maxTreeNodes) && (treeNodes[*index].parent >= parIndex))
2016     {
2017         (*index)++;
2018     }
2019 }
2020
2021 //--------------------------------------------------------------------------------------------
2022 // CanPromoteStructVar - checks if the struct can be promoted.
2023 //
2024 // Arguments:
2025 //   lclNum - struct number to check.
2026 //
2027 // Return value:
2028 //   true if the struct var can be promoted.
2029 //
2030 bool Compiler::StructPromotionHelper::CanPromoteStructVar(unsigned lclNum)
2031 {
2032     LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
2033
2034     assert(varTypeIsStruct(varDsc));
2035     assert(!varDsc->lvPromoted); // Don't ask again :)
2036
2037     // If this lclVar is used in a SIMD intrinsic, then we don't want to struct promote it.
2038     // Note, however, that SIMD lclVars that are NOT used in a SIMD intrinsic may be
2039     // profitably promoted.
2040     if (varDsc->lvIsUsedInSIMDIntrinsic())
2041     {
2042         JITDUMP("  struct promotion of V%02u is disabled because lvIsUsedInSIMDIntrinsic()\n", lclNum);
2043         return false;
2044     }
2045
2046     // Reject struct promotion of parameters when -GS stack reordering is enabled
2047     // as we could introduce shadow copies of them.
2048     if (varDsc->lvIsParam && compiler->compGSReorderStackLayout)
2049     {
2050         JITDUMP("  struct promotion of V%02u is disabled because lvIsParam and compGSReorderStackLayout\n", lclNum);
2051         return false;
2052     }
2053
2054     if (varDsc->lvIsParam && compiler->fgNoStructParamPromotion)
2055     {
2056         JITDUMP("  struct promotion of V%02u is disabled by fgNoStructParamPromotion\n", lclNum);
2057         return false;
2058     }
2059
2060     if (!compiler->lvaEnregMultiRegVars && varDsc->lvIsMultiRegArgOrRet())
2061     {
2062         JITDUMP("  struct promotion of V%02u is disabled because lvIsMultiRegArgOrRet()\n", lclNum);
2063         return false;
2064     }
2065
2066     // If the local was exposed at Tier0, we currently have to assume it's aliased for OSR.
2067     //
2068     if (compiler->lvaIsOSRLocal(lclNum) && compiler->info.compPatchpointInfo->IsExposed(lclNum))
2069     {
2070         JITDUMP("  struct promotion of V%02u is disabled because it is an exposed OSR local\n", lclNum);
2071         return false;
2072     }
2073
2074     if (varDsc->IsAddressExposed())
2075     {
2076         JITDUMP("  struct promotion of V%02u is disabled because it has already been marked address exposed\n", lclNum);
2077         return false;
2078     }
2079
2080     if (varDsc->GetLayout()->IsBlockLayout())
2081     {
2082         return false;
2083     }
2084
2085     CORINFO_CLASS_HANDLE typeHnd = varDsc->GetLayout()->GetClassHandle();
2086     assert(typeHnd != NO_CLASS_HANDLE);
2087
2088     bool canPromote = CanPromoteStructType(typeHnd);
2089     if (canPromote && varDsc->lvIsMultiRegArgOrRet())
2090     {
2091         unsigned fieldCnt = structPromotionInfo.fieldCnt;
2092         if (fieldCnt > MAX_MULTIREG_COUNT)
2093         {
2094             canPromote = false;
2095         }
2096 #if defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
2097         else
2098         {
2099             for (unsigned i = 0; canPromote && (i < fieldCnt); i++)
2100             {
2101                 var_types fieldType = structPromotionInfo.fields[i].fldType;
2102                 // Non-HFA structs are always passed in general purpose registers.
2103                 // If there are any floating point fields, don't promote for now.
2104                 // Likewise, since HVA structs are passed in SIMD registers
2105                 // promotion of non FP or SIMD type fields is disallowed.
2106                 // TODO-1stClassStructs: add support in Lowering and prolog generation
2107                 // to enable promoting these types.
2108                 if (varDsc->lvIsParam && (varDsc->lvIsHfa() != varTypeUsesFloatReg(fieldType)))
2109                 {
2110                     canPromote = false;
2111                 }
2112 #if defined(FEATURE_SIMD)
2113                 // If we have a register-passed struct with mixed non-opaque SIMD types (i.e. with defined fields)
2114                 // and non-SIMD types, we don't currently handle that case in the prolog, so we can't promote.
2115                 else if ((fieldCnt > 1) && varTypeIsStruct(fieldType) &&
2116                          (structPromotionInfo.fields[i].fldSIMDTypeHnd != NO_CLASS_HANDLE) &&
2117                          !compiler->isOpaqueSIMDType(structPromotionInfo.fields[i].fldSIMDTypeHnd))
2118                 {
2119                     canPromote = false;
2120                 }
2121 #endif // FEATURE_SIMD
2122             }
2123         }
2124 #elif defined(UNIX_AMD64_ABI)
2125         else
2126         {
2127             SortStructFields();
2128             // Only promote if the field types match the registers, unless we have a single SIMD field.
2129             SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
2130             compiler->eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc);
2131             unsigned regCount = structDesc.eightByteCount;
2132             if ((structPromotionInfo.fieldCnt == 1) && varTypeIsSIMD(structPromotionInfo.fields[0].fldType))
2133             {
2134                 // Allow the case of promoting a single SIMD field, even if there are multiple registers.
2135                 // We will fix this up in the prolog.
2136             }
2137             else if (structPromotionInfo.fieldCnt != regCount)
2138             {
2139                 canPromote = false;
2140             }
2141             else
2142             {
2143                 for (unsigned i = 0; canPromote && (i < regCount); i++)
2144                 {
2145                     lvaStructFieldInfo* fieldInfo = &(structPromotionInfo.fields[i]);
2146                     var_types           fieldType = fieldInfo->fldType;
2147                     // We don't currently support passing SIMD types in registers.
2148                     if (varTypeIsSIMD(fieldType))
2149                     {
2150                         canPromote = false;
2151                     }
2152                     else if (varTypeUsesFloatReg(fieldType) !=
2153                              (structDesc.eightByteClassifications[i] == SystemVClassificationTypeSSE))
2154                     {
2155                         canPromote = false;
2156                     }
2157                 }
2158             }
2159         }
2160 #endif // UNIX_AMD64_ABI
2161     }
2162     return canPromote;
2163 }
2164
2165 //--------------------------------------------------------------------------------------------
2166 // ShouldPromoteStructVar - Should a struct var be promoted if it can be promoted?
2167 // This routine mainly performs profitability checks.  Right now it also has
2168 // some correctness checks due to limitations of down-stream phases.
2169 //
2170 // Arguments:
2171 //   lclNum - struct local number;
2172 //
2173 // Return value:
2174 //   true if the struct should be promoted.
2175 //
2176 bool Compiler::StructPromotionHelper::ShouldPromoteStructVar(unsigned lclNum)
2177 {
2178     LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
2179     assert(varTypeIsStruct(varDsc));
2180     assert(varDsc->GetLayout()->GetClassHandle() == structPromotionInfo.typeHnd);
2181     assert(structPromotionInfo.canPromote);
2182
2183     bool shouldPromote = true;
2184
2185     // We *can* promote; *should* we promote?
2186     // We should only do so if promotion has potential savings.  One source of savings
2187     // is if a field of the struct is accessed, since this access will be turned into
2188     // an access of the corresponding promoted field variable.  Even if there are no
2189     // field accesses, but only block-level operations on the whole struct, if the struct
2190     // has only one or two fields, then doing those block operations field-wise is probably faster
2191     // than doing a whole-variable block operation (e.g., a hardware "copy loop" on x86).
2192     // Struct promotion also provides the following benefits: reduce stack frame size,
2193     // reduce the need for zero init of stack frame and fine grained constant/copy prop.
2194     // Asm diffs indicate that promoting structs up to 3 fields is a net size win.
2195     // So if no fields are accessed independently, and there are four or more fields,
2196     // then do not promote.
2197     //
2198     // TODO: Ideally we would want to consider the impact of whether the struct is
2199     // passed as a parameter or assigned the return value of a call. Because once promoted,
2200     // struct copying is done by field by field assignment instead of a more efficient
2201     // rep.stos or xmm reg based copy.
2202     if (structPromotionInfo.fieldCnt > 3 && !varDsc->lvFieldAccessed)
2203     {
2204         JITDUMP("Not promoting promotable struct local V%02u: #fields = %d, fieldAccessed = %d.\n", lclNum,
2205                 structPromotionInfo.fieldCnt, varDsc->lvFieldAccessed);
2206         shouldPromote = false;
2207     }
2208     else if (varDsc->lvIsMultiRegRet && structPromotionInfo.anySignificantPadding)
2209     {
2210         JITDUMP("Not promoting multi-reg returned struct local V%02u with significant padding.\n", lclNum);
2211         shouldPromote = false;
2212     }
2213 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
2214     else if ((structPromotionInfo.fieldCnt == 2) && (varTypeIsFloating(structPromotionInfo.fields[0].fldType) ||
2215                                                      varTypeIsFloating(structPromotionInfo.fields[1].fldType)))
2216     {
2217         // TODO-LoongArch64 - struct passed by float registers.
2218         JITDUMP("Not promoting promotable struct local V%02u: #fields = %d because it is a struct with "
2219                 "float field(s).\n",
2220                 lclNum, structPromotionInfo.fieldCnt);
2221         shouldPromote = false;
2222     }
2223 #endif // TARGET_LOONGARCH64 || TARGET_RISCV64
2224     else if (varDsc->lvIsParam && !compiler->lvaIsImplicitByRefLocal(lclNum) && !varDsc->lvIsHfa())
2225     {
2226 #if FEATURE_MULTIREG_STRUCT_PROMOTE
2227         // Is this a variable holding a value with exactly two fields passed in
2228         // multiple registers?
2229         if (compiler->lvaIsMultiregStruct(varDsc, compiler->info.compIsVarArgs))
2230         {
2231             if (structPromotionInfo.anySignificantPadding)
2232             {
2233                 JITDUMP("Not promoting multi-reg struct local V%02u with significant padding.\n", lclNum);
2234                 shouldPromote = false;
2235             }
2236             else if ((structPromotionInfo.fieldCnt != 2) &&
2237                      !((structPromotionInfo.fieldCnt == 1) && varTypeIsSIMD(structPromotionInfo.fields[0].fldType)))
2238             {
2239                 JITDUMP("Not promoting multireg struct local V%02u, because lvIsParam is true, #fields != 2 and it's "
2240                         "not a single SIMD.\n",
2241                         lclNum);
2242                 shouldPromote = false;
2243             }
2244 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
2245             else if (varDsc->lvIsSplit)
2246             {
2247                 JITDUMP("Not promoting multireg struct local V%02u, because it is splitted.\n", lclNum);
2248                 shouldPromote = false;
2249             }
2250 #endif // TARGET_LOONGARCH64 || TARGET_RISCV64
2251         }
2252         else
2253 #endif // !FEATURE_MULTIREG_STRUCT_PROMOTE
2254
2255             // TODO-PERF - Implement struct promotion for incoming single-register structs.
2256             //             Also the implementation of jmp uses the 4 byte move to store
2257             //             byte parameters to the stack, so that if we have a byte field
2258             //             with something else occupying the same 4-byte slot, it will
2259             //             overwrite other fields.
2260             if (structPromotionInfo.fieldCnt != 1)
2261         {
2262             JITDUMP("Not promoting promotable struct local V%02u, because lvIsParam is true and #fields = "
2263                     "%d.\n",
2264                     lclNum, structPromotionInfo.fieldCnt);
2265             shouldPromote = false;
2266         }
2267     }
2268     else if ((lclNum == compiler->genReturnLocal) && (structPromotionInfo.fieldCnt > 1))
2269     {
2270         // TODO-1stClassStructs: a temporary solution to keep diffs small, it will be fixed later.
2271         shouldPromote = false;
2272     }
2273 #if defined(DEBUG)
2274     else if (compiler->compPromoteFewerStructs(lclNum))
2275     {
2276         // Do not promote some structs, that can be promoted, to stress promoted/unpromoted moves.
2277         JITDUMP("Not promoting promotable struct local V%02u, because of STRESS_PROMOTE_FEWER_STRUCTS\n", lclNum);
2278         shouldPromote = false;
2279     }
2280 #endif
2281
2282     //
2283     // If the lvRefCnt is zero and we have a struct promoted parameter we can end up with an extra store of
2284     // the incoming register into the stack frame slot.
2285     // In that case, we would like to avoid promortion.
2286     // However we haven't yet computed the lvRefCnt values so we can't do that.
2287     //
2288     CLANG_FORMAT_COMMENT_ANCHOR;
2289
2290     return shouldPromote;
2291 }
2292
2293 //--------------------------------------------------------------------------------------------
2294 // SortStructFields - sort the fields according to the increasing order of the field offset.
2295 //
2296 // Notes:
2297 //   This is needed because the fields need to be pushed on stack (when referenced as a struct) in offset order.
2298 //
2299 void Compiler::StructPromotionHelper::SortStructFields()
2300 {
2301     if (!structPromotionInfo.fieldsSorted)
2302     {
2303         jitstd::sort(structPromotionInfo.fields, structPromotionInfo.fields + structPromotionInfo.fieldCnt,
2304                      [](const lvaStructFieldInfo& lhs, const lvaStructFieldInfo& rhs) {
2305                          return lhs.fldOffset < rhs.fldOffset;
2306                      });
2307         structPromotionInfo.fieldsSorted = true;
2308     }
2309 }
2310
2311 //--------------------------------------------------------------------------------------------
2312 // PromoteStructVar - promote struct variable.
2313 //
2314 // Arguments:
2315 //   lclNum - struct local number;
2316 //
2317 void Compiler::StructPromotionHelper::PromoteStructVar(unsigned lclNum)
2318 {
2319     LclVarDsc* varDsc = compiler->lvaGetDesc(lclNum);
2320
2321     // We should never see a reg-sized non-field-addressed struct here.
2322     assert(!varDsc->lvRegStruct);
2323
2324     assert(varDsc->GetLayout()->GetClassHandle() == structPromotionInfo.typeHnd);
2325     assert(structPromotionInfo.canPromote);
2326
2327     varDsc->lvFieldCnt              = structPromotionInfo.fieldCnt;
2328     varDsc->lvFieldLclStart         = compiler->lvaCount;
2329     varDsc->lvPromoted              = true;
2330     varDsc->lvContainsHoles         = structPromotionInfo.containsHoles;
2331     varDsc->lvAnySignificantPadding = structPromotionInfo.anySignificantPadding;
2332
2333 #ifdef DEBUG
2334     // Don't stress this in LCL_FLD stress.
2335     varDsc->lvKeepType = 1;
2336 #endif
2337
2338 #ifdef DEBUG
2339     if (compiler->verbose)
2340     {
2341         printf("\nPromoting struct local V%02u (%s):", lclNum,
2342                compiler->eeGetClassName(varDsc->GetLayout()->GetClassHandle()));
2343     }
2344 #endif
2345
2346     SortStructFields();
2347
2348     for (unsigned index = 0; index < structPromotionInfo.fieldCnt; ++index)
2349     {
2350         const lvaStructFieldInfo* pFieldInfo = &structPromotionInfo.fields[index];
2351
2352         if (!varTypeUsesIntReg(pFieldInfo->fldType))
2353         {
2354             // Whenever we promote a struct that contains a floating point field
2355             // it's possible we transition from a method that originally only had integer
2356             // local vars to start having FP.  We have to communicate this through this flag
2357             // since LSRA later on will use this flag to determine whether or not to track FP register sets.
2358             compiler->compFloatingPointUsed = true;
2359         }
2360
2361 // Now grab the temp for the field local.
2362
2363 #ifdef DEBUG
2364         char        buf[200];
2365         char        fieldNameBuffer[128];
2366         const char* fieldName =
2367             compiler->eeGetFieldName(pFieldInfo->diagFldHnd, false, fieldNameBuffer, sizeof(fieldNameBuffer));
2368         sprintf_s(buf, sizeof(buf), "field V%02u.%s (fldOffset=0x%x)", lclNum, fieldName, pFieldInfo->fldOffset);
2369
2370         // We need to copy 'buf' as lvaGrabTemp() below caches a copy to its argument.
2371         size_t len  = strlen(buf) + 1;
2372         char*  bufp = compiler->getAllocator(CMK_DebugOnly).allocate<char>(len);
2373         strcpy_s(bufp, len, buf);
2374
2375         if (index > 0)
2376         {
2377             noway_assert(pFieldInfo->fldOffset > (pFieldInfo - 1)->fldOffset);
2378         }
2379 #endif
2380
2381         // Lifetime of field locals might span multiple BBs, so they must be long lifetime temps.
2382         const unsigned varNum = compiler->lvaGrabTemp(false DEBUGARG(bufp));
2383
2384         // lvaGrabTemp can reallocate the lvaTable, so
2385         // refresh the cached varDsc for lclNum.
2386         varDsc = compiler->lvaGetDesc(lclNum);
2387
2388         LclVarDsc* fieldVarDsc           = compiler->lvaGetDesc(varNum);
2389         fieldVarDsc->lvType              = pFieldInfo->fldType;
2390         fieldVarDsc->lvIsStructField     = true;
2391         fieldVarDsc->lvFldOffset         = pFieldInfo->fldOffset;
2392         fieldVarDsc->lvFldOrdinal        = pFieldInfo->fldOrdinal;
2393         fieldVarDsc->lvParentLcl         = lclNum;
2394         fieldVarDsc->lvIsParam           = varDsc->lvIsParam;
2395         fieldVarDsc->lvIsOSRLocal        = varDsc->lvIsOSRLocal;
2396         fieldVarDsc->lvIsOSRExposedLocal = varDsc->lvIsOSRExposedLocal;
2397
2398         // This new local may be the first time we've seen a long typed local.
2399         if (fieldVarDsc->lvType == TYP_LONG)
2400         {
2401             compiler->compLongUsed = true;
2402         }
2403
2404 #if FEATURE_IMPLICIT_BYREFS
2405         // Reset the implicitByRef flag.
2406         fieldVarDsc->lvIsImplicitByRef = 0;
2407 #endif // FEATURE_IMPLICIT_BYREFS
2408
2409         // Do we have a parameter that can be enregistered?
2410         //
2411         if (varDsc->lvIsRegArg)
2412         {
2413             fieldVarDsc->lvIsRegArg = true;
2414             regNumber parentArgReg  = varDsc->GetArgReg();
2415 #if FEATURE_MULTIREG_ARGS
2416             if (!compiler->lvaIsImplicitByRefLocal(lclNum))
2417             {
2418 #ifdef UNIX_AMD64_ABI
2419                 if (varTypeIsSIMD(fieldVarDsc) && (varDsc->lvFieldCnt == 1))
2420                 {
2421                     // This SIMD typed field may be passed in multiple registers.
2422                     fieldVarDsc->SetArgReg(parentArgReg);
2423                     fieldVarDsc->SetOtherArgReg(varDsc->GetOtherArgReg());
2424                 }
2425                 else
2426 #endif // UNIX_AMD64_ABI
2427                 {
2428                     regNumber fieldRegNum;
2429                     if (index == 0)
2430                     {
2431                         fieldRegNum = parentArgReg;
2432                     }
2433                     else if (varDsc->lvIsHfa())
2434                     {
2435                         unsigned regIncrement = fieldVarDsc->lvFldOrdinal;
2436 #ifdef TARGET_ARM
2437                         // TODO: Need to determine if/how to handle split args.
2438                         if (varDsc->GetHfaType() == TYP_DOUBLE)
2439                         {
2440                             regIncrement *= 2;
2441                         }
2442 #endif // TARGET_ARM
2443                         fieldRegNum = (regNumber)(parentArgReg + regIncrement);
2444                     }
2445                     else
2446                     {
2447                         assert(index == 1);
2448                         fieldRegNum = varDsc->GetOtherArgReg();
2449                     }
2450                     fieldVarDsc->SetArgReg(fieldRegNum);
2451                 }
2452             }
2453             else
2454 #endif // FEATURE_MULTIREG_ARGS && defined(FEATURE_SIMD)
2455             {
2456                 fieldVarDsc->SetArgReg(parentArgReg);
2457             }
2458         }
2459
2460 #ifdef FEATURE_SIMD
2461         if (varTypeIsSIMD(pFieldInfo->fldType))
2462         {
2463             // We will not recursively promote this, so mark it as 'lvRegStruct' (note that we wouldn't
2464             // be promoting this if we didn't think it could be enregistered.
2465             fieldVarDsc->lvRegStruct = true;
2466
2467             // SIMD types may be HFAs so we need to set the correct state on
2468             // the promoted fields to get the right ABI treatment in the
2469             // backend.
2470             if (GlobalJitOptions::compFeatureHfa && (pFieldInfo->fldSize <= MAX_PASS_MULTIREG_BYTES))
2471             {
2472                 // hfaType is set to float, double or SIMD type if it is an HFA, otherwise TYP_UNDEF
2473                 var_types hfaType = compiler->GetHfaType(pFieldInfo->fldSIMDTypeHnd);
2474                 if (varTypeIsValidHfaType(hfaType))
2475                 {
2476                     fieldVarDsc->SetHfaType(hfaType);
2477                     fieldVarDsc->lvIsMultiRegArg = (varDsc->lvIsMultiRegArg != 0) && (fieldVarDsc->lvHfaSlots() > 1);
2478                 }
2479             }
2480         }
2481 #endif // FEATURE_SIMD
2482
2483 #ifdef DEBUG
2484         // This temporary should not be converted to a double in stress mode,
2485         // because we introduce assigns to it after the stress conversion
2486         fieldVarDsc->lvKeepType = 1;
2487 #endif
2488     }
2489 }
2490
2491 //--------------------------------------------------------------------------------------------
2492 // lvaGetFieldLocal - returns the local var index for a promoted field in a promoted struct var.
2493 //
2494 // Arguments:
2495 //   varDsc    - the promoted struct var descriptor;
2496 //   fldOffset - field offset in the struct.
2497 //
2498 // Return value:
2499 //   the index of the local that represents this field.
2500 //
2501 unsigned Compiler::lvaGetFieldLocal(const LclVarDsc* varDsc, unsigned int fldOffset)
2502 {
2503     noway_assert(varTypeIsStruct(varDsc));
2504     noway_assert(varDsc->lvPromoted);
2505
2506     for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
2507     {
2508         noway_assert(lvaTable[i].lvIsStructField);
2509         noway_assert(lvaTable[i].lvParentLcl == (unsigned)(varDsc - lvaTable));
2510         if (lvaTable[i].lvFldOffset == fldOffset)
2511         {
2512             return i;
2513         }
2514     }
2515
2516     // This is the not-found error return path, the caller should check for BAD_VAR_NUM
2517     return BAD_VAR_NUM;
2518 }
2519
2520 /*****************************************************************************
2521  *
2522  *  Set the local var "varNum" as address-exposed.
2523  *  If this is a promoted struct, label it's fields the same way.
2524  */
2525
2526 void Compiler::lvaSetVarAddrExposed(unsigned varNum DEBUGARG(AddressExposedReason reason))
2527 {
2528     LclVarDsc* varDsc = lvaGetDesc(varNum);
2529     assert(!varDsc->lvIsStructField);
2530
2531     varDsc->SetAddressExposed(true DEBUGARG(reason));
2532
2533     if (varDsc->lvPromoted)
2534     {
2535         noway_assert(varTypeIsStruct(varDsc));
2536
2537         for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
2538         {
2539             noway_assert(lvaTable[i].lvIsStructField);
2540             lvaTable[i].SetAddressExposed(true DEBUGARG(AddressExposedReason::PARENT_EXPOSED));
2541             lvaSetVarDoNotEnregister(i DEBUGARG(DoNotEnregisterReason::AddrExposed));
2542         }
2543     }
2544
2545     lvaSetVarDoNotEnregister(varNum DEBUGARG(DoNotEnregisterReason::AddrExposed));
2546 }
2547
2548 //------------------------------------------------------------------------
2549 // lvaSetHiddenBufferStructArg: Set the local var "varNum" as hidden buffer struct arg.
2550 //
2551 // Arguments:
2552 //    varNum - the varNum of the local
2553 //
2554 // Notes:
2555 //    Most ABIs "return" large structures via return buffers, where the callee takes an address as the
2556 //    argument, and writes the result to it. This presents a problem: ordinarily, addresses of locals
2557 //    that escape to calls leave the local in question address-exposed. For this very special case of
2558 //    a return buffer, however, it is known that the callee will not do anything with it except write
2559 //    to it, once. As such, we handle addresses of locals that represent return buffers specially: we
2560 //    *do not* mark the local address-exposed and treat the call much like a local store node throughout
2561 //    the compilation.
2562 //
2563 //    TODO-ADDR-Bug: currently, we rely on these locals not being present in call argument lists,
2564 //    outside of the buffer address argument itself, as liveness - currently - treats the location node
2565 //    associated with the address itself as the definition point, and call arguments can be reordered
2566 //    rather arbitrarily. We should fix liveness to treat the call as the definition point instead and
2567 //    enable this optimization for "!lvIsTemp" locals.
2568 //
2569 void Compiler::lvaSetHiddenBufferStructArg(unsigned varNum)
2570 {
2571     LclVarDsc* varDsc = lvaGetDesc(varNum);
2572
2573 #ifdef DEBUG
2574     varDsc->SetHiddenBufferStructArg(true);
2575 #endif
2576
2577     if (varDsc->lvPromoted)
2578     {
2579         noway_assert(varTypeIsStruct(varDsc));
2580
2581         for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
2582         {
2583             noway_assert(lvaTable[i].lvIsStructField);
2584 #ifdef DEBUG
2585             lvaTable[i].SetHiddenBufferStructArg(true);
2586 #endif
2587
2588             lvaSetVarDoNotEnregister(i DEBUGARG(DoNotEnregisterReason::HiddenBufferStructArg));
2589         }
2590     }
2591
2592     lvaSetVarDoNotEnregister(varNum DEBUGARG(DoNotEnregisterReason::HiddenBufferStructArg));
2593 }
2594
2595 //------------------------------------------------------------------------
2596 // lvaSetVarLiveInOutOfHandler: Set the local varNum as being live in and/or out of a handler
2597 //
2598 // Arguments:
2599 //    varNum - the varNum of the local
2600 //
2601 void Compiler::lvaSetVarLiveInOutOfHandler(unsigned varNum)
2602 {
2603     LclVarDsc* varDsc = lvaGetDesc(varNum);
2604
2605     varDsc->lvLiveInOutOfHndlr = 1;
2606
2607     if (varDsc->lvPromoted)
2608     {
2609         noway_assert(varTypeIsStruct(varDsc));
2610
2611         for (unsigned i = varDsc->lvFieldLclStart; i < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++i)
2612         {
2613             noway_assert(lvaTable[i].lvIsStructField);
2614             lvaTable[i].lvLiveInOutOfHndlr = 1;
2615             // For now, only enregister an EH Var if it is a single def and whose refCnt > 1.
2616             if (!lvaEnregEHVars || !lvaTable[i].lvSingleDefRegCandidate || lvaTable[i].lvRefCnt() <= 1)
2617             {
2618                 lvaSetVarDoNotEnregister(i DEBUGARG(DoNotEnregisterReason::LiveInOutOfHandler));
2619             }
2620         }
2621     }
2622
2623     // For now, only enregister an EH Var if it is a single def and whose refCnt > 1.
2624     if (!lvaEnregEHVars || !varDsc->lvSingleDefRegCandidate || varDsc->lvRefCnt() <= 1)
2625     {
2626         lvaSetVarDoNotEnregister(varNum DEBUGARG(DoNotEnregisterReason::LiveInOutOfHandler));
2627     }
2628 #ifdef JIT32_GCENCODER
2629     else if (lvaKeepAliveAndReportThis() && (varNum == info.compThisArg))
2630     {
2631         // For the JIT32_GCENCODER, when lvaKeepAliveAndReportThis is true, we must either keep the "this" pointer
2632         // in the same register for the entire method, or keep it on the stack. If it is EH-exposed, we can't ever
2633         // keep it in a register, since it must also be live on the stack. Therefore, we won't attempt to allocate it.
2634         lvaSetVarDoNotEnregister(varNum DEBUGARG(DoNotEnregisterReason::LiveInOutOfHandler));
2635     }
2636 #endif // JIT32_GCENCODER
2637 }
2638
2639 /*****************************************************************************
2640  *
2641  *  Record that the local var "varNum" should not be enregistered (for one of several reasons.)
2642  */
2643
2644 void Compiler::lvaSetVarDoNotEnregister(unsigned varNum DEBUGARG(DoNotEnregisterReason reason))
2645 {
2646     LclVarDsc* varDsc = lvaGetDesc(varNum);
2647
2648     const bool wasAlreadyMarkedDoNotEnreg = (varDsc->lvDoNotEnregister == 1);
2649     varDsc->lvDoNotEnregister             = 1;
2650
2651 #ifdef DEBUG
2652     if (!wasAlreadyMarkedDoNotEnreg)
2653     {
2654         varDsc->SetDoNotEnregReason(reason);
2655     }
2656
2657     if (verbose)
2658     {
2659         printf("\nLocal V%02u should not be enregistered because: ", varNum);
2660     }
2661
2662     switch (reason)
2663     {
2664         case DoNotEnregisterReason::AddrExposed:
2665             JITDUMP("it is address exposed\n");
2666             assert(varDsc->IsAddressExposed());
2667             break;
2668         case DoNotEnregisterReason::HiddenBufferStructArg:
2669             JITDUMP("it is hidden buffer struct arg\n");
2670             assert(varDsc->IsHiddenBufferStructArg());
2671             break;
2672         case DoNotEnregisterReason::DontEnregStructs:
2673             JITDUMP("struct enregistration is disabled\n");
2674             assert(varTypeIsStruct(varDsc));
2675             break;
2676         case DoNotEnregisterReason::NotRegSizeStruct:
2677             JITDUMP("struct size does not match reg size\n");
2678             assert(varTypeIsStruct(varDsc));
2679             break;
2680         case DoNotEnregisterReason::LocalField:
2681             JITDUMP("was accessed as a local field\n");
2682             break;
2683         case DoNotEnregisterReason::VMNeedsStackAddr:
2684             JITDUMP("VM needs stack addr\n");
2685             break;
2686         case DoNotEnregisterReason::LiveInOutOfHandler:
2687             JITDUMP("live in/out of a handler\n");
2688             varDsc->lvLiveInOutOfHndlr = 1;
2689             break;
2690         case DoNotEnregisterReason::BlockOp:
2691             JITDUMP("written/read in a block op\n");
2692             break;
2693         case DoNotEnregisterReason::IsStructArg:
2694             if (varTypeIsStruct(varDsc))
2695             {
2696                 JITDUMP("it is a struct arg\n");
2697             }
2698             else
2699             {
2700                 JITDUMP("it is reinterpreted as a struct arg\n");
2701             }
2702             break;
2703         case DoNotEnregisterReason::DepField:
2704             JITDUMP("field of a dependently promoted struct\n");
2705             assert(varDsc->lvIsStructField && (lvaGetParentPromotionType(varNum) != PROMOTION_TYPE_INDEPENDENT));
2706             break;
2707         case DoNotEnregisterReason::NoRegVars:
2708             JITDUMP("opts.compFlags & CLFLG_REGVAR is not set\n");
2709             assert(!compEnregLocals());
2710             break;
2711         case DoNotEnregisterReason::MinOptsGC:
2712             JITDUMP("it is a GC Ref and we are compiling MinOpts\n");
2713             assert(!JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet()));
2714             break;
2715 #if !defined(TARGET_64BIT)
2716         case DoNotEnregisterReason::LongParamField:
2717             JITDUMP("it is a decomposed field of a long parameter\n");
2718             break;
2719 #endif
2720 #ifdef JIT32_GCENCODER
2721         case DoNotEnregisterReason::PinningRef:
2722             JITDUMP("pinning ref\n");
2723             assert(varDsc->lvPinned);
2724             break;
2725 #endif
2726         case DoNotEnregisterReason::LclAddrNode:
2727             JITDUMP("LclAddrVar/Fld takes the address of this node\n");
2728             break;
2729
2730         case DoNotEnregisterReason::CastTakesAddr:
2731             JITDUMP("cast takes addr\n");
2732             break;
2733
2734         case DoNotEnregisterReason::StoreBlkSrc:
2735             JITDUMP("the local is used as store block src\n");
2736             break;
2737
2738         case DoNotEnregisterReason::SwizzleArg:
2739             JITDUMP("SwizzleArg\n");
2740             break;
2741
2742         case DoNotEnregisterReason::BlockOpRet:
2743             JITDUMP("return uses a block op\n");
2744             break;
2745
2746         case DoNotEnregisterReason::ReturnSpCheck:
2747             JITDUMP("Used for SP check on return\n");
2748             break;
2749
2750         case DoNotEnregisterReason::CallSpCheck:
2751             JITDUMP("Used for SP check on call\n");
2752             break;
2753
2754         case DoNotEnregisterReason::SimdUserForcesDep:
2755             JITDUMP("Promoted struct used by a SIMD/HWI node\n");
2756             break;
2757
2758         default:
2759             unreached();
2760             break;
2761     }
2762 #endif
2763 }
2764
2765 //------------------------------------------------------------------------
2766 // lvaIsImplicitByRefLocal: Is the local an "implicit byref" parameter?
2767 //
2768 // We term structs passed via pointers to shadow copies "implicit byrefs".
2769 // They are used on Windows x64 for structs 3, 5, 6, 7, > 8 bytes in size,
2770 // and on ARM64/LoongArch64 for structs larger than 16 bytes.
2771 //
2772 // They are "byrefs" because the VM sometimes uses memory allocated on the
2773 // GC heap for the shadow copies.
2774 //
2775 // Arguments:
2776 //    lclNum - The local in question
2777 //
2778 // Return Value:
2779 //    Whether "lclNum" refers to an implicit byref.
2780 //
2781 bool Compiler::lvaIsImplicitByRefLocal(unsigned lclNum) const
2782 {
2783 #if FEATURE_IMPLICIT_BYREFS
2784     LclVarDsc* varDsc = lvaGetDesc(lclNum);
2785     if (varDsc->lvIsImplicitByRef)
2786     {
2787         assert(varDsc->lvIsParam);
2788
2789         assert(varTypeIsStruct(varDsc) || (varDsc->TypeGet() == TYP_BYREF));
2790         return true;
2791     }
2792 #endif // FEATURE_IMPLICIT_BYREFS
2793     return false;
2794 }
2795
2796 //------------------------------------------------------------------------
2797 // lvaIsLocalImplicitlyAccessedByRef: Will this local be accessed indirectly?
2798 //
2799 // Arguments:
2800 //    lclNum - The number of local in question
2801 //
2802 // Return Value:
2803 //    If "lclNum" is an implicit byref parameter, or its dependently promoted
2804 //    field, "true", otherwise, "false".
2805 //
2806 // Notes:
2807 //   This method is only meaningful before the locals have been morphed into
2808 //   explicit indirections.
2809 //
2810 bool Compiler::lvaIsLocalImplicitlyAccessedByRef(unsigned lclNum) const
2811 {
2812     if (lvaGetDesc(lclNum)->lvIsStructField)
2813     {
2814         return lvaIsImplicitByRefLocal(lvaGetDesc(lclNum)->lvParentLcl);
2815     }
2816
2817     return lvaIsImplicitByRefLocal(lclNum);
2818 }
2819
2820 // Returns true if this local var is a multireg struct.
2821 // TODO-Throughput: This does a lookup on the class handle, and in the outgoing arg context
2822 // this information is already available on the CallArgABIInformation, and shouldn't need to be
2823 // recomputed.
2824 //
2825 bool Compiler::lvaIsMultiregStruct(LclVarDsc* varDsc, bool isVarArg)
2826 {
2827     if (varTypeIsStruct(varDsc->TypeGet()))
2828     {
2829         CORINFO_CLASS_HANDLE clsHnd = varDsc->GetLayout()->GetClassHandle();
2830         structPassingKind    howToPassStruct;
2831
2832         var_types type = getArgTypeForStruct(clsHnd, &howToPassStruct, isVarArg, varDsc->lvExactSize());
2833
2834         if (howToPassStruct == SPK_ByValueAsHfa)
2835         {
2836             assert(type == TYP_STRUCT);
2837             return true;
2838         }
2839
2840 #if defined(UNIX_AMD64_ABI) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
2841         if (howToPassStruct == SPK_ByValue)
2842         {
2843             assert(type == TYP_STRUCT);
2844             return true;
2845         }
2846 #endif
2847     }
2848     return false;
2849 }
2850
2851 //------------------------------------------------------------------------
2852 // lvaSetStruct: Set the type of a local to a struct, given a layout.
2853 //
2854 // Arguments:
2855 //    varNum              - The local
2856 //    layout              - The layout
2857 //    unsafeValueClsCheck - Whether to check if we should potentially emit a GS cookie due to this local.
2858 //
2859 void Compiler::lvaSetStruct(unsigned varNum, ClassLayout* layout, bool unsafeValueClsCheck)
2860 {
2861     LclVarDsc* varDsc = lvaGetDesc(varNum);
2862
2863     // Set the type and associated info if we haven't already set it.
2864     if (varDsc->lvType == TYP_UNDEF)
2865     {
2866         varDsc->lvType = TYP_STRUCT;
2867     }
2868     if (varDsc->GetLayout() == nullptr)
2869     {
2870         varDsc->SetLayout(layout);
2871
2872         if (layout->IsValueClass())
2873         {
2874             varDsc->lvType = layout->GetType();
2875
2876 #if FEATURE_IMPLICIT_BYREFS
2877             // Mark implicit byref struct parameters
2878             if (varDsc->lvIsParam && !varDsc->lvIsStructField)
2879             {
2880                 structPassingKind howToReturnStruct;
2881                 getArgTypeForStruct(layout->GetClassHandle(), &howToReturnStruct, this->info.compIsVarArgs,
2882                                     varDsc->lvExactSize());
2883
2884                 if (howToReturnStruct == SPK_ByReference)
2885                 {
2886                     JITDUMP("Marking V%02i as a byref parameter\n", varNum);
2887                     varDsc->lvIsImplicitByRef = 1;
2888                 }
2889             }
2890 #endif // FEATURE_IMPLICIT_BYREFS
2891
2892             // For structs that are small enough, we check and set HFA element type
2893             if (GlobalJitOptions::compFeatureHfa && (layout->GetSize() <= MAX_PASS_MULTIREG_BYTES))
2894             {
2895                 // hfaType is set to float, double or SIMD type if it is an HFA, otherwise TYP_UNDEF
2896                 var_types hfaType = GetHfaType(layout->GetClassHandle());
2897                 if (varTypeIsValidHfaType(hfaType))
2898                 {
2899                     varDsc->SetHfaType(hfaType);
2900
2901                     // hfa variables can never contain GC pointers
2902                     assert(!layout->HasGCPtr());
2903                     // The size of this struct should be evenly divisible by 4 or 8
2904                     assert((varDsc->lvExactSize() % genTypeSize(hfaType)) == 0);
2905                     // The number of elements in the HFA should fit into our MAX_ARG_REG_COUNT limit
2906                     assert((varDsc->lvExactSize() / genTypeSize(hfaType)) <= MAX_ARG_REG_COUNT);
2907                 }
2908             }
2909         }
2910     }
2911     else
2912     {
2913         assert(ClassLayout::AreCompatible(varDsc->GetLayout(), layout));
2914         // Inlining could replace a canon struct type with an exact one.
2915         varDsc->SetLayout(layout);
2916         assert(layout->IsBlockLayout() || (layout->GetSize() != 0));
2917     }
2918
2919     if (!layout->IsBlockLayout())
2920     {
2921 #ifndef TARGET_64BIT
2922         bool fDoubleAlignHint = false;
2923 #ifdef TARGET_X86
2924         fDoubleAlignHint = true;
2925 #endif
2926
2927         if (info.compCompHnd->getClassAlignmentRequirement(layout->GetClassHandle(), fDoubleAlignHint) == 8)
2928         {
2929 #ifdef DEBUG
2930             if (verbose)
2931             {
2932                 printf("Marking struct in V%02i with double align flag\n", varNum);
2933             }
2934 #endif
2935             varDsc->lvStructDoubleAlign = 1;
2936         }
2937 #endif // not TARGET_64BIT
2938
2939         // Check whether this local is an unsafe value type and requires GS cookie protection.
2940         // GS checks require the stack to be re-ordered, which can't be done with EnC.
2941         if (unsafeValueClsCheck)
2942         {
2943             unsigned classAttribs = info.compCompHnd->getClassAttribs(layout->GetClassHandle());
2944
2945             if ((classAttribs & CORINFO_FLG_UNSAFE_VALUECLASS) && !opts.compDbgEnC)
2946             {
2947                 setNeedsGSSecurityCookie();
2948                 compGSReorderStackLayout = true;
2949                 varDsc->lvIsUnsafeBuffer = true;
2950             }
2951         }
2952
2953 #ifdef DEBUG
2954         if (JitConfig.EnableExtraSuperPmiQueries())
2955         {
2956             makeExtraStructQueries(layout->GetClassHandle(), 2);
2957         }
2958 #endif // DEBUG
2959     }
2960 }
2961
2962 //------------------------------------------------------------------------
2963 // lvaSetStruct: Set the type of a local to a struct, given its type handle.
2964 //
2965 // Arguments:
2966 //    varNum              - The local
2967 //    typeHnd             - The type handle
2968 //    unsafeValueClsCheck - Whether to check if we should potentially emit a GS cookie due to this local.
2969 //
2970 void Compiler::lvaSetStruct(unsigned varNum, CORINFO_CLASS_HANDLE typeHnd, bool unsafeValueClsCheck)
2971 {
2972     lvaSetStruct(varNum, typGetObjLayout(typeHnd), unsafeValueClsCheck);
2973 }
2974
2975 #ifdef DEBUG
2976 //------------------------------------------------------------------------
2977 // makeExtraStructQueries: Query the information for the given struct handle.
2978 //
2979 // Arguments:
2980 //    structHandle -- The handle for the struct type we're querying.
2981 //    level        -- How many more levels to recurse.
2982 //
2983 void Compiler::makeExtraStructQueries(CORINFO_CLASS_HANDLE structHandle, int level)
2984 {
2985     if (level <= 0)
2986     {
2987         return;
2988     }
2989     assert(structHandle != NO_CLASS_HANDLE);
2990     (void)typGetObjLayout(structHandle);
2991     DWORD typeFlags = info.compCompHnd->getClassAttribs(structHandle);
2992
2993     unsigned const fieldCnt = info.compCompHnd->getClassNumInstanceFields(structHandle);
2994     impNormStructType(structHandle);
2995 #ifdef TARGET_ARMARCH
2996     GetHfaType(structHandle);
2997 #endif
2998
2999     // In a lambda since this requires a lot of stack and this function is recursive.
3000     auto queryLayout = [this, structHandle]() {
3001         CORINFO_TYPE_LAYOUT_NODE nodes[256];
3002         size_t                   numNodes = ArrLen(nodes);
3003         info.compCompHnd->getTypeLayout(structHandle, nodes, &numNodes);
3004     };
3005     queryLayout();
3006
3007     // Bypass fetching instance fields of ref classes for now,
3008     // as it requires traversing the class hierarchy.
3009     //
3010     if ((typeFlags & CORINFO_FLG_VALUECLASS) == 0)
3011     {
3012         return;
3013     }
3014
3015     // In R2R we cannot query arbitrary information about struct fields, so
3016     // skip it there. Note that the getTypeLayout call above is enough to cover
3017     // us for promotion at least.
3018     if (!opts.IsReadyToRun())
3019     {
3020         for (unsigned int i = 0; i < fieldCnt; i++)
3021         {
3022             CORINFO_FIELD_HANDLE fieldHandle      = info.compCompHnd->getFieldInClass(structHandle, i);
3023             unsigned             fldOffset        = info.compCompHnd->getFieldOffset(fieldHandle);
3024             CORINFO_CLASS_HANDLE fieldClassHandle = NO_CLASS_HANDLE;
3025             CorInfoType          fieldCorType     = info.compCompHnd->getFieldType(fieldHandle, &fieldClassHandle);
3026             var_types            fieldVarType     = JITtype2varType(fieldCorType);
3027             if (fieldClassHandle != NO_CLASS_HANDLE)
3028             {
3029                 if (varTypeIsStruct(fieldVarType))
3030                 {
3031                     makeExtraStructQueries(fieldClassHandle, level - 1);
3032                 }
3033             }
3034         }
3035     }
3036 }
3037 #endif // DEBUG
3038
3039 //------------------------------------------------------------------------
3040 // lvaSetStructUsedAsVarArg: update hfa information for vararg struct args
3041 //
3042 // Arguments:
3043 //    varNum   -- number of the variable
3044 //
3045 // Notes:
3046 //    This only affects arm64 varargs on windows where we need to pass
3047 //    hfa arguments as if they are not HFAs.
3048 //
3049 //    This function should only be called if the struct is used in a varargs
3050 //    method.
3051
3052 void Compiler::lvaSetStructUsedAsVarArg(unsigned varNum)
3053 {
3054     if (GlobalJitOptions::compFeatureHfa && TargetOS::IsWindows)
3055     {
3056 #if defined(TARGET_ARM64)
3057         LclVarDsc* varDsc = lvaGetDesc(varNum);
3058         // For varargs methods incoming and outgoing arguments should not be treated
3059         // as HFA.
3060         varDsc->SetHfaType(TYP_UNDEF);
3061 #endif // defined(TARGET_ARM64)
3062     }
3063 }
3064
3065 //------------------------------------------------------------------------
3066 // lvaSetClass: set class information for a local var.
3067 //
3068 // Arguments:
3069 //    varNum -- number of the variable
3070 //    clsHnd -- class handle to use in set or update
3071 //    isExact -- true if class is known exactly
3072 //
3073 // Notes:
3074 //    varNum must not already have a ref class handle.
3075
3076 void Compiler::lvaSetClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool isExact)
3077 {
3078     noway_assert(varNum < lvaCount);
3079
3080     if (clsHnd != NO_CLASS_HANDLE && !isExact && JitConfig.JitEnableExactDevirtualization())
3081     {
3082         CORINFO_CLASS_HANDLE exactClass;
3083         if (info.compCompHnd->getExactClasses(clsHnd, 1, &exactClass) == 1)
3084         {
3085             isExact = true;
3086             clsHnd  = exactClass;
3087         }
3088     }
3089
3090     // Else we should have a type handle.
3091     assert(clsHnd != nullptr);
3092
3093     LclVarDsc* varDsc = lvaGetDesc(varNum);
3094     assert(varDsc->lvType == TYP_REF);
3095
3096     // We should not have any ref type information for this var.
3097     assert(varDsc->lvClassHnd == NO_CLASS_HANDLE);
3098     assert(!varDsc->lvClassIsExact);
3099
3100     JITDUMP("\nlvaSetClass: setting class for V%02i to (%p) %s %s\n", varNum, dspPtr(clsHnd), eeGetClassName(clsHnd),
3101             isExact ? " [exact]" : "");
3102
3103     varDsc->lvClassHnd     = clsHnd;
3104     varDsc->lvClassIsExact = isExact;
3105 }
3106
3107 //------------------------------------------------------------------------
3108 // lvaSetClass: set class information for a local var from a tree or stack type
3109 //
3110 // Arguments:
3111 //    varNum -- number of the variable. Must be a single def local
3112 //    tree  -- tree establishing the variable's value
3113 //    stackHnd -- handle for the type from the evaluation stack
3114 //
3115 // Notes:
3116 //    Preferentially uses the tree's type, when available. Since not all
3117 //    tree kinds can track ref types, the stack type is used as a
3118 //    fallback. If there is no stack type, then the class is set to object.
3119
3120 void Compiler::lvaSetClass(unsigned varNum, GenTree* tree, CORINFO_CLASS_HANDLE stackHnd)
3121 {
3122     bool                 isExact   = false;
3123     bool                 isNonNull = false;
3124     CORINFO_CLASS_HANDLE clsHnd    = gtGetClassHandle(tree, &isExact, &isNonNull);
3125
3126     if (clsHnd != nullptr)
3127     {
3128         lvaSetClass(varNum, clsHnd, isExact);
3129     }
3130     else if (stackHnd != nullptr)
3131     {
3132         lvaSetClass(varNum, stackHnd);
3133     }
3134     else
3135     {
3136         lvaSetClass(varNum, impGetObjectClass());
3137     }
3138 }
3139
3140 //------------------------------------------------------------------------
3141 // lvaUpdateClass: update class information for a local var.
3142 //
3143 // Arguments:
3144 //    varNum -- number of the variable
3145 //    clsHnd -- class handle to use in set or update
3146 //    isExact -- true if class is known exactly
3147 //
3148 // Notes:
3149 //
3150 //    This method models the type update rule for an assignment.
3151 //
3152 //    Updates currently should only happen for single-def user args or
3153 //    locals, when we are processing the expression actually being
3154 //    used to initialize the local (or inlined arg). The update will
3155 //    change the local from the declared type to the type of the
3156 //    initial value.
3157 //
3158 //    These updates should always *improve* what we know about the
3159 //    type, that is making an inexact type exact, or changing a type
3160 //    to some subtype. However the jit lacks precise type information
3161 //    for shared code, so ensuring this is so is currently not
3162 //    possible.
3163
3164 void Compiler::lvaUpdateClass(unsigned varNum, CORINFO_CLASS_HANDLE clsHnd, bool isExact)
3165 {
3166     assert(varNum < lvaCount);
3167
3168     // Else we should have a class handle to consider
3169     assert(clsHnd != nullptr);
3170
3171     LclVarDsc* varDsc = lvaGetDesc(varNum);
3172     assert(varDsc->lvType == TYP_REF);
3173
3174     // We should already have a class
3175     assert(varDsc->lvClassHnd != NO_CLASS_HANDLE);
3176
3177     // We should only be updating classes for single-def locals.
3178     assert(varDsc->lvSingleDef);
3179
3180     // Now see if we should update.
3181     //
3182     // New information may not always be "better" so do some
3183     // simple analysis to decide if the update is worthwhile.
3184     const bool isNewClass   = (clsHnd != varDsc->lvClassHnd);
3185     bool       shouldUpdate = false;
3186
3187     // Are we attempting to update the class? Only check this when we have
3188     // an new type and the existing class is inexact... we should not be
3189     // updating exact classes.
3190     if (!varDsc->lvClassIsExact && isNewClass)
3191     {
3192         shouldUpdate = !!info.compCompHnd->isMoreSpecificType(varDsc->lvClassHnd, clsHnd);
3193     }
3194     // Else are we attempting to update exactness?
3195     else if (isExact && !varDsc->lvClassIsExact && !isNewClass)
3196     {
3197         shouldUpdate = true;
3198     }
3199
3200 #if DEBUG
3201     if (isNewClass || (isExact != varDsc->lvClassIsExact))
3202     {
3203         JITDUMP("\nlvaUpdateClass:%s Updating class for V%02u", shouldUpdate ? "" : " NOT", varNum);
3204         JITDUMP(" from (%p) %s%s", dspPtr(varDsc->lvClassHnd), eeGetClassName(varDsc->lvClassHnd),
3205                 varDsc->lvClassIsExact ? " [exact]" : "");
3206         JITDUMP(" to (%p) %s%s\n", dspPtr(clsHnd), eeGetClassName(clsHnd), isExact ? " [exact]" : "");
3207     }
3208 #endif // DEBUG
3209
3210     if (shouldUpdate)
3211     {
3212         varDsc->lvClassHnd     = clsHnd;
3213         varDsc->lvClassIsExact = isExact;
3214
3215 #if DEBUG
3216         // Note we've modified the type...
3217         varDsc->lvClassInfoUpdated = true;
3218 #endif // DEBUG
3219     }
3220
3221     return;
3222 }
3223
3224 //------------------------------------------------------------------------
3225 // lvaUpdateClass: Uupdate class information for a local var from a tree
3226 //  or stack type
3227 //
3228 // Arguments:
3229 //    varNum -- number of the variable. Must be a single def local
3230 //    tree  -- tree establishing the variable's value
3231 //    stackHnd -- handle for the type from the evaluation stack
3232 //
3233 // Notes:
3234 //    Preferentially uses the tree's type, when available. Since not all
3235 //    tree kinds can track ref types, the stack type is used as a
3236 //    fallback.
3237
3238 void Compiler::lvaUpdateClass(unsigned varNum, GenTree* tree, CORINFO_CLASS_HANDLE stackHnd)
3239 {
3240     bool                 isExact   = false;
3241     bool                 isNonNull = false;
3242     CORINFO_CLASS_HANDLE clsHnd    = gtGetClassHandle(tree, &isExact, &isNonNull);
3243
3244     if (clsHnd != nullptr)
3245     {
3246         lvaUpdateClass(varNum, clsHnd, isExact);
3247     }
3248     else if (stackHnd != nullptr)
3249     {
3250         lvaUpdateClass(varNum, stackHnd);
3251     }
3252 }
3253
3254 //------------------------------------------------------------------------
3255 // lvaLclSize: returns size of a local variable, in bytes
3256 //
3257 // Arguments:
3258 //    varNum -- variable to query
3259 //
3260 // Returns:
3261 //    Number of bytes needed on the frame for such a local.
3262 //
3263 unsigned Compiler::lvaLclSize(unsigned varNum)
3264 {
3265     assert(varNum < lvaCount);
3266
3267     var_types varType = lvaTable[varNum].TypeGet();
3268
3269     if (varType == TYP_STRUCT)
3270     {
3271         return lvaTable[varNum].lvSize();
3272     }
3273
3274 #ifdef TARGET_64BIT
3275     // We only need this Quirk for TARGET_64BIT
3276     if (lvaTable[varNum].lvQuirkToLong)
3277     {
3278         noway_assert(lvaTable[varNum].IsAddressExposed());
3279         return genTypeStSz(TYP_LONG) * sizeof(int); // return 8  (2 * 4)
3280     }
3281 #endif
3282     return genTypeStSz(varType) * sizeof(int);
3283 }
3284
3285 //
3286 // Return the exact width of local variable "varNum" -- the number of bytes
3287 // you'd need to copy in order to overwrite the value.
3288 //
3289 unsigned Compiler::lvaLclExactSize(unsigned varNum)
3290 {
3291     assert(varNum < lvaCount);
3292     return lvaGetDesc(varNum)->lvExactSize();
3293 }
3294
3295 // LclVarDsc "less" comparer used to compare the weight of two locals, when optimizing for small code.
3296 class LclVarDsc_SmallCode_Less
3297 {
3298     const LclVarDsc* m_lvaTable;
3299     RefCountState    m_rcs;
3300     INDEBUG(unsigned m_lvaCount;)
3301
3302 public:
3303     LclVarDsc_SmallCode_Less(const LclVarDsc* lvaTable, RefCountState rcs DEBUGARG(unsigned lvaCount))
3304         : m_lvaTable(lvaTable)
3305         , m_rcs(rcs)
3306 #ifdef DEBUG
3307         , m_lvaCount(lvaCount)
3308 #endif
3309     {
3310     }
3311
3312     bool operator()(unsigned n1, unsigned n2)
3313     {
3314         assert(n1 < m_lvaCount);
3315         assert(n2 < m_lvaCount);
3316
3317         const LclVarDsc* dsc1 = &m_lvaTable[n1];
3318         const LclVarDsc* dsc2 = &m_lvaTable[n2];
3319
3320         // We should not be sorting untracked variables
3321         assert(dsc1->lvTracked);
3322         assert(dsc2->lvTracked);
3323         // We should not be sorting after registers have been allocated
3324         assert(!dsc1->lvRegister);
3325         assert(!dsc2->lvRegister);
3326
3327         unsigned weight1 = dsc1->lvRefCnt(m_rcs);
3328         unsigned weight2 = dsc2->lvRefCnt(m_rcs);
3329
3330 #ifndef TARGET_ARM
3331         // ARM-TODO: this was disabled for ARM under !FEATURE_FP_REGALLOC; it was probably a left-over from
3332         // legacy backend. It should be enabled and verified.
3333
3334         // Force integer candidates to sort above float candidates.
3335         const bool isFloat1 = isFloatRegType(dsc1->lvType);
3336         const bool isFloat2 = isFloatRegType(dsc2->lvType);
3337
3338         if (isFloat1 != isFloat2)
3339         {
3340             if ((weight2 != 0) && isFloat1)
3341             {
3342                 return false;
3343             }
3344
3345             if ((weight1 != 0) && isFloat2)
3346             {
3347                 return true;
3348             }
3349         }
3350 #endif
3351
3352         if (weight1 != weight2)
3353         {
3354             return weight1 > weight2;
3355         }
3356
3357         // If the weighted ref counts are different then use their difference.
3358         if (dsc1->lvRefCntWtd() != dsc2->lvRefCntWtd())
3359         {
3360             return dsc1->lvRefCntWtd() > dsc2->lvRefCntWtd();
3361         }
3362
3363         // We have equal ref counts and weighted ref counts.
3364         // Break the tie by:
3365         //   - Increasing the weight by 2   if we are a register arg.
3366         //   - Increasing the weight by 0.5 if we are a GC type.
3367         //
3368         // Review: seems odd that this is mixing counts and weights.
3369
3370         if (weight1 != 0)
3371         {
3372             if (dsc1->lvIsRegArg)
3373             {
3374                 weight1 += 2 * BB_UNITY_WEIGHT_UNSIGNED;
3375             }
3376
3377             if (varTypeIsGC(dsc1->TypeGet()))
3378             {
3379                 weight1 += BB_UNITY_WEIGHT_UNSIGNED / 2;
3380             }
3381         }
3382
3383         if (weight2 != 0)
3384         {
3385             if (dsc2->lvIsRegArg)
3386             {
3387                 weight2 += 2 * BB_UNITY_WEIGHT_UNSIGNED;
3388             }
3389
3390             if (varTypeIsGC(dsc2->TypeGet()))
3391             {
3392                 weight2 += BB_UNITY_WEIGHT_UNSIGNED / 2;
3393             }
3394         }
3395
3396         if (weight1 != weight2)
3397         {
3398             return weight1 > weight2;
3399         }
3400
3401         // To achieve a stable sort we use the LclNum (by way of the pointer address).
3402         return dsc1 < dsc2;
3403     }
3404 };
3405
3406 // LclVarDsc "less" comparer used to compare the weight of two locals, when optimizing for blended code.
3407 class LclVarDsc_BlendedCode_Less
3408 {
3409     const LclVarDsc* m_lvaTable;
3410     RefCountState    m_rcs;
3411     INDEBUG(unsigned m_lvaCount;)
3412
3413 public:
3414     LclVarDsc_BlendedCode_Less(const LclVarDsc* lvaTable, RefCountState rcs DEBUGARG(unsigned lvaCount))
3415         : m_lvaTable(lvaTable)
3416         , m_rcs(rcs)
3417 #ifdef DEBUG
3418         , m_lvaCount(lvaCount)
3419 #endif
3420     {
3421     }
3422
3423     bool operator()(unsigned n1, unsigned n2)
3424     {
3425         assert(n1 < m_lvaCount);
3426         assert(n2 < m_lvaCount);
3427
3428         const LclVarDsc* dsc1 = &m_lvaTable[n1];
3429         const LclVarDsc* dsc2 = &m_lvaTable[n2];
3430
3431         // We should not be sorting untracked variables
3432         assert(dsc1->lvTracked);
3433         assert(dsc2->lvTracked);
3434         // We should not be sorting after registers have been allocated
3435         assert(!dsc1->lvRegister);
3436         assert(!dsc2->lvRegister);
3437
3438         weight_t weight1 = dsc1->lvRefCntWtd(m_rcs);
3439         weight_t weight2 = dsc2->lvRefCntWtd(m_rcs);
3440
3441 #ifndef TARGET_ARM
3442         // ARM-TODO: this was disabled for ARM under !FEATURE_FP_REGALLOC; it was probably a left-over from
3443         // legacy backend. It should be enabled and verified.
3444
3445         // Force integer candidates to sort above float candidates.
3446         const bool isFloat1 = isFloatRegType(dsc1->lvType);
3447         const bool isFloat2 = isFloatRegType(dsc2->lvType);
3448
3449         if (isFloat1 != isFloat2)
3450         {
3451             if (!Compiler::fgProfileWeightsEqual(weight2, 0) && isFloat1)
3452             {
3453                 return false;
3454             }
3455
3456             if (!Compiler::fgProfileWeightsEqual(weight1, 0) && isFloat2)
3457             {
3458                 return true;
3459             }
3460         }
3461 #endif
3462
3463         if (!Compiler::fgProfileWeightsEqual(weight1, 0) && dsc1->lvIsRegArg)
3464         {
3465             weight1 += 2 * BB_UNITY_WEIGHT;
3466         }
3467
3468         if (!Compiler::fgProfileWeightsEqual(weight2, 0) && dsc2->lvIsRegArg)
3469         {
3470             weight2 += 2 * BB_UNITY_WEIGHT;
3471         }
3472
3473         if (!Compiler::fgProfileWeightsEqual(weight1, weight2))
3474         {
3475             return weight1 > weight2;
3476         }
3477
3478         // If the weighted ref counts are different then try the unweighted ref counts.
3479         if (dsc1->lvRefCnt(m_rcs) != dsc2->lvRefCnt(m_rcs))
3480         {
3481             return dsc1->lvRefCnt(m_rcs) > dsc2->lvRefCnt(m_rcs);
3482         }
3483
3484         // If one is a GC type and the other is not the GC type wins.
3485         if (varTypeIsGC(dsc1->TypeGet()) != varTypeIsGC(dsc2->TypeGet()))
3486         {
3487             return varTypeIsGC(dsc1->TypeGet());
3488         }
3489
3490         // To achieve a stable sort we use the LclNum (by way of the pointer address).
3491         return dsc1 < dsc2;
3492     }
3493 };
3494
3495 /*****************************************************************************
3496  *
3497  *  Sort the local variable table by refcount and assign tracking indices.
3498  */
3499
3500 void Compiler::lvaSortByRefCount()
3501 {
3502     lvaTrackedCount             = 0;
3503     lvaTrackedCountInSizeTUnits = 0;
3504
3505 #ifdef DEBUG
3506     VarSetOps::AssignNoCopy(this, lvaTrackedVars, VarSetOps::MakeEmpty(this));
3507 #endif
3508
3509     if (lvaCount == 0)
3510     {
3511         return;
3512     }
3513
3514     /* We'll sort the variables by ref count - allocate the sorted table */
3515
3516     if (lvaTrackedToVarNumSize < lvaCount)
3517     {
3518         lvaTrackedToVarNumSize = lvaCount;
3519         lvaTrackedToVarNum     = new (getAllocator(CMK_LvaTable)) unsigned[lvaTrackedToVarNumSize];
3520     }
3521
3522     unsigned  trackedCandidateCount = 0;
3523     unsigned* trackedCandidates     = lvaTrackedToVarNum;
3524
3525     // Fill in the table used for sorting
3526
3527     for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++)
3528     {
3529         LclVarDsc* varDsc = lvaGetDesc(lclNum);
3530
3531         // Start by assuming that the variable will be tracked.
3532         varDsc->lvTracked = 1;
3533         INDEBUG(varDsc->lvTrackedWithoutIndex = 0);
3534
3535         if (varDsc->lvRefCnt(lvaRefCountState) == 0)
3536         {
3537             // Zero ref count, make this untracked.
3538             varDsc->lvTracked = 0;
3539             varDsc->setLvRefCntWtd(0, lvaRefCountState);
3540         }
3541
3542 #if !defined(TARGET_64BIT)
3543         if (varTypeIsLong(varDsc) && varDsc->lvPromoted)
3544         {
3545             varDsc->lvTracked = 0;
3546         }
3547 #endif // !defined(TARGET_64BIT)
3548
3549         // Variables that are address-exposed, and all struct locals, are never enregistered, or tracked.
3550         // (The struct may be promoted, and its field variables enregistered/tracked, or the VM may "normalize"
3551         // its type so that its not seen by the JIT as a struct.)
3552         // Pinned variables may not be tracked (a condition of the GCInfo representation)
3553         // or enregistered, on x86 -- it is believed that we can enregister pinned (more properly, "pinning")
3554         // references when using the general GC encoding.
3555         if (varDsc->IsAddressExposed())
3556         {
3557             varDsc->lvTracked = 0;
3558             assert(varDsc->lvType != TYP_STRUCT ||
3559                    varDsc->lvDoNotEnregister); // For structs, should have set this when we set m_addrExposed.
3560         }
3561         if (varTypeIsStruct(varDsc))
3562         {
3563             // Promoted structs will never be considered for enregistration anyway,
3564             // and the DoNotEnregister flag was used to indicate whether promotion was
3565             // independent or dependent.
3566             if (varDsc->lvPromoted)
3567             {
3568                 varDsc->lvTracked = 0;
3569             }
3570             else if (!varDsc->IsEnregisterableType())
3571             {
3572                 lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::NotRegSizeStruct));
3573             }
3574             else if (varDsc->lvType == TYP_STRUCT)
3575             {
3576                 if (!varDsc->lvRegStruct && !compEnregStructLocals())
3577                 {
3578                     lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::DontEnregStructs));
3579                 }
3580                 else if (varDsc->lvIsMultiRegArgOrRet())
3581                 {
3582                     // Prolog and return generators do not support SIMD<->general register moves.
3583                     lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::IsStructArg));
3584                 }
3585 #if defined(TARGET_ARM)
3586                 else if (varDsc->lvIsParam)
3587                 {
3588                     // On arm we prespill all struct args,
3589                     // TODO-Arm-CQ: keep them in registers, it will need a fix
3590                     // to "On the ARM we will spill any incoming struct args" logic in codegencommon.
3591                     lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::IsStructArg));
3592                 }
3593 #endif // TARGET_ARM
3594             }
3595         }
3596         if (varDsc->lvIsStructField && (lvaGetParentPromotionType(lclNum) != PROMOTION_TYPE_INDEPENDENT))
3597         {
3598             lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::DepField));
3599         }
3600         if (varDsc->lvPinned)
3601         {
3602             varDsc->lvTracked = 0;
3603 #ifdef JIT32_GCENCODER
3604             lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::PinningRef));
3605 #endif
3606         }
3607         if (opts.MinOpts() && !JitConfig.JitMinOptsTrackGCrefs() && varTypeIsGC(varDsc->TypeGet()))
3608         {
3609             varDsc->lvTracked = 0;
3610             lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::MinOptsGC));
3611         }
3612         if (!compEnregLocals())
3613         {
3614             lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::NoRegVars));
3615         }
3616 #if defined(JIT32_GCENCODER) && defined(FEATURE_EH_FUNCLETS)
3617         if (lvaIsOriginalThisArg(lclNum) && (info.compMethodInfo->options & CORINFO_GENERICS_CTXT_FROM_THIS) != 0)
3618         {
3619             // For x86/Linux, we need to track "this".
3620             // However we cannot have it in tracked variables, so we set "this" pointer always untracked
3621             varDsc->lvTracked = 0;
3622         }
3623 #endif
3624
3625         //  Are we not optimizing and we have exception handlers?
3626         //   if so mark all args and locals "do not enregister".
3627         //
3628         if (opts.MinOpts() && compHndBBtabCount > 0)
3629         {
3630             lvaSetVarDoNotEnregister(lclNum DEBUGARG(DoNotEnregisterReason::LiveInOutOfHandler));
3631         }
3632         else
3633         {
3634             var_types type = genActualType(varDsc->TypeGet());
3635
3636             switch (type)
3637             {
3638                 case TYP_FLOAT:
3639                 case TYP_DOUBLE:
3640                 case TYP_INT:
3641                 case TYP_LONG:
3642                 case TYP_REF:
3643                 case TYP_BYREF:
3644 #ifdef FEATURE_SIMD
3645                 case TYP_SIMD8:
3646                 case TYP_SIMD12:
3647                 case TYP_SIMD16:
3648 #if defined(TARGET_XARCH)
3649                 case TYP_SIMD32:
3650                 case TYP_SIMD64:
3651 #endif // TARGET_XARCH
3652 #endif // FEATURE_SIMD
3653                 case TYP_STRUCT:
3654                     break;
3655
3656                 case TYP_UNDEF:
3657                 case TYP_UNKNOWN:
3658                     noway_assert(!"lvType not set correctly");
3659                     varDsc->lvType = TYP_INT;
3660
3661                     FALLTHROUGH;
3662
3663                 default:
3664                     varDsc->lvTracked = 0;
3665             }
3666         }
3667
3668         if (varDsc->lvTracked)
3669         {
3670             trackedCandidates[trackedCandidateCount++] = lclNum;
3671         }
3672     }
3673
3674     lvaTrackedCount = min(trackedCandidateCount, (unsigned)JitConfig.JitMaxLocalsToTrack());
3675
3676     // Sort the candidates. In the late liveness passes we want lower tracked
3677     // indices to be more important variables, so we always do this. In early
3678     // liveness it does not matter, so we can skip it when we are going to
3679     // track everything.
3680     // TODO-TP: For early liveness we could do a partial sort for the large
3681     // case.
3682     if (!fgIsDoingEarlyLiveness || (lvaTrackedCount < trackedCandidateCount))
3683     {
3684         // Now sort the tracked variable table by ref-count
3685         if (compCodeOpt() == SMALL_CODE)
3686         {
3687             jitstd::sort(trackedCandidates, trackedCandidates + trackedCandidateCount,
3688                          LclVarDsc_SmallCode_Less(lvaTable, lvaRefCountState DEBUGARG(lvaCount)));
3689         }
3690         else
3691         {
3692             jitstd::sort(trackedCandidates, trackedCandidates + trackedCandidateCount,
3693                          LclVarDsc_BlendedCode_Less(lvaTable, lvaRefCountState DEBUGARG(lvaCount)));
3694         }
3695     }
3696
3697     JITDUMP("Tracked variable (%u out of %u) table:\n", lvaTrackedCount, lvaCount);
3698
3699     // Assign indices to all the variables we've decided to track
3700     for (unsigned varIndex = 0; varIndex < lvaTrackedCount; varIndex++)
3701     {
3702         LclVarDsc* varDsc = lvaGetDesc(trackedCandidates[varIndex]);
3703         assert(varDsc->lvTracked);
3704         varDsc->lvVarIndex = static_cast<unsigned short>(varIndex);
3705
3706         INDEBUG(if (verbose) { gtDispLclVar(trackedCandidates[varIndex]); })
3707         JITDUMP(" [%6s]: refCnt = %4u, refCntWtd = %6s\n", varTypeName(varDsc->TypeGet()),
3708                 varDsc->lvRefCnt(lvaRefCountState),
3709                 refCntWtd2str(varDsc->lvRefCntWtd(lvaRefCountState), /* padForDecimalPlaces */ true));
3710     }
3711
3712     JITDUMP("\n");
3713
3714     // Mark all variables past the first 'lclMAX_TRACKED' as untracked
3715     for (unsigned varIndex = lvaTrackedCount; varIndex < trackedCandidateCount; varIndex++)
3716     {
3717         LclVarDsc* varDsc = lvaGetDesc(trackedCandidates[varIndex]);
3718         assert(varDsc->lvTracked);
3719         varDsc->lvTracked = 0;
3720     }
3721
3722     // We have a new epoch, and also cache the tracked var count in terms of size_t's sufficient to hold that many bits.
3723     lvaCurEpoch++;
3724     lvaTrackedCountInSizeTUnits =
3725         roundUp((unsigned)lvaTrackedCount, (unsigned)(sizeof(size_t) * 8)) / unsigned(sizeof(size_t) * 8);
3726
3727 #ifdef DEBUG
3728     VarSetOps::AssignNoCopy(this, lvaTrackedVars, VarSetOps::MakeFull(this));
3729 #endif
3730 }
3731
3732 //------------------------------------------------------------------------
3733 // lvExactSize: Get the exact size of the type of this local.
3734 //
3735 // Return Value:
3736 //    Size in bytes. Always non-zero, but not necessarily a multiple of the
3737 //    stack slot size.
3738 //
3739 unsigned LclVarDsc::lvExactSize() const
3740 {
3741     return (lvType == TYP_STRUCT) ? GetLayout()->GetSize() : genTypeSize(lvType);
3742 }
3743
3744 //------------------------------------------------------------------------
3745 // lvSize: Get the size of a struct local on the stack frame.
3746 //
3747 // Return Value:
3748 //    Size in bytes.
3749 //
3750 unsigned LclVarDsc::lvSize() const // Size needed for storage representation. Only used for structs.
3751 {
3752     // TODO-Review: Sometimes we get called on ARM with HFA struct variables that have been promoted,
3753     // where the struct itself is no longer used because all access is via its member fields.
3754     // When that happens, the struct is marked as unused and its type has been changed to
3755     // TYP_INT (to keep the GC tracking code from looking at it).
3756     // See Compiler::raAssignVars() for details. For example:
3757     //      N002 (  4,  3) [00EA067C] -------------               return    struct $346
3758     //      N001 (  3,  2) [00EA0628] -------------                  lclVar    struct(U) V03 loc2
3759     //                                                                        float  V03.f1 (offs=0x00) -> V12 tmp7
3760     //                                                                        f8 (last use) (last use) $345
3761     // Here, the "struct(U)" shows that the "V03 loc2" variable is unused. Not shown is that V03
3762     // is now TYP_INT in the local variable table. It's not really unused, because it's in the tree.
3763
3764     assert(varTypeIsStruct(lvType) || (lvPromoted && lvUnusedStruct));
3765
3766     if (lvIsParam)
3767     {
3768         assert(varTypeIsStruct(lvType));
3769         const bool     isFloatHfa       = (lvIsHfa() && (GetHfaType() == TYP_FLOAT));
3770         const unsigned argSizeAlignment = Compiler::eeGetArgSizeAlignment(lvType, isFloatHfa);
3771         return roundUp(lvExactSize(), argSizeAlignment);
3772     }
3773
3774 #if defined(FEATURE_SIMD) && !defined(TARGET_64BIT)
3775     // For 32-bit architectures, we make local variable SIMD12 types 16 bytes instead of just 12. We can't do
3776     // this for arguments, which must be passed according the defined ABI. We don't want to do this for
3777     // dependently promoted struct fields, but we don't know that here. See lvaMapSimd12ToSimd16().
3778     // (Note that for 64-bits, we are already rounding up to 16.)
3779     if (lvType == TYP_SIMD12)
3780     {
3781         assert(!lvIsParam);
3782         return 16;
3783     }
3784 #endif // defined(FEATURE_SIMD) && !defined(TARGET_64BIT)
3785
3786     return roundUp(lvExactSize(), TARGET_POINTER_SIZE);
3787 }
3788
3789 /**********************************************************************************
3790 * Get stack size of the varDsc.
3791 */
3792 size_t LclVarDsc::lvArgStackSize() const
3793 {
3794     // Make sure this will have a stack size
3795     assert(!this->lvIsRegArg);
3796
3797     size_t stackSize = 0;
3798     if (varTypeIsStruct(this))
3799     {
3800 #if defined(WINDOWS_AMD64_ABI)
3801         // Structs are either passed by reference or can be passed by value using one pointer
3802         stackSize = TARGET_POINTER_SIZE;
3803 #elif defined(TARGET_ARM64) || defined(UNIX_AMD64_ABI) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
3804         // lvSize performs a roundup.
3805         stackSize = this->lvSize();
3806
3807 #if defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
3808         if ((stackSize > TARGET_POINTER_SIZE * 2) && (!this->lvIsHfa()))
3809         {
3810             // If the size is greater than 16 bytes then it will
3811             // be passed by reference.
3812             stackSize = TARGET_POINTER_SIZE;
3813         }
3814 #endif // defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
3815
3816 #else // !TARGET_ARM64 !WINDOWS_AMD64_ABI !UNIX_AMD64_ABI !TARGET_LOONGARCH64 !TARGET_RISCV64
3817
3818         NYI("Unsupported target.");
3819         unreached();
3820
3821 #endif //  !TARGET_ARM64 !WINDOWS_AMD64_ABI !UNIX_AMD64_ABI
3822     }
3823     else
3824     {
3825         stackSize = TARGET_POINTER_SIZE;
3826     }
3827
3828     return stackSize;
3829 }
3830
3831 //------------------------------------------------------------------------
3832 // GetRegisterType: Determine register type for this local var.
3833 //
3834 // Arguments:
3835 //    tree - node that uses the local, its type is checked first.
3836 //
3837 // Return Value:
3838 //    TYP_UNDEF if the layout is not enregistrable, the register type otherwise.
3839 //
3840 var_types LclVarDsc::GetRegisterType(const GenTreeLclVarCommon* tree) const
3841 {
3842     var_types targetType = tree->TypeGet();
3843
3844     if (targetType == TYP_STRUCT)
3845     {
3846         ClassLayout* layout;
3847         if (tree->OperIs(GT_LCL_FLD, GT_STORE_LCL_FLD))
3848         {
3849             layout = tree->AsLclFld()->GetLayout();
3850         }
3851         else
3852         {
3853             assert((TypeGet() == TYP_STRUCT) && tree->OperIs(GT_LCL_VAR, GT_STORE_LCL_VAR));
3854             layout = GetLayout();
3855         }
3856
3857         targetType = layout->GetRegisterType();
3858     }
3859
3860 #ifdef DEBUG
3861     if ((targetType != TYP_UNDEF) && tree->OperIs(GT_STORE_LCL_VAR) && lvNormalizeOnStore())
3862     {
3863         const bool phiStore = (tree->gtGetOp1()->OperIsNonPhiLocal() == false);
3864         // Ensure that the lclVar node is typed correctly,
3865         // does not apply to phi-stores because they do not produce code in the merge block.
3866         assert(phiStore || targetType == genActualType(TypeGet()));
3867     }
3868 #endif
3869     return targetType;
3870 }
3871
3872 //------------------------------------------------------------------------
3873 // GetRegisterType: Determine register type for this local var.
3874 //
3875 // Return Value:
3876 //    TYP_UNDEF if the layout is not enregistrable, the register type otherwise.
3877 //
3878 var_types LclVarDsc::GetRegisterType() const
3879 {
3880     if (TypeGet() != TYP_STRUCT)
3881     {
3882 #if !defined(TARGET_64BIT)
3883         if (TypeGet() == TYP_LONG)
3884         {
3885             return TYP_UNDEF;
3886         }
3887 #endif
3888         return TypeGet();
3889     }
3890     assert(m_layout != nullptr);
3891     return m_layout->GetRegisterType();
3892 }
3893
3894 //------------------------------------------------------------------------
3895 // GetStackSlotHomeType:
3896 //   Get the canonical type of the stack slot that this enregistrable local is
3897 //   using when stored on the stack.
3898 //
3899 // Return Value:
3900 //   TYP_UNDEF if the layout is not enregistrable. Otherwise returns the type
3901 //    of the stack slot home for the local.
3902 //
3903 // Remarks:
3904 //   This function always returns a canonical type: for all 4-byte types
3905 //   (structs, floats, ints) it will return TYP_INT. It is meant to be used
3906 //   when moving locals between register and stack. Because of this the
3907 //   returned type is usually at least one 4-byte stack slot. However, there
3908 //   are certain exceptions for promoted fields in OSR methods (that may refer
3909 //   back to the original frame) and due to macOS arm64 where subsequent small
3910 //   parameters can be packed into the same stack slot.
3911 //
3912 var_types LclVarDsc::GetStackSlotHomeType() const
3913 {
3914     if (varTypeIsSmall(TypeGet()))
3915     {
3916         if (compMacOsArm64Abi() && lvIsParam && !lvIsRegArg)
3917         {
3918             // Allocated by caller and potentially only takes up a small slot
3919             return GetRegisterType();
3920         }
3921
3922         if (lvIsOSRLocal && lvIsStructField)
3923         {
3924 #if defined(TARGET_X86)
3925             // Revisit when we support OSR on x86
3926             unreached();
3927 #else
3928             return GetRegisterType();
3929 #endif
3930         }
3931     }
3932
3933     return genActualType(GetRegisterType());
3934 }
3935
3936 //----------------------------------------------------------------------------------------------
3937 // CanBeReplacedWithItsField: check if a whole struct reference could be replaced by a field.
3938 //
3939 // Arguments:
3940 //    comp - the compiler instance;
3941 //
3942 // Return Value:
3943 //    true if that can be replaced, false otherwise.
3944 //
3945 // Notes:
3946 //    The replacement can be made only for independently promoted structs
3947 //    with 1 field without holes.
3948 //
3949 bool LclVarDsc::CanBeReplacedWithItsField(Compiler* comp) const
3950 {
3951     if (!lvPromoted)
3952     {
3953         return false;
3954     }
3955
3956     if (comp->lvaGetPromotionType(this) != Compiler::PROMOTION_TYPE_INDEPENDENT)
3957     {
3958         return false;
3959     }
3960     if (lvFieldCnt != 1)
3961     {
3962         return false;
3963     }
3964     if (lvContainsHoles)
3965     {
3966         return false;
3967     }
3968
3969 #if defined(FEATURE_SIMD)
3970     // If we return `struct A { SIMD16 a; }` we split the struct into several fields.
3971     // In order to do that we have to have its field `a` in memory. Right now lowering cannot
3972     // handle RETURN struct(multiple registers)->SIMD16(one register), but it can be improved.
3973     LclVarDsc* fieldDsc = comp->lvaGetDesc(lvFieldLclStart);
3974     if (varTypeIsSIMD(fieldDsc))
3975     {
3976         return false;
3977     }
3978 #endif // FEATURE_SIMD
3979
3980     return true;
3981 }
3982
3983 //------------------------------------------------------------------------
3984 // lvaMarkLclRefs: increment local var references counts and more
3985 //
3986 // Arguments:
3987 //     tree - some node in a tree
3988 //     block - block that the tree node belongs to
3989 //     stmt - stmt that the tree node belongs to
3990 //     isRecompute - true if we should just recompute counts
3991 //
3992 // Notes:
3993 //     Invoked via the MarkLocalVarsVisitor
3994 //
3995 //     Primarily increments the regular and weighted local var ref
3996 //     counts for any local referred to directly by tree.
3997 //
3998 //     Also:
3999 //
4000 //     Accounts for implicit references to frame list root for
4001 //     pinvokes that will be expanded later.
4002 //
4003 //     Determines if locals of TYP_BOOL can safely be considered
4004 //     to hold only 0 or 1 or may have a broader range of true values.
4005 //
4006 //     Does some setup work for assertion prop, noting locals that are
4007 //     eligible for assertion prop, single defs, and tracking which blocks
4008 //     hold uses.
4009 //
4010 //     Looks for uses of generic context and sets lvaGenericsContextInUse.
4011 //
4012 //     In checked builds:
4013 //
4014 //     Verifies that local accesses are consistently typed.
4015 //     Verifies that casts remain in bounds.
4016
4017 void Compiler::lvaMarkLclRefs(GenTree* tree, BasicBlock* block, Statement* stmt, bool isRecompute)
4018 {
4019     const weight_t weight = block->getBBWeight(this);
4020
4021     /* Is this a call to unmanaged code ? */
4022     if (tree->IsCall() && compMethodRequiresPInvokeFrame())
4023     {
4024         assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
4025         if (!opts.ShouldUsePInvokeHelpers())
4026         {
4027             /* Get the special variable descriptor */
4028             LclVarDsc* varDsc = lvaGetDesc(info.compLvFrameListRoot);
4029
4030             /* Increment the ref counts twice */
4031             varDsc->incRefCnts(weight, this);
4032             varDsc->incRefCnts(weight, this);
4033         }
4034     }
4035
4036     if (tree->OperIs(GT_LCL_ADDR))
4037     {
4038         LclVarDsc* varDsc = lvaGetDesc(tree->AsLclVarCommon());
4039         assert(varDsc->IsAddressExposed() || varDsc->IsHiddenBufferStructArg());
4040         varDsc->incRefCnts(weight, this);
4041         return;
4042     }
4043
4044     if (!tree->OperIsLocal())
4045     {
4046         return;
4047     }
4048
4049     /* This must be a local variable reference */
4050
4051     // See if this is a generics context use.
4052     if ((tree->gtFlags & GTF_VAR_CONTEXT) != 0)
4053     {
4054         assert(tree->OperIs(GT_LCL_VAR));
4055         if (!lvaGenericsContextInUse)
4056         {
4057             JITDUMP("-- generic context in use at [%06u]\n", dspTreeID(tree));
4058             lvaGenericsContextInUse = true;
4059         }
4060     }
4061
4062     unsigned   lclNum = tree->AsLclVarCommon()->GetLclNum();
4063     LclVarDsc* varDsc = lvaGetDesc(lclNum);
4064
4065     /* Increment the reference counts */
4066
4067     varDsc->incRefCnts(weight, this);
4068
4069 #ifdef DEBUG
4070     if (varDsc->lvIsStructField)
4071     {
4072         // If ref count was increased for struct field, ensure that the
4073         // parent struct is still promoted.
4074         LclVarDsc* parentStruct = lvaGetDesc(varDsc->lvParentLcl);
4075         assert(!parentStruct->lvUndoneStructPromotion);
4076     }
4077 #endif
4078
4079     if (!isRecompute)
4080     {
4081         if (varDsc->IsAddressExposed())
4082         {
4083             varDsc->lvIsBoolean      = false;
4084             varDsc->lvAllDefsAreNoGc = false;
4085         }
4086
4087         if (!tree->OperIsScalarLocal())
4088         {
4089             return;
4090         }
4091
4092         if (fgDomsComputed && IsDominatedByExceptionalEntry(block))
4093         {
4094             SetVolatileHint(varDsc);
4095         }
4096
4097         if (tree->OperIs(GT_STORE_LCL_VAR))
4098         {
4099             GenTree* value = tree->AsLclVar()->Data();
4100
4101             if (varDsc->lvPinned && varDsc->lvAllDefsAreNoGc && !value->IsNotGcDef())
4102             {
4103                 varDsc->lvAllDefsAreNoGc = false;
4104             }
4105
4106             if (value->gtType != TYP_BOOL)
4107             {
4108                 // Is the value clearly a boolean one?
4109                 switch (value->gtOper)
4110                 {
4111                     case GT_CNS_INT:
4112                         if (value->AsIntCon()->gtIconVal == 0)
4113                         {
4114                             break;
4115                         }
4116                         if (value->AsIntCon()->gtIconVal == 1)
4117                         {
4118                             break;
4119                         }
4120
4121                         // Not 0 or 1, fall through ....
4122                         FALLTHROUGH;
4123                     default:
4124                         if (value->OperIsCompare())
4125                         {
4126                             break;
4127                         }
4128
4129                         varDsc->lvIsBoolean = false;
4130                         break;
4131                 }
4132             }
4133
4134             if (!varDsc->lvDisqualifySingleDefRegCandidate) // If this var is already disqualified, we can skip this
4135             {
4136                 bool bbInALoop  = (block->bbFlags & BBF_BACKWARD_JUMP) != 0;
4137                 bool bbIsReturn = block->bbJumpKind == BBJ_RETURN;
4138                 // TODO: Zero-inits in LSRA are created with below condition. But if filter out based on that condition
4139                 // we filter a lot of interesting variables that would benefit otherwise with EH var enregistration.
4140                 // bool needsExplicitZeroInit = !varDsc->lvIsParam && (info.compInitMem ||
4141                 // varTypeIsGC(varDsc->TypeGet()));
4142                 bool needsExplicitZeroInit = fgVarNeedsExplicitZeroInit(lclNum, bbInALoop, bbIsReturn);
4143
4144                 if (varDsc->lvSingleDefRegCandidate || needsExplicitZeroInit)
4145                 {
4146 #ifdef DEBUG
4147                     if (needsExplicitZeroInit)
4148                     {
4149                         varDsc->lvSingleDefDisqualifyReason = 'Z';
4150                         JITDUMP("V%02u needs explicit zero init. Disqualified as a single-def register candidate.\n",
4151                                 lclNum);
4152                     }
4153                     else
4154                     {
4155                         varDsc->lvSingleDefDisqualifyReason = 'M';
4156                         JITDUMP("V%02u has multiple definitions. Disqualified as a single-def register candidate.\n",
4157                                 lclNum);
4158                     }
4159
4160 #endif // DEBUG
4161                     varDsc->lvSingleDefRegCandidate           = false;
4162                     varDsc->lvDisqualifySingleDefRegCandidate = true;
4163                 }
4164                 else if (!varDsc->lvDoNotEnregister)
4165                 {
4166                     // Variables can be marked as DoNotEngister in earlier stages like LocalAddressVisitor.
4167                     // No need to track them for single-def.
4168                     CLANG_FORMAT_COMMENT_ANCHOR;
4169
4170 #if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
4171                     // TODO-CQ: If the varType needs partial callee save, conservatively do not enregister
4172                     // such variable. In future, we should enable enregisteration for such variables.
4173                     if (!varTypeNeedsPartialCalleeSave(varDsc->GetRegisterType()))
4174 #endif
4175                     {
4176                         varDsc->lvSingleDefRegCandidate = true;
4177                         JITDUMP("Marking EH Var V%02u as a register candidate.\n", lclNum);
4178                     }
4179                 }
4180             }
4181         }
4182
4183         // Check that the LCL_VAR node has the same type as the underlying variable, save a few mismatches we allow.
4184         assert(tree->TypeIs(varDsc->TypeGet(), genActualType(varDsc)) ||
4185                (tree->TypeIs(TYP_I_IMPL) && (varDsc->TypeGet() == TYP_BYREF)) || // Created for spill clique import.
4186                (tree->TypeIs(TYP_BYREF) && (varDsc->TypeGet() == TYP_I_IMPL)) || // Created by inliner substitution.
4187                (tree->TypeIs(TYP_INT) && (varDsc->TypeGet() == TYP_LONG)));      // Created by "optNarrowTree".
4188     }
4189 }
4190
4191 //------------------------------------------------------------------------
4192 // IsDominatedByExceptionalEntry: Check is the block dominated by an exception entry block.
4193 //
4194 // Arguments:
4195 //    block - the checking block.
4196 //
4197 bool Compiler::IsDominatedByExceptionalEntry(BasicBlock* block)
4198 {
4199     assert(fgDomsComputed);
4200     return block->IsDominatedByExceptionalEntryFlag();
4201 }
4202
4203 //------------------------------------------------------------------------
4204 // SetVolatileHint: Set a local var's volatile hint.
4205 //
4206 // Arguments:
4207 //    varDsc - the local variable that needs the hint.
4208 //
4209 void Compiler::SetVolatileHint(LclVarDsc* varDsc)
4210 {
4211     varDsc->lvVolatileHint = true;
4212 }
4213
4214 //------------------------------------------------------------------------
4215 // lvaMarkLocalVars: update local var ref counts for IR in a basic block
4216 //
4217 // Arguments:
4218 //    block - the block in question
4219 //    isRecompute - true if counts are being recomputed
4220 //
4221 // Notes:
4222 //    Invokes lvaMarkLclRefs on each tree node for each
4223 //    statement in the block.
4224
4225 void Compiler::lvaMarkLocalVars(BasicBlock* block, bool isRecompute)
4226 {
4227     class MarkLocalVarsVisitor final : public GenTreeVisitor<MarkLocalVarsVisitor>
4228     {
4229     private:
4230         BasicBlock* m_block;
4231         Statement*  m_stmt;
4232         bool        m_isRecompute;
4233
4234     public:
4235         enum
4236         {
4237             DoPreOrder = true,
4238         };
4239
4240         MarkLocalVarsVisitor(Compiler* compiler, BasicBlock* block, Statement* stmt, bool isRecompute)
4241             : GenTreeVisitor<MarkLocalVarsVisitor>(compiler), m_block(block), m_stmt(stmt), m_isRecompute(isRecompute)
4242         {
4243         }
4244
4245         Compiler::fgWalkResult PreOrderVisit(GenTree** use, GenTree* user)
4246         {
4247             // TODO: Stop passing isRecompute once we are sure that this assert is never hit.
4248             assert(!m_isRecompute);
4249             m_compiler->lvaMarkLclRefs(*use, m_block, m_stmt, m_isRecompute);
4250             return WALK_CONTINUE;
4251         }
4252     };
4253
4254     JITDUMP("\n*** %s local variables in block " FMT_BB " (weight=%s)\n", isRecompute ? "recomputing" : "marking",
4255             block->bbNum, refCntWtd2str(block->getBBWeight(this)));
4256
4257     for (Statement* const stmt : block->NonPhiStatements())
4258     {
4259         MarkLocalVarsVisitor visitor(this, block, stmt, isRecompute);
4260         DISPSTMT(stmt);
4261         visitor.WalkTree(stmt->GetRootNodePointer(), nullptr);
4262     }
4263 }
4264
4265 //------------------------------------------------------------------------
4266 // lvaMarkLocalVars: enable normal ref counting, compute initial counts, sort locals table
4267 //
4268 // Returns:
4269 //    suitable phase status
4270 //
4271 // Notes:
4272 //    Now behaves differently in minopts / debug. Instead of actually inspecting
4273 //    the IR and counting references, the jit assumes all locals are referenced
4274 //    and does not sort the locals table.
4275 //
4276 //    Also, when optimizing, lays the groundwork for assertion prop and more.
4277 //    See details in lvaMarkLclRefs.
4278
4279 PhaseStatus Compiler::lvaMarkLocalVars()
4280 {
4281     JITDUMP("\n*************** In lvaMarkLocalVars()");
4282
4283     // If we have direct pinvokes, verify the frame list root local was set up properly
4284     if (compMethodRequiresPInvokeFrame())
4285     {
4286         assert((!opts.ShouldUsePInvokeHelpers()) || (info.compLvFrameListRoot == BAD_VAR_NUM));
4287         if (!opts.ShouldUsePInvokeHelpers())
4288         {
4289             noway_assert(info.compLvFrameListRoot >= info.compLocalsCount && info.compLvFrameListRoot < lvaCount);
4290         }
4291     }
4292
4293     unsigned const lvaCountOrig = lvaCount;
4294
4295 #if !defined(FEATURE_EH_FUNCLETS)
4296
4297     // Grab space for exception handling
4298
4299     if (ehNeedsShadowSPslots())
4300     {
4301         // The first slot is reserved for ICodeManager::FixContext(ppEndRegion)
4302         // ie. the offset of the end-of-last-executed-filter
4303         unsigned slotsNeeded = 1;
4304
4305         unsigned handlerNestingLevel = ehMaxHndNestingCount;
4306
4307         if (opts.compDbgEnC && (handlerNestingLevel < (unsigned)MAX_EnC_HANDLER_NESTING_LEVEL))
4308             handlerNestingLevel = (unsigned)MAX_EnC_HANDLER_NESTING_LEVEL;
4309
4310         slotsNeeded += handlerNestingLevel;
4311
4312         // For a filter (which can be active at the same time as a catch/finally handler)
4313         slotsNeeded++;
4314         // For zero-termination of the shadow-Stack-pointer chain
4315         slotsNeeded++;
4316
4317         lvaShadowSPslotsVar = lvaGrabTempWithImplicitUse(false DEBUGARG("lvaShadowSPslotsVar"));
4318         lvaSetStruct(lvaShadowSPslotsVar, typGetBlkLayout(slotsNeeded * TARGET_POINTER_SIZE), false);
4319         lvaSetVarAddrExposed(lvaShadowSPslotsVar DEBUGARG(AddressExposedReason::EXTERNALLY_VISIBLE_IMPLICITLY));
4320     }
4321
4322 #endif // !FEATURE_EH_FUNCLETS
4323
4324     // PSPSym and LocAllocSPvar are not used by the NativeAOT ABI
4325     if (!IsTargetAbi(CORINFO_NATIVEAOT_ABI))
4326     {
4327 #if defined(FEATURE_EH_FUNCLETS)
4328         if (ehNeedsPSPSym())
4329         {
4330             lvaPSPSym            = lvaGrabTempWithImplicitUse(false DEBUGARG("PSPSym"));
4331             LclVarDsc* lclPSPSym = lvaGetDesc(lvaPSPSym);
4332             lclPSPSym->lvType    = TYP_I_IMPL;
4333             lvaSetVarDoNotEnregister(lvaPSPSym DEBUGARG(DoNotEnregisterReason::VMNeedsStackAddr));
4334         }
4335 #endif // FEATURE_EH_FUNCLETS
4336
4337 #ifdef JIT32_GCENCODER
4338         // LocAllocSPvar is only required by the implicit frame layout expected by the VM on x86. Whether
4339         // a function contains a Localloc is conveyed in the GC information, in the InfoHdrSmall.localloc
4340         // field. The function must have an EBP frame. Then, the VM finds the LocAllocSP slot by assuming
4341         // the following stack layout:
4342         //
4343         //      -- higher addresses --
4344         //      saved EBP                       <-- EBP points here
4345         //      other callee-saved registers    // InfoHdrSmall.savedRegsCountExclFP specifies this size
4346         //      optional GS cookie              // InfoHdrSmall.security is 1 if this exists
4347         //      LocAllocSP slot
4348         //      -- lower addresses --
4349         //
4350         // See also eetwain.cpp::GetLocallocSPOffset() and its callers.
4351         if (compLocallocUsed)
4352         {
4353             lvaLocAllocSPvar         = lvaGrabTempWithImplicitUse(false DEBUGARG("LocAllocSPvar"));
4354             LclVarDsc* locAllocSPvar = lvaGetDesc(lvaLocAllocSPvar);
4355             locAllocSPvar->lvType    = TYP_I_IMPL;
4356         }
4357 #endif // JIT32_GCENCODER
4358     }
4359
4360     // Ref counting is now enabled normally.
4361     lvaRefCountState = RCS_NORMAL;
4362
4363 #if defined(DEBUG)
4364     const bool setSlotNumbers = true;
4365 #else
4366     const bool setSlotNumbers = opts.compScopeInfo && (info.compVarScopesCount > 0);
4367 #endif // defined(DEBUG)
4368
4369     const bool isRecompute = false;
4370     lvaComputeRefCounts(isRecompute, setSlotNumbers);
4371
4372     // If we don't need precise reference counts, e.g. we're not optimizing, we're done.
4373     if (!PreciseRefCountsRequired())
4374     {
4375         // This phase may add new locals
4376         //
4377         return (lvaCount != lvaCountOrig) ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
4378     }
4379
4380     const bool reportParamTypeArg = lvaReportParamTypeArg();
4381
4382     // Update bookkeeping on the generic context.
4383     if (lvaKeepAliveAndReportThis())
4384     {
4385         lvaGetDesc(0u)->lvImplicitlyReferenced = reportParamTypeArg;
4386     }
4387     else if (lvaReportParamTypeArg())
4388     {
4389         // We should have a context arg.
4390         assert(info.compTypeCtxtArg != (int)BAD_VAR_NUM);
4391         lvaGetDesc(info.compTypeCtxtArg)->lvImplicitlyReferenced = reportParamTypeArg;
4392     }
4393
4394     assert(PreciseRefCountsRequired());
4395
4396     // This phase may add new locals.
4397     //
4398     return (lvaCount != lvaCountOrig) ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING;
4399 }
4400
4401 //------------------------------------------------------------------------
4402 // lvaComputeRefCounts: compute ref counts for locals
4403 //
4404 // Arguments:
4405 //    isRecompute -- true if we just want ref counts and no other side effects;
4406 //                   false means to also look for true boolean locals, lay
4407 //                   groundwork for assertion prop, check type consistency, etc.
4408 //                   See lvaMarkLclRefs for details on what else goes on.
4409 //    setSlotNumbers -- true if local slot numbers should be assigned.
4410 //
4411 // Notes:
4412 //    Some implicit references are given actual counts or weight bumps here
4413 //    to match pre-existing behavior.
4414 //
4415 //    In fast-jitting modes where we don't ref count locals, this bypasses
4416 //    actual counting, and makes all locals implicitly referenced on first
4417 //    compute. It asserts all locals are implicitly referenced on recompute.
4418 //
4419 //    When optimizing we also recompute lvaGenericsContextInUse based
4420 //    on specially flagged LCL_VAR appearances.
4421 //
4422 void Compiler::lvaComputeRefCounts(bool isRecompute, bool setSlotNumbers)
4423 {
4424     JITDUMP("\n*** lvaComputeRefCounts ***\n");
4425     unsigned   lclNum = 0;
4426     LclVarDsc* varDsc = nullptr;
4427
4428     // Fast path for minopts and debug codegen.
4429     //
4430     // On first compute: mark all locals as implicitly referenced and untracked.
4431     // On recompute: do nothing.
4432     if (!PreciseRefCountsRequired())
4433     {
4434         if (isRecompute)
4435         {
4436
4437 #if defined(DEBUG)
4438             // All local vars should be marked as implicitly referenced
4439             // and not tracked.
4440             for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4441             {
4442                 const bool isSpecialVarargsParam = varDsc->lvIsParam && raIsVarargsStackArg(lclNum);
4443
4444                 if (isSpecialVarargsParam)
4445                 {
4446                     assert(varDsc->lvRefCnt() == 0);
4447                 }
4448                 else
4449                 {
4450                     assert(varDsc->lvImplicitlyReferenced);
4451                 }
4452
4453                 assert(!varDsc->lvTracked);
4454             }
4455 #endif // defined (DEBUG)
4456
4457             return;
4458         }
4459
4460         // First compute.
4461         for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4462         {
4463             // Using lvImplicitlyReferenced here ensures that we can't
4464             // accidentally make locals be unreferenced later by decrementing
4465             // the ref count to zero.
4466             //
4467             // If, in minopts/debug, we really want to allow locals to become
4468             // unreferenced later, we'll have to explicitly clear this bit.
4469             varDsc->setLvRefCnt(0);
4470             varDsc->setLvRefCntWtd(BB_ZERO_WEIGHT);
4471
4472             // Special case for some varargs params ... these must
4473             // remain unreferenced.
4474             const bool isSpecialVarargsParam = varDsc->lvIsParam && raIsVarargsStackArg(lclNum);
4475
4476             if (!isSpecialVarargsParam)
4477             {
4478                 varDsc->lvImplicitlyReferenced = 1;
4479             }
4480
4481             varDsc->lvTracked = 0;
4482
4483             if (setSlotNumbers)
4484             {
4485                 varDsc->lvSlotNum = lclNum;
4486             }
4487
4488             // Assert that it's ok to bypass the type repair logic in lvaMarkLclRefs
4489             assert((varDsc->lvType != TYP_UNDEF) && (varDsc->lvType != TYP_VOID) && (varDsc->lvType != TYP_UNKNOWN));
4490         }
4491
4492         lvaCurEpoch++;
4493         lvaTrackedCount             = 0;
4494         lvaTrackedCountInSizeTUnits = 0;
4495         return;
4496     }
4497
4498     // Slower path we take when optimizing, to get accurate counts.
4499     //
4500     // First, reset all explicit ref counts and weights.
4501     for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4502     {
4503         varDsc->setLvRefCnt(0);
4504         varDsc->setLvRefCntWtd(BB_ZERO_WEIGHT);
4505
4506         if (setSlotNumbers)
4507         {
4508             varDsc->lvSlotNum = lclNum;
4509         }
4510
4511         // Set initial value for lvSingleDef for explicit and implicit
4512         // argument locals as they are "defined" on entry.
4513         // However, if we are just recomputing the ref counts, retain the value
4514         // that was set by past phases.
4515         if (!isRecompute)
4516         {
4517             varDsc->lvSingleDef             = varDsc->lvIsParam;
4518             varDsc->lvSingleDefRegCandidate = varDsc->lvIsParam;
4519
4520             varDsc->lvAllDefsAreNoGc = (varDsc->lvImplicitlyReferenced == false);
4521         }
4522     }
4523
4524     // Remember current state of generic context use, and prepare
4525     // to compute new state.
4526     const bool oldLvaGenericsContextInUse = lvaGenericsContextInUse;
4527     lvaGenericsContextInUse               = false;
4528
4529     JITDUMP("\n*** lvaComputeRefCounts -- explicit counts ***\n");
4530
4531     // Second, account for all explicit local variable references
4532     for (BasicBlock* const block : Blocks())
4533     {
4534         if (block->IsLIR())
4535         {
4536             assert(isRecompute);
4537
4538             const weight_t weight = block->getBBWeight(this);
4539             for (GenTree* node : LIR::AsRange(block))
4540             {
4541                 if (node->OperIsAnyLocal())
4542                 {
4543                     LclVarDsc* varDsc = lvaGetDesc(node->AsLclVarCommon());
4544                     // If this is an EH var, use a zero weight for defs, so that we don't
4545                     // count those in our heuristic for register allocation, since they always
4546                     // must be stored, so there's no value in enregistering them at defs; only
4547                     // if there are enough uses to justify it.
4548                     if (varDsc->lvLiveInOutOfHndlr && !varDsc->lvDoNotEnregister &&
4549                         ((node->gtFlags & GTF_VAR_DEF) != 0))
4550                     {
4551                         varDsc->incRefCnts(0, this);
4552                     }
4553                     else
4554                     {
4555                         varDsc->incRefCnts(weight, this);
4556                     }
4557
4558                     if ((node->gtFlags & GTF_VAR_CONTEXT) != 0)
4559                     {
4560                         assert(node->OperIs(GT_LCL_VAR));
4561                         lvaGenericsContextInUse = true;
4562                     }
4563                 }
4564             }
4565         }
4566         else
4567         {
4568             lvaMarkLocalVars(block, isRecompute);
4569         }
4570     }
4571
4572     if (oldLvaGenericsContextInUse && !lvaGenericsContextInUse)
4573     {
4574         // Context was in use but no longer is. This can happen
4575         // if we're able to optimize, so just leave a note.
4576         JITDUMP("\n** Generics context no longer in use\n");
4577     }
4578     else if (lvaGenericsContextInUse && !oldLvaGenericsContextInUse)
4579     {
4580         // Context was not in use but now is.
4581         //
4582         // Changing from unused->used should never happen; creation of any new IR
4583         // for context use should also be setting lvaGenericsContextInUse.
4584         assert(!"unexpected new use of generics context");
4585     }
4586
4587     JITDUMP("\n*** lvaComputeRefCounts -- implicit counts ***\n");
4588
4589     // Third, bump ref counts for some implicit prolog references
4590     for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
4591     {
4592         // Todo: review justification for these count bumps.
4593         if (varDsc->lvIsRegArg)
4594         {
4595             if ((lclNum < info.compArgsCount) && (varDsc->lvRefCnt() > 0))
4596             {
4597                 // Fix 388376 ARM JitStress WP7
4598                 varDsc->incRefCnts(BB_UNITY_WEIGHT, this);
4599                 varDsc->incRefCnts(BB_UNITY_WEIGHT, this);
4600             }
4601
4602             // Ref count bump that was in lvaPromoteStructVar
4603             //
4604             // This was formerly done during RCS_EARLY counting,
4605             // and we did not used to reset counts like we do now.
4606             if (varDsc->lvIsStructField && varTypeIsStruct(lvaGetDesc(varDsc->lvParentLcl)))
4607             {
4608                 varDsc->incRefCnts(BB_UNITY_WEIGHT, this);
4609             }
4610         }
4611
4612         // If we have JMP, all arguments must have a location
4613         // even if we don't use them inside the method
4614         if (compJmpOpUsed && varDsc->lvIsParam && (varDsc->lvRefCnt() == 0))
4615         {
4616             // except when we have varargs and the argument is
4617             // passed on the stack.  In that case, it's important
4618             // for the ref count to be zero, so that we don't attempt
4619             // to track them for GC info (which is not possible since we
4620             // don't know their offset in the stack).  See the assert at the
4621             // end of raMarkStkVars and bug #28949 for more info.
4622             if (!raIsVarargsStackArg(lclNum))
4623             {
4624                 varDsc->lvImplicitlyReferenced = 1;
4625             }
4626         }
4627
4628         if (varDsc->lvPinned && varDsc->lvAllDefsAreNoGc)
4629         {
4630             varDsc->lvPinned = 0;
4631
4632             JITDUMP("V%02u was unpinned as all def candidates were local.\n", lclNum);
4633         }
4634     }
4635 }
4636
4637 void Compiler::lvaAllocOutgoingArgSpaceVar()
4638 {
4639 #if FEATURE_FIXED_OUT_ARGS
4640
4641     // Setup the outgoing argument region, in case we end up using it later
4642
4643     if (lvaOutgoingArgSpaceVar == BAD_VAR_NUM)
4644     {
4645         lvaOutgoingArgSpaceVar = lvaGrabTempWithImplicitUse(false DEBUGARG("OutgoingArgSpace"));
4646         lvaSetStruct(lvaOutgoingArgSpaceVar, typGetBlkLayout(0), false);
4647         lvaSetVarAddrExposed(lvaOutgoingArgSpaceVar DEBUGARG(AddressExposedReason::EXTERNALLY_VISIBLE_IMPLICITLY));
4648     }
4649
4650     noway_assert(lvaOutgoingArgSpaceVar >= info.compLocalsCount && lvaOutgoingArgSpaceVar < lvaCount);
4651
4652 #endif // FEATURE_FIXED_OUT_ARGS
4653 }
4654
4655 inline void Compiler::lvaIncrementFrameSize(unsigned size)
4656 {
4657     if (size > MAX_FrameSize || compLclFrameSize + size > MAX_FrameSize)
4658     {
4659         BADCODE("Frame size overflow");
4660     }
4661
4662     compLclFrameSize += size;
4663 }
4664
4665 /****************************************************************************
4666 *
4667 *  Return true if absolute offsets of temps are larger than vars, or in other
4668 *  words, did we allocate temps before of after vars.  The /GS buffer overrun
4669 *  checks want temps to be at low stack addresses than buffers
4670 */
4671 bool Compiler::lvaTempsHaveLargerOffsetThanVars()
4672 {
4673 #ifdef TARGET_ARM
4674     // We never want to place the temps with larger offsets for ARM
4675     return false;
4676 #else
4677     if (compGSReorderStackLayout)
4678     {
4679         return codeGen->isFramePointerUsed();
4680     }
4681     else
4682     {
4683         return true;
4684     }
4685 #endif
4686 }
4687
4688 /****************************************************************************
4689 *
4690 *  Return an upper bound estimate for the size of the compiler spill temps
4691 *
4692 */
4693 unsigned Compiler::lvaGetMaxSpillTempSize()
4694 {
4695     unsigned result = 0;
4696
4697     if (codeGen->regSet.hasComputedTmpSize())
4698     {
4699         result = codeGen->regSet.tmpGetTotalSize();
4700     }
4701     else
4702     {
4703         result = MAX_SPILL_TEMP_SIZE;
4704     }
4705     return result;
4706 }
4707
4708 // clang-format off
4709 /*****************************************************************************
4710  *
4711  *  Compute stack frame offsets for arguments, locals and optionally temps.
4712  *
4713  *  The frame is laid out as follows for x86:
4714  *
4715  *              ESP frames
4716  *
4717  *      |                       |
4718  *      |-----------------------|
4719  *      |       incoming        |
4720  *      |       arguments       |
4721  *      |-----------------------| <---- Virtual '0'
4722  *      |    return address     |
4723  *      +=======================+
4724  *      |Callee saved registers |
4725  *      |-----------------------|
4726  *      |       Temps           |
4727  *      |-----------------------|
4728  *      |       Variables       |
4729  *      |-----------------------| <---- Ambient ESP
4730  *      |   Arguments for the   |
4731  *      ~    next function      ~
4732  *      |                       |
4733  *      |       |               |
4734  *      |       | Stack grows   |
4735  *              | downward
4736  *              V
4737  *
4738  *
4739  *              EBP frames
4740  *
4741  *      |                       |
4742  *      |-----------------------|
4743  *      |       incoming        |
4744  *      |       arguments       |
4745  *      |-----------------------| <---- Virtual '0'
4746  *      |    return address     |
4747  *      +=======================+
4748  *      |    incoming EBP       |
4749  *      |-----------------------| <---- EBP
4750  *      |Callee saved registers |
4751  *      |-----------------------|
4752  *      |   security object     |
4753  *      |-----------------------|
4754  *      |     ParamTypeArg      |
4755  *      |-----------------------|
4756  *      |  Last-executed-filter |
4757  *      |-----------------------|
4758  *      |                       |
4759  *      ~      Shadow SPs       ~
4760  *      |                       |
4761  *      |-----------------------|
4762  *      |                       |
4763  *      ~      Variables        ~
4764  *      |                       |
4765  *      ~-----------------------|
4766  *      |       Temps           |
4767  *      |-----------------------|
4768  *      |       localloc        |
4769  *      |-----------------------| <---- Ambient ESP
4770  *      |   Arguments for the   |
4771  *      |    next function      ~
4772  *      |                       |
4773  *      |       |               |
4774  *      |       | Stack grows   |
4775  *              | downward
4776  *              V
4777  *
4778  *
4779  *  The frame is laid out as follows for x64:
4780  *
4781  *              RSP frames
4782  *      |                       |
4783  *      |-----------------------|
4784  *      |       incoming        |
4785  *      |       arguments       |
4786  *      |-----------------------|
4787  *      |   4 fixed incoming    |
4788  *      |    argument slots     |
4789  *      |-----------------------| <---- Caller's SP & Virtual '0'
4790  *      |    return address     |
4791  *      +=======================+
4792  *      | Callee saved Int regs |
4793  *      -------------------------
4794  *      |        Padding        | <---- this padding (0 or 8 bytes) is to ensure flt registers are saved at a mem location aligned at 16-bytes
4795  *      |                       |       so that we can save 128-bit callee saved xmm regs using performant "movaps" instruction instead of "movups"
4796  *      -------------------------
4797  *      | Callee saved Flt regs | <----- entire 128-bits of callee saved xmm registers are stored here
4798  *      |-----------------------|
4799  *      |         Temps         |
4800  *      |-----------------------|
4801  *      |       Variables       |
4802  *      |-----------------------|
4803  *      |   Arguments for the   |
4804  *      ~    next function      ~
4805  *      |                       |
4806  *      |-----------------------|
4807  *      |   4 fixed outgoing    |
4808  *      |    argument slots     |
4809  *      |-----------------------| <---- Ambient RSP
4810  *      |       |               |
4811  *      ~       | Stack grows   ~
4812  *      |       | downward      |
4813  *              V
4814  *
4815  *
4816  *              RBP frames
4817  *      |                       |
4818  *      |-----------------------|
4819  *      |       incoming        |
4820  *      |       arguments       |
4821  *      |-----------------------|
4822  *      |   4 fixed incoming    |
4823  *      |    argument slots     |
4824  *      |-----------------------| <---- Caller's SP & Virtual '0'
4825  *      |    return address     |
4826  *      +=======================+
4827  *      | Callee saved Int regs |
4828  *      -------------------------
4829  *      |        Padding        |
4830  *      -------------------------
4831  *      | Callee saved Flt regs |
4832  *      |-----------------------|
4833  *      |   security object     |
4834  *      |-----------------------|
4835  *      |     ParamTypeArg      |
4836  *      |-----------------------|
4837  *      |                       |
4838  *      |                       |
4839  *      ~       Variables       ~
4840  *      |                       |
4841  *      |                       |
4842  *      |-----------------------|
4843  *      |        Temps          |
4844  *      |-----------------------|
4845  *      |                       |
4846  *      ~       localloc        ~   // not in frames with EH
4847  *      |                       |
4848  *      |-----------------------|
4849  *      |        PSPSym         |   // only in frames with EH (thus no localloc)
4850  *      |                       |
4851  *      |-----------------------| <---- RBP in localloc frames (max 240 bytes from Initial-SP)
4852  *      |   Arguments for the   |
4853  *      ~    next function      ~
4854  *      |                       |
4855  *      |-----------------------|
4856  *      |   4 fixed outgoing    |
4857  *      |    argument slots     |
4858  *      |-----------------------| <---- Ambient RSP (before localloc, this is Initial-SP)
4859  *      |       |               |
4860  *      ~       | Stack grows   ~
4861  *      |       | downward      |
4862  *              V
4863  *
4864  *
4865  *  The frame is laid out as follows for ARM (this is a general picture; details may differ for different conditions):
4866  *
4867  *              SP frames
4868  *      |                       |
4869  *      |-----------------------|
4870  *      |       incoming        |
4871  *      |       arguments       |
4872  *      +=======================+ <---- Caller's SP
4873  *      |  Pre-spill registers  |
4874  *      |-----------------------| <---- Virtual '0'
4875  *      |Callee saved registers |
4876  *      |-----------------------|
4877  *      ~ possible double align ~
4878  *      |-----------------------|
4879  *      |   security object     |
4880  *      |-----------------------|
4881  *      |     ParamTypeArg      |
4882  *      |-----------------------|
4883  *      |  possible GS cookie   |
4884  *      |-----------------------|
4885  *      |       Variables       |
4886  *      |-----------------------|
4887  *      |  possible GS cookie   |
4888  *      |-----------------------|
4889  *      |        Temps          |
4890  *      |-----------------------|
4891  *      |   Stub Argument Var   |
4892  *      |-----------------------|
4893  *      |Inlined PInvoke Frame V|
4894  *      |-----------------------|
4895  *      ~ possible double align ~
4896  *      |-----------------------|
4897  *      |   Arguments for the   |
4898  *      ~    next function      ~
4899  *      |                       |
4900  *      |-----------------------| <---- Ambient SP
4901  *      |       |               |
4902  *      ~       | Stack grows   ~
4903  *      |       | downward      |
4904  *              V
4905  *
4906  *
4907  *              FP / R11 frames
4908  *      |                       |
4909  *      |-----------------------|
4910  *      |       incoming        |
4911  *      |       arguments       |
4912  *      +=======================+ <---- Caller's SP
4913  *      |  Pre-spill registers  |
4914  *      |-----------------------| <---- Virtual '0'
4915  *      |Callee saved registers |
4916  *      |-----------------------|
4917  *      |        PSPSym         |   // Only for frames with EH, which means FP-based frames
4918  *      |-----------------------|
4919  *      ~ possible double align ~
4920  *      |-----------------------|
4921  *      |   security object     |
4922  *      |-----------------------|
4923  *      |     ParamTypeArg      |
4924  *      |-----------------------|
4925  *      |  possible GS cookie   |
4926  *      |-----------------------|
4927  *      |       Variables       |
4928  *      |-----------------------|
4929  *      |  possible GS cookie   |
4930  *      |-----------------------|
4931  *      |        Temps          |
4932  *      |-----------------------|
4933  *      |   Stub Argument Var   |
4934  *      |-----------------------|
4935  *      |Inlined PInvoke Frame V|
4936  *      |-----------------------|
4937  *      ~ possible double align ~
4938  *      |-----------------------|
4939  *      |       localloc        |
4940  *      |-----------------------|
4941  *      |   Arguments for the   |
4942  *      ~    next function      ~
4943  *      |                       |
4944  *      |-----------------------| <---- Ambient SP
4945  *      |       |               |
4946  *      ~       | Stack grows   ~
4947  *      |       | downward      |
4948  *              V
4949  *
4950  *
4951  *  The frame is laid out as follows for ARM64 (this is a general picture; details may differ for different conditions):
4952  *  NOTE: SP must be 16-byte aligned, so there may be alignment slots in the frame.
4953  *  We will often save and establish a frame pointer to create better ETW stack walks.
4954  *
4955  *              SP frames
4956  *      |                       |
4957  *      |-----------------------|
4958  *      |       incoming        |
4959  *      |       arguments       |
4960  *      +=======================+ <---- Caller's SP
4961  *      |         homed         | // this is only needed if reg argument need to be homed, e.g., for varargs
4962  *      |   register arguments  |
4963  *      |-----------------------| <---- Virtual '0'
4964  *      |Callee saved registers |
4965  *      |   except fp/lr        |
4966  *      |-----------------------|
4967  *      |   security object     |
4968  *      |-----------------------|
4969  *      |     ParamTypeArg      |
4970  *      |-----------------------|
4971  *      |  possible GS cookie   |
4972  *      |-----------------------|
4973  *      |       Variables       |
4974  *      |-----------------------|
4975  *      |  possible GS cookie   |
4976  *      |-----------------------|
4977  *      |        Temps          |
4978  *      |-----------------------|
4979  *      |   Stub Argument Var   |
4980  *      |-----------------------|
4981  *      |Inlined PInvoke Frame V|
4982  *      |-----------------------|
4983  *      |      Saved LR         |
4984  *      |-----------------------|
4985  *      |      Saved FP         | <---- Frame pointer
4986  *      |-----------------------|
4987  *      |  Stack arguments for  |
4988  *      |   the next function   |
4989  *      |-----------------------| <---- SP
4990  *      |       |               |
4991  *      ~       | Stack grows   ~
4992  *      |       | downward      |
4993  *              V
4994  *
4995  *
4996  *              FP (R29 / x29) frames
4997  *      |                       |
4998  *      |-----------------------|
4999  *      |       incoming        |
5000  *      |       arguments       |
5001  *      +=======================+ <---- Caller's SP
5002  *      |     optional homed    | // this is only needed if reg argument need to be homed, e.g., for varargs
5003  *      |   register arguments  |
5004  *      |-----------------------| <---- Virtual '0'
5005  *      |Callee saved registers |
5006  *      |   except fp/lr        |
5007  *      |-----------------------|
5008  *      |        PSPSym         | // Only for frames with EH, which requires FP-based frames
5009  *      |-----------------------|
5010  *      |   security object     |
5011  *      |-----------------------|
5012  *      |     ParamTypeArg      |
5013  *      |-----------------------|
5014  *      |  possible GS cookie   |
5015  *      |-----------------------|
5016  *      |       Variables       |
5017  *      |-----------------------|
5018  *      |  possible GS cookie   |
5019  *      |-----------------------|
5020  *      |        Temps          |
5021  *      |-----------------------|
5022  *      |   Stub Argument Var   |
5023  *      |-----------------------|
5024  *      |Inlined PInvoke Frame V|
5025  *      |-----------------------|
5026  *      |      Saved LR         |
5027  *      |-----------------------|
5028  *      |      Saved FP         | <---- Frame pointer
5029  *      |-----------------------|
5030  *      ~       localloc        ~
5031  *      |-----------------------|
5032  *      |  Stack arguments for  |
5033  *      |   the next function   |
5034  *      |-----------------------| <---- Ambient SP
5035  *      |       |               |
5036  *      ~       | Stack grows   ~
5037  *      |       | downward      |
5038  *              V
5039  *
5040  *
5041  *              FP (R29 / x29) frames where FP/LR are stored at the top of the frame (frames requiring GS that have localloc)
5042  *      |                       |
5043  *      |-----------------------|
5044  *      |       incoming        |
5045  *      |       arguments       |
5046  *      +=======================+ <---- Caller's SP
5047  *      |     optional homed    | // this is only needed if reg argument need to be homed, e.g., for varargs
5048  *      |   register arguments  |
5049  *      |-----------------------| <---- Virtual '0'
5050  *      |      Saved LR         |
5051  *      |-----------------------|
5052  *      |      Saved FP         | <---- Frame pointer
5053  *      |-----------------------|
5054  *      |Callee saved registers |
5055  *      |-----------------------|
5056  *      |        PSPSym         | // Only for frames with EH, which requires FP-based frames
5057  *      |-----------------------|
5058  *      |   security object     |
5059  *      |-----------------------|
5060  *      |     ParamTypeArg      |
5061  *      |-----------------------|
5062  *      |  possible GS cookie   |
5063  *      |-----------------------|
5064  *      |       Variables       |
5065  *      |-----------------------|
5066  *      |  possible GS cookie   |
5067  *      |-----------------------|
5068  *      |        Temps          |
5069  *      |-----------------------|
5070  *      |   Stub Argument Var   |
5071  *      |-----------------------|
5072  *      |Inlined PInvoke Frame V|
5073  *      |-----------------------|
5074  *      ~       localloc        ~
5075  *      |-----------------------|
5076  *      |  Stack arguments for  |
5077  *      |   the next function   |
5078  *      |-----------------------| <---- Ambient SP
5079  *      |       |               |
5080  *      ~       | Stack grows   ~
5081  *      |       | downward      |
5082  *              V
5083  *
5084  *
5085  *  Doing this all in one pass is 'hard'.  So instead we do it in 2 basic passes:
5086  *    1. Assign all the offsets relative to the Virtual '0'. Offsets above (the
5087  *      incoming arguments) are positive. Offsets below (everything else) are
5088  *      negative.  This pass also calcuates the total frame size (between Caller's
5089  *      SP/return address and the Ambient SP).
5090  *    2. Figure out where to place the frame pointer, and then adjust the offsets
5091  *      as needed for the final stack size and whether the offset is frame pointer
5092  *      relative or stack pointer relative.
5093  *
5094  */
5095 // clang-format on
5096
5097 void Compiler::lvaAssignFrameOffsets(FrameLayoutState curState)
5098 {
5099     noway_assert((lvaDoneFrameLayout < curState) || (curState == REGALLOC_FRAME_LAYOUT));
5100
5101     lvaDoneFrameLayout = curState;
5102
5103 #ifdef DEBUG
5104     if (verbose)
5105     {
5106
5107         printf("*************** In lvaAssignFrameOffsets");
5108         if (curState == INITIAL_FRAME_LAYOUT)
5109         {
5110             printf("(INITIAL_FRAME_LAYOUT)");
5111         }
5112         else if (curState == PRE_REGALLOC_FRAME_LAYOUT)
5113         {
5114             printf("(PRE_REGALLOC_FRAME_LAYOUT)");
5115         }
5116         else if (curState == REGALLOC_FRAME_LAYOUT)
5117         {
5118             printf("(REGALLOC_FRAME_LAYOUT)");
5119         }
5120         else if (curState == TENTATIVE_FRAME_LAYOUT)
5121         {
5122             printf("(TENTATIVE_FRAME_LAYOUT)");
5123         }
5124         else if (curState == FINAL_FRAME_LAYOUT)
5125         {
5126             printf("(FINAL_FRAME_LAYOUT)");
5127         }
5128         else
5129         {
5130             printf("(UNKNOWN)");
5131             unreached();
5132         }
5133         printf("\n");
5134     }
5135 #endif
5136
5137 #if FEATURE_FIXED_OUT_ARGS
5138     assert(lvaOutgoingArgSpaceVar != BAD_VAR_NUM);
5139 #endif // FEATURE_FIXED_OUT_ARGS
5140
5141     /*-------------------------------------------------------------------------
5142      *
5143      * First process the arguments.
5144      *
5145      *-------------------------------------------------------------------------
5146      */
5147
5148     lvaAssignVirtualFrameOffsetsToArgs();
5149
5150     /*-------------------------------------------------------------------------
5151      *
5152      * Now compute stack offsets for any variables that don't live in registers
5153      *
5154      *-------------------------------------------------------------------------
5155      */
5156
5157     lvaAssignVirtualFrameOffsetsToLocals();
5158
5159     lvaAlignFrame();
5160
5161     /*-------------------------------------------------------------------------
5162      *
5163      * Now patch the offsets
5164      *
5165      *-------------------------------------------------------------------------
5166      */
5167
5168     lvaFixVirtualFrameOffsets();
5169
5170     // Modify the stack offset for fields of promoted structs.
5171     lvaAssignFrameOffsetsToPromotedStructs();
5172
5173     /*-------------------------------------------------------------------------
5174      *
5175      * Finalize
5176      *
5177      *-------------------------------------------------------------------------
5178      */
5179
5180     // If it's not the final frame layout, then it's just an estimate. This means
5181     // we're allowed to once again write to these variables, even if we've read
5182     // from them to make tentative code generation or frame layout decisions.
5183     if (curState < FINAL_FRAME_LAYOUT)
5184     {
5185         codeGen->resetFramePointerUsedWritePhase();
5186     }
5187 }
5188
5189 /*****************************************************************************
5190  *  lvaFixVirtualFrameOffsets() : Now that everything has a virtual offset,
5191  *  determine the final value for the frame pointer (if needed) and then
5192  *  adjust all the offsets appropriately.
5193  *
5194  *  This routine fixes virtual offset to be relative to frame pointer or SP
5195  *  based on whether varDsc->lvFramePointerBased is true or false respectively.
5196  */
5197 void Compiler::lvaFixVirtualFrameOffsets()
5198 {
5199     LclVarDsc* varDsc;
5200
5201 #if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_AMD64)
5202     if (lvaPSPSym != BAD_VAR_NUM)
5203     {
5204         // We need to fix the offset of the PSPSym so there is no padding between it and the outgoing argument space.
5205         // Without this code, lvaAlignFrame might have put the padding lower than the PSPSym, which would be between
5206         // the PSPSym and the outgoing argument space.
5207         varDsc = lvaGetDesc(lvaPSPSym);
5208         assert(varDsc->lvFramePointerBased); // We always access it RBP-relative.
5209         assert(!varDsc->lvMustInit);         // It is never "must init".
5210         varDsc->SetStackOffset(codeGen->genCallerSPtoInitialSPdelta() + lvaLclSize(lvaOutgoingArgSpaceVar));
5211
5212         if (opts.IsOSR())
5213         {
5214             // With OSR RBP points at the base of the OSR frame, but the virtual offsets
5215             // are from the base of the Tier0 frame. Adjust.
5216             //
5217             varDsc->SetStackOffset(varDsc->GetStackOffset() - info.compPatchpointInfo->TotalFrameSize());
5218         }
5219     }
5220 #endif
5221
5222     // The delta to be added to virtual offset to adjust it relative to frame pointer or SP
5223     int delta = 0;
5224
5225 #ifdef TARGET_XARCH
5226     delta += REGSIZE_BYTES; // pushed PC (return address) for x86/x64
5227     JITDUMP("--- delta bump %d for RA\n", REGSIZE_BYTES);
5228
5229     if (codeGen->doubleAlignOrFramePointerUsed())
5230     {
5231         JITDUMP("--- delta bump %d for FP\n", REGSIZE_BYTES);
5232         delta += REGSIZE_BYTES; // pushed EBP (frame pointer)
5233     }
5234 #endif
5235
5236     if (!codeGen->isFramePointerUsed())
5237     {
5238         // pushed registers, return address, and padding
5239         JITDUMP("--- delta bump %d for RSP frame\n", codeGen->genTotalFrameSize());
5240         delta += codeGen->genTotalFrameSize();
5241     }
5242 #if defined(TARGET_ARM)
5243     else
5244     {
5245         // We set FP to be after LR, FP
5246         delta += 2 * REGSIZE_BYTES;
5247     }
5248 #elif defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
5249     else
5250     {
5251         // FP is used.
5252         JITDUMP("--- delta bump %d for FP frame\n", codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta());
5253         delta += codeGen->genTotalFrameSize() - codeGen->genSPtoFPdelta();
5254     }
5255 #endif // TARGET_AMD64 || TARGET_ARM64 || TARGET_LOONGARCH64 || TARGET_RISCV64
5256
5257     if (opts.IsOSR())
5258     {
5259 #if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
5260         // Stack offset includes Tier0 frame.
5261         //
5262         JITDUMP("--- delta bump %d for OSR + Tier0 frame\n", info.compPatchpointInfo->TotalFrameSize());
5263         delta += info.compPatchpointInfo->TotalFrameSize();
5264 #endif
5265     }
5266
5267     JITDUMP("--- virtual stack offset to actual stack offset delta is %d\n", delta);
5268
5269     unsigned lclNum;
5270     for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
5271     {
5272         bool doAssignStkOffs = true;
5273
5274         // Can't be relative to EBP unless we have an EBP
5275         noway_assert(!varDsc->lvFramePointerBased || codeGen->doubleAlignOrFramePointerUsed());
5276
5277         // Is this a non-param promoted struct field?
5278         //   if so then set doAssignStkOffs to false.
5279         //
5280         if (varDsc->lvIsStructField)
5281         {
5282             LclVarDsc*       parentvarDsc  = lvaGetDesc(varDsc->lvParentLcl);
5283             lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);
5284
5285 #if defined(TARGET_X86)
5286             // On x86, we set the stack offset for a promoted field
5287             // to match a struct parameter in lvAssignFrameOffsetsToPromotedStructs.
5288             if ((!varDsc->lvIsParam || parentvarDsc->lvIsParam) && promotionType == PROMOTION_TYPE_DEPENDENT)
5289 #else
5290             if (!varDsc->lvIsParam && promotionType == PROMOTION_TYPE_DEPENDENT)
5291 #endif
5292             {
5293                 doAssignStkOffs = false; // Assigned later in lvaAssignFrameOffsetsToPromotedStructs()
5294             }
5295         }
5296
5297         if (!varDsc->lvOnFrame)
5298         {
5299             if (!varDsc->lvIsParam
5300 #if !defined(TARGET_AMD64)
5301                 || (varDsc->lvIsRegArg
5302 #if defined(TARGET_ARM) && defined(PROFILING_SUPPORTED)
5303                     && compIsProfilerHookNeeded() &&
5304                     !lvaIsPreSpilled(lclNum, codeGen->regSet.rsMaskPreSpillRegs(false)) // We need assign stack offsets
5305                                                                                         // for prespilled arguments
5306 #endif
5307                     )
5308 #endif // !defined(TARGET_AMD64)
5309                     )
5310             {
5311                 doAssignStkOffs = false; // Not on frame or an incoming stack arg
5312             }
5313         }
5314
5315         if (doAssignStkOffs)
5316         {
5317             JITDUMP("-- V%02u was %d, now %d\n", lclNum, varDsc->GetStackOffset(), varDsc->GetStackOffset() + delta);
5318             varDsc->SetStackOffset(varDsc->GetStackOffset() + delta);
5319
5320 #if DOUBLE_ALIGN
5321             if (genDoubleAlign() && !codeGen->isFramePointerUsed())
5322             {
5323                 if (varDsc->lvFramePointerBased)
5324                 {
5325                     varDsc->SetStackOffset(varDsc->GetStackOffset() - delta);
5326
5327                     // We need to re-adjust the offsets of the parameters so they are EBP
5328                     // relative rather than stack/frame pointer relative
5329
5330                     varDsc->SetStackOffset(varDsc->GetStackOffset() +
5331                                            (2 * TARGET_POINTER_SIZE)); // return address and pushed EBP
5332
5333                     noway_assert(varDsc->GetStackOffset() >= FIRST_ARG_STACK_OFFS);
5334                 }
5335             }
5336 #endif
5337             // On System V environments the stkOffs could be 0 for params passed in registers.
5338             //
5339             // For normal methods only EBP relative references can have negative offsets.
5340             assert(codeGen->isFramePointerUsed() || varDsc->GetStackOffset() >= 0);
5341         }
5342     }
5343
5344     assert(codeGen->regSet.tmpAllFree());
5345     for (TempDsc* temp = codeGen->regSet.tmpListBeg(); temp != nullptr; temp = codeGen->regSet.tmpListNxt(temp))
5346     {
5347         temp->tdAdjustTempOffs(delta);
5348     }
5349
5350     lvaCachedGenericContextArgOffs += delta;
5351
5352 #if FEATURE_FIXED_OUT_ARGS
5353
5354     if (lvaOutgoingArgSpaceVar != BAD_VAR_NUM)
5355     {
5356         varDsc = lvaGetDesc(lvaOutgoingArgSpaceVar);
5357         varDsc->SetStackOffset(0);
5358         varDsc->lvFramePointerBased = false;
5359         varDsc->lvMustInit          = false;
5360     }
5361
5362 #endif // FEATURE_FIXED_OUT_ARGS
5363
5364 #if defined(TARGET_ARM64)
5365     // We normally add alignment below the locals between them and the outgoing
5366     // arg space area. When we store fp/lr(ra) at the bottom, however, this will
5367     // be below the alignment. So we should not apply the alignment adjustment to
5368     // them. It turns out we always store these at +0 and +8 of the FP,
5369     // so instead of dealing with skipping adjustment just for them we just set
5370     // them here always.
5371     assert(codeGen->isFramePointerUsed());
5372     if (lvaRetAddrVar != BAD_VAR_NUM)
5373     {
5374         lvaTable[lvaRetAddrVar].SetStackOffset(REGSIZE_BYTES);
5375     }
5376 #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
5377     assert(codeGen->isFramePointerUsed());
5378     if (lvaRetAddrVar != BAD_VAR_NUM)
5379     {
5380         // For LoongArch64 and RISCV64, the RA is below the fp. see the `genPushCalleeSavedRegisters`
5381         lvaTable[lvaRetAddrVar].SetStackOffset(-REGSIZE_BYTES);
5382     }
5383 #endif // !TARGET_LOONGARCH64
5384 }
5385
5386 #ifdef TARGET_ARM
5387 bool Compiler::lvaIsPreSpilled(unsigned lclNum, regMaskTP preSpillMask)
5388 {
5389     const LclVarDsc& desc = lvaTable[lclNum];
5390     return desc.lvIsRegArg && (preSpillMask & genRegMask(desc.GetArgReg()));
5391 }
5392 #endif // TARGET_ARM
5393
5394 //------------------------------------------------------------------------
5395 // lvaUpdateArgWithInitialReg: Set the initial register of a local variable
5396 //                             to the one assigned by the register allocator.
5397 //
5398 // Arguments:
5399 //    varDsc - the local variable descriptor
5400 //
5401 void Compiler::lvaUpdateArgWithInitialReg(LclVarDsc* varDsc)
5402 {
5403     noway_assert(varDsc->lvIsParam);
5404
5405     if (varDsc->lvIsRegCandidate())
5406     {
5407         varDsc->SetRegNum(varDsc->GetArgInitReg());
5408     }
5409 }
5410
5411 //------------------------------------------------------------------------
5412 // lvaUpdateArgsWithInitialReg() : For each argument variable descriptor, update
5413 //     its current register with the initial register as assigned by LSRA.
5414 //
5415 void Compiler::lvaUpdateArgsWithInitialReg()
5416 {
5417     if (!compLSRADone)
5418     {
5419         return;
5420     }
5421
5422     for (unsigned lclNum = 0; lclNum < info.compArgsCount; lclNum++)
5423     {
5424         LclVarDsc* varDsc = lvaGetDesc(lclNum);
5425
5426         if (varDsc->lvPromoted)
5427         {
5428             for (unsigned fieldVarNum = varDsc->lvFieldLclStart;
5429                  fieldVarNum < varDsc->lvFieldLclStart + varDsc->lvFieldCnt; ++fieldVarNum)
5430             {
5431                 LclVarDsc* fieldVarDsc = lvaGetDesc(fieldVarNum);
5432                 lvaUpdateArgWithInitialReg(fieldVarDsc);
5433             }
5434         }
5435         else
5436         {
5437             lvaUpdateArgWithInitialReg(varDsc);
5438         }
5439     }
5440 }
5441
5442 /*****************************************************************************
5443  *  lvaAssignVirtualFrameOffsetsToArgs() : Assign virtual stack offsets to the
5444  *  arguments, and implicit arguments (this ptr, return buffer, generics,
5445  *  and varargs).
5446  */
5447 void Compiler::lvaAssignVirtualFrameOffsetsToArgs()
5448 {
5449     unsigned lclNum  = 0;
5450     int      argOffs = 0;
5451 #ifdef UNIX_AMD64_ABI
5452     int callerArgOffset = 0;
5453 #endif // UNIX_AMD64_ABI
5454
5455     /*
5456         Assign stack offsets to arguments (in reverse order of passing).
5457
5458         This means that if we pass arguments left->right, we start at
5459         the end of the list and work backwards, for right->left we start
5460         with the first argument and move forward.
5461
5462         This is all relative to our Virtual '0'
5463      */
5464
5465     if (info.compArgOrder == Target::ARG_ORDER_L2R)
5466     {
5467         argOffs = compArgSize;
5468     }
5469
5470     /* Update the argOffs to reflect arguments that are passed in registers */
5471
5472     noway_assert(codeGen->intRegState.rsCalleeRegArgCount <= MAX_REG_ARG);
5473     noway_assert(compMacOsArm64Abi() || compArgSize >= codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES);
5474
5475     if (info.compArgOrder == Target::ARG_ORDER_L2R)
5476     {
5477         argOffs -= codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES;
5478     }
5479
5480     // Update the arg initial register locations.
5481     lvaUpdateArgsWithInitialReg();
5482
5483     /* Is there a "this" argument? */
5484
5485     if (!info.compIsStatic)
5486     {
5487         noway_assert(lclNum == info.compThisArg);
5488 #ifndef TARGET_X86
5489         argOffs =
5490             lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5491 #endif // TARGET_X86
5492         lclNum++;
5493     }
5494
5495     unsigned userArgsToSkip = 0;
5496 #if !defined(TARGET_ARM)
5497     // In the native instance method calling convention on Windows,
5498     // the this parameter comes before the hidden return buffer parameter.
5499     // So, we want to process the native "this" parameter before we process
5500     // the native return buffer parameter.
5501     if (TargetOS::IsWindows && callConvIsInstanceMethodCallConv(info.compCallConv))
5502     {
5503 #ifdef TARGET_X86
5504         if (!lvaTable[lclNum].lvIsRegArg)
5505         {
5506             argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs);
5507         }
5508 #elif !defined(UNIX_AMD64_ABI)
5509         argOffs              = lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs);
5510 #endif // TARGET_X86
5511         lclNum++;
5512         userArgsToSkip++;
5513     }
5514 #endif
5515
5516     /* if we have a hidden buffer parameter, that comes here */
5517
5518     if (info.compRetBuffArg != BAD_VAR_NUM)
5519     {
5520         noway_assert(lclNum == info.compRetBuffArg);
5521         argOffs =
5522             lvaAssignVirtualFrameOffsetToArg(lclNum, REGSIZE_BYTES, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5523         lclNum++;
5524     }
5525
5526 #if USER_ARGS_COME_LAST
5527
5528     //@GENERICS: extra argument for instantiation info
5529     if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
5530     {
5531         noway_assert(lclNum == (unsigned)info.compTypeCtxtArg);
5532         argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
5533                                                    argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5534     }
5535
5536     if (info.compIsVarArgs)
5537     {
5538         argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
5539                                                    argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5540     }
5541
5542 #endif // USER_ARGS_COME_LAST
5543
5544     CORINFO_ARG_LIST_HANDLE argLst    = info.compMethodInfo->args.args;
5545     unsigned                argSigLen = info.compMethodInfo->args.numArgs;
5546     // Skip any user args that we've already processed.
5547     assert(userArgsToSkip <= argSigLen);
5548     argSigLen -= userArgsToSkip;
5549     for (unsigned i = 0; i < userArgsToSkip; i++, argLst = info.compCompHnd->getArgNext(argLst))
5550     {
5551         ;
5552     }
5553
5554 #ifdef TARGET_ARM
5555     //
5556     // struct_n { int; int; ... n times };
5557     //
5558     // Consider signature:
5559     //
5560     // Foo (float a,double b,float c,double d,float e,double f,float g,double h,
5561     //      float i,double j,float k,double l,struct_3 m) { }
5562     //
5563     // Basically the signature is: (all float regs full, 1 double, struct_3);
5564     //
5565     // The double argument occurs before pre spill in the argument iteration and
5566     // computes an argOffset of 0. struct_3 offset becomes 8. This is wrong.
5567     // Because struct_3 is prespilled and double occurs after prespill.
5568     // The correct offsets are double = 16 (aligned stk), struct_3 = 0..12,
5569     // Offset 12 will be skipped for double alignment of double.
5570     //
5571     // Another example is (struct_2, all float regs full, double, struct_2);
5572     // Here, notice the order is similarly messed up because of 2 pre-spilled
5573     // struct_2.
5574     //
5575     // Succinctly,
5576     // ARG_INDEX(i) > ARG_INDEX(j) DOES NOT IMPLY |ARG_OFFSET(i)| > |ARG_OFFSET(j)|
5577     //
5578     // Therefore, we'll do a two pass offset calculation, one that considers pre-spill
5579     // and the next, stack args.
5580     //
5581
5582     unsigned argLcls = 0;
5583
5584     // Take care of pre spill registers first.
5585     regMaskTP preSpillMask = codeGen->regSet.rsMaskPreSpillRegs(false);
5586     regMaskTP tempMask     = RBM_NONE;
5587     for (unsigned i = 0, preSpillLclNum = lclNum; i < argSigLen; ++i, ++preSpillLclNum)
5588     {
5589         if (lvaIsPreSpilled(preSpillLclNum, preSpillMask))
5590         {
5591             unsigned argSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
5592             argOffs          = lvaAssignVirtualFrameOffsetToArg(preSpillLclNum, argSize, argOffs);
5593             argLcls++;
5594
5595             // Early out if we can. If size is 8 and base reg is 2, then the mask is 0x1100
5596             tempMask |= ((((1 << (roundUp(argSize, TARGET_POINTER_SIZE) / REGSIZE_BYTES))) - 1)
5597                          << lvaTable[preSpillLclNum].GetArgReg());
5598             if (tempMask == preSpillMask)
5599             {
5600                 // We won't encounter more pre-spilled registers,
5601                 // so don't bother iterating further.
5602                 break;
5603             }
5604         }
5605         argLst = info.compCompHnd->getArgNext(argLst);
5606     }
5607
5608     // Take care of non pre-spilled stack arguments.
5609     argLst = info.compMethodInfo->args.args;
5610     for (unsigned i = 0, stkLclNum = lclNum; i < argSigLen; ++i, ++stkLclNum)
5611     {
5612         if (!lvaIsPreSpilled(stkLclNum, preSpillMask))
5613         {
5614             const unsigned argSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
5615             argOffs                = lvaAssignVirtualFrameOffsetToArg(stkLclNum, argSize, argOffs);
5616             argLcls++;
5617         }
5618         argLst = info.compCompHnd->getArgNext(argLst);
5619     }
5620
5621     lclNum += argLcls;
5622 #else  // !TARGET_ARM
5623     for (unsigned i = 0; i < argSigLen; i++)
5624     {
5625         unsigned argumentSize = eeGetArgSize(argLst, &info.compMethodInfo->args);
5626
5627         assert(compMacOsArm64Abi() || argumentSize % TARGET_POINTER_SIZE == 0);
5628
5629         argOffs =
5630             lvaAssignVirtualFrameOffsetToArg(lclNum++, argumentSize, argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5631         argLst = info.compCompHnd->getArgNext(argLst);
5632     }
5633 #endif // !TARGET_ARM
5634
5635 #if !USER_ARGS_COME_LAST
5636
5637     //@GENERICS: extra argument for instantiation info
5638     if (info.compMethodInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE)
5639     {
5640         noway_assert(lclNum == (unsigned)info.compTypeCtxtArg);
5641         argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
5642                                                    argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5643     }
5644
5645     if (info.compIsVarArgs)
5646     {
5647         argOffs = lvaAssignVirtualFrameOffsetToArg(lclNum++, REGSIZE_BYTES,
5648                                                    argOffs UNIX_AMD64_ABI_ONLY_ARG(&callerArgOffset));
5649     }
5650
5651 #endif // USER_ARGS_COME_LAST
5652 }
5653
5654 #ifdef UNIX_AMD64_ABI
5655 //
5656 //  lvaAssignVirtualFrameOffsetToArg() : Assign virtual stack offsets to an
5657 //  individual argument, and return the offset for the next argument.
5658 //  Note: This method only calculates the initial offset of the stack passed/spilled arguments
5659 //  (if any - the RA might decide to spill(home on the stack) register passed arguments, if rarely used.)
5660 //        The final offset is calculated in lvaFixVirtualFrameOffsets method. It accounts for FP existence,
5661 //        ret address slot, stack frame padding, alloca instructions, etc.
5662 //  Note: This is the implementation for UNIX_AMD64 System V platforms.
5663 //
5664 int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum,
5665                                                unsigned argSize,
5666                                                int argOffs UNIX_AMD64_ABI_ONLY_ARG(int* callerArgOffset))
5667 {
5668     noway_assert(lclNum < info.compArgsCount);
5669     noway_assert(argSize);
5670
5671     if (info.compArgOrder == Target::ARG_ORDER_L2R)
5672     {
5673         argOffs -= argSize;
5674     }
5675
5676     unsigned fieldVarNum = BAD_VAR_NUM;
5677
5678     LclVarDsc* varDsc = lvaGetDesc(lclNum);
5679
5680     noway_assert(varDsc->lvIsParam);
5681
5682     if (varDsc->lvIsRegArg)
5683     {
5684         // Argument is passed in a register, don't count it
5685         // when updating the current offset on the stack.
5686
5687         if (varDsc->lvOnFrame)
5688         {
5689             // The offset for args needs to be set only for the stack homed arguments for System V.
5690             varDsc->SetStackOffset(argOffs);
5691         }
5692         else
5693         {
5694             varDsc->SetStackOffset(0);
5695         }
5696     }
5697     else
5698     {
5699         // For Windows AMD64 there are 4 slots for the register passed arguments on the top of the caller's stack.
5700         // This is where they are always homed. So, they can be accessed with positive offset.
5701         // On System V platforms, if the RA decides to home a register passed arg on the stack, it creates a stack
5702         // location on the callee stack (like any other local var.) In such a case, the register passed, stack homed
5703         // arguments are accessed using negative offsets and the stack passed arguments are accessed using positive
5704         // offset (from the caller's stack.)
5705         // For  System V platforms if there is no frame pointer the caller stack parameter offset should include the
5706         // callee allocated space. If frame register is used, the callee allocated space should not be included for
5707         // accessing the caller stack parameters. The last two requirements are met in lvaFixVirtualFrameOffsets
5708         // method, which fixes the offsets, based on frame pointer existence, existence of alloca instructions, ret
5709         // address pushed, ets.
5710
5711         varDsc->SetStackOffset(*callerArgOffset);
5712         // Structs passed on stack could be of size less than TARGET_POINTER_SIZE.
5713         // Make sure they get at least TARGET_POINTER_SIZE on the stack - this is required for alignment.
5714         if (argSize > TARGET_POINTER_SIZE)
5715         {
5716             *callerArgOffset += (int)roundUp(argSize, TARGET_POINTER_SIZE);
5717         }
5718         else
5719         {
5720             *callerArgOffset += TARGET_POINTER_SIZE;
5721         }
5722     }
5723
5724     // For struct promoted parameters we need to set the offsets for the field lclVars.
5725     //
5726     // For a promoted struct we also assign the struct fields stack offset
5727     if (varDsc->lvPromoted)
5728     {
5729         unsigned firstFieldNum = varDsc->lvFieldLclStart;
5730         int      offset        = varDsc->GetStackOffset();
5731         for (unsigned i = 0; i < varDsc->lvFieldCnt; i++)
5732         {
5733             LclVarDsc* fieldVarDsc = lvaGetDesc(firstFieldNum + i);
5734             fieldVarDsc->SetStackOffset(offset + fieldVarDsc->lvFldOffset);
5735         }
5736     }
5737
5738     if (info.compArgOrder == Target::ARG_ORDER_R2L && !varDsc->lvIsRegArg)
5739     {
5740         argOffs += argSize;
5741     }
5742
5743     return argOffs;
5744 }
5745
5746 #else // !UNIX_AMD64_ABI
5747
5748 //
5749 //  lvaAssignVirtualFrameOffsetToArg() : Assign virtual stack offsets to an
5750 //  individual argument, and return the offset for the next argument.
5751 //  Note: This method only calculates the initial offset of the stack passed/spilled arguments
5752 //  (if any - the RA might decide to spill(home on the stack) register passed arguments, if rarely used.)
5753 //        The final offset is calculated in lvaFixVirtualFrameOffsets method. It accounts for FP existence,
5754 //        ret address slot, stack frame padding, alloca instructions, etc.
5755 //  Note: This implementation for all the platforms but UNIX_AMD64 OSs (System V 64 bit.)
5756 int Compiler::lvaAssignVirtualFrameOffsetToArg(unsigned lclNum,
5757                                                unsigned argSize,
5758                                                int argOffs UNIX_AMD64_ABI_ONLY_ARG(int* callerArgOffset))
5759 {
5760     noway_assert(lclNum < info.compArgsCount);
5761     noway_assert(argSize);
5762
5763     if (info.compArgOrder == Target::ARG_ORDER_L2R)
5764     {
5765         argOffs -= argSize;
5766     }
5767
5768     unsigned fieldVarNum = BAD_VAR_NUM;
5769
5770     LclVarDsc* varDsc = lvaGetDesc(lclNum);
5771
5772     noway_assert(varDsc->lvIsParam);
5773
5774     if (varDsc->lvIsRegArg)
5775     {
5776         /* Argument is passed in a register, don't count it
5777          * when updating the current offset on the stack */
5778         CLANG_FORMAT_COMMENT_ANCHOR;
5779
5780 #if !defined(TARGET_ARMARCH) && !defined(TARGET_LOONGARCH64) && !defined(TARGET_RISCV64)
5781 #if DEBUG
5782         // TODO: Remove this noway_assert and replace occurrences of TARGET_POINTER_SIZE with argSize
5783         // Also investigate why we are incrementing argOffs for X86 as this seems incorrect
5784         //
5785         noway_assert(argSize == TARGET_POINTER_SIZE);
5786 #endif // DEBUG
5787 #endif
5788
5789 #if defined(TARGET_X86)
5790         argOffs += TARGET_POINTER_SIZE;
5791 #elif defined(TARGET_AMD64)
5792         // Register arguments on AMD64 also takes stack space. (in the backing store)
5793         varDsc->SetStackOffset(argOffs);
5794         argOffs += TARGET_POINTER_SIZE;
5795 #elif defined(TARGET_ARM64)
5796         // Register arguments on ARM64 only take stack space when they have a frame home.
5797         // Unless on windows and in a vararg method.
5798         if (compFeatureArgSplit() && this->info.compIsVarArgs)
5799         {
5800             if (varDsc->lvType == TYP_STRUCT && varDsc->GetOtherArgReg() >= MAX_REG_ARG &&
5801                 varDsc->GetOtherArgReg() != REG_NA)
5802             {
5803                 // This is a split struct. It will account for an extra (8 bytes)
5804                 // of alignment.
5805                 varDsc->SetStackOffset(varDsc->GetStackOffset() + TARGET_POINTER_SIZE);
5806                 argOffs += TARGET_POINTER_SIZE;
5807             }
5808         }
5809
5810 #elif defined(TARGET_ARM)
5811         // On ARM we spill the registers in codeGen->regSet.rsMaskPreSpillRegArg
5812         // in the prolog, so we have to do SetStackOffset() here
5813         //
5814         regMaskTP regMask = genRegMask(varDsc->GetArgReg());
5815         if (codeGen->regSet.rsMaskPreSpillRegArg & regMask)
5816         {
5817             // Signature: void foo(struct_8, int, struct_4)
5818             // ------- CALLER SP -------
5819             // r3 struct_4
5820             // r2 int - not prespilled, but added for alignment. argOffs should skip this.
5821             // r1 struct_8
5822             // r0 struct_8
5823             // -------------------------
5824             // If we added alignment we need to fix argOffs for all registers above alignment.
5825             if (codeGen->regSet.rsMaskPreSpillAlign != RBM_NONE)
5826             {
5827                 assert(genCountBits(codeGen->regSet.rsMaskPreSpillAlign) == 1);
5828                 // Is register beyond the alignment pos?
5829                 if (regMask > codeGen->regSet.rsMaskPreSpillAlign)
5830                 {
5831                     // Increment argOffs just once for the _first_ register after alignment pos
5832                     // in the prespill mask.
5833                     if (!BitsBetween(codeGen->regSet.rsMaskPreSpillRegArg, regMask,
5834                                      codeGen->regSet.rsMaskPreSpillAlign))
5835                     {
5836                         argOffs += TARGET_POINTER_SIZE;
5837                     }
5838                 }
5839             }
5840
5841             switch (varDsc->lvType)
5842             {
5843                 case TYP_STRUCT:
5844                     if (!varDsc->lvStructDoubleAlign)
5845                     {
5846                         break;
5847                     }
5848                     FALLTHROUGH;
5849
5850                 case TYP_DOUBLE:
5851                 case TYP_LONG:
5852                 {
5853                     //
5854                     // Let's assign offsets to arg1, a double in r2. argOffs has to be 4 not 8.
5855                     //
5856                     // ------- CALLER SP -------
5857                     // r3
5858                     // r2 double   -- argOffs = 4, but it doesn't need to be skipped, because there is no skipping.
5859                     // r1 VACookie -- argOffs = 0
5860                     // -------------------------
5861                     //
5862                     // Consider argOffs as if it accounts for number of prespilled registers before the current
5863                     // register. In the above example, for r2, it is r1 that is prespilled, but since r1 is
5864                     // accounted for by argOffs being 4, there should have been no skipping. Instead, if we didn't
5865                     // assign r1 to any variable, then argOffs would still be 0 which implies it is not accounting
5866                     // for r1, equivalently r1 is skipped.
5867                     //
5868                     // If prevRegsSize is unaccounted for by a corresponding argOffs, we must have skipped a register.
5869                     int prevRegsSize =
5870                         genCountBits(codeGen->regSet.rsMaskPreSpillRegArg & (regMask - 1)) * TARGET_POINTER_SIZE;
5871                     if (argOffs < prevRegsSize)
5872                     {
5873                         // We must align up the argOffset to a multiple of 8 to account for skipped registers.
5874                         argOffs = roundUp((unsigned)argOffs, 2 * TARGET_POINTER_SIZE);
5875                     }
5876                     // We should've skipped only a single register.
5877                     assert(argOffs == prevRegsSize);
5878                 }
5879                 break;
5880
5881                 default:
5882                     // No alignment of argOffs required
5883                     break;
5884             }
5885             varDsc->SetStackOffset(argOffs);
5886             argOffs += argSize;
5887         }
5888
5889 #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
5890
5891         if (varDsc->lvIsSplit)
5892         {
5893             assert((varDsc->lvType == TYP_STRUCT) && (varDsc->GetOtherArgReg() == REG_STK));
5894             // This is a split struct. It will account for an extra (8 bytes) for the whole struct.
5895             varDsc->SetStackOffset(varDsc->GetStackOffset() + TARGET_POINTER_SIZE);
5896             argOffs += TARGET_POINTER_SIZE;
5897         }
5898
5899 #else // TARGET*
5900 #error Unsupported or unset target architecture
5901 #endif // TARGET*
5902     }
5903     else
5904     {
5905 #if defined(TARGET_ARM)
5906         // Dev11 Bug 42817: incorrect codegen for DrawFlatCheckBox causes A/V in WinForms
5907         //
5908         // Here we have method with a signature (int a1, struct a2, struct a3, int a4, int a5).
5909         // Struct parameter 'a2' is 16-bytes with no alignment requirements;
5910         //  it uses r1,r2,r3 and [OutArg+0] when passed.
5911         // Struct parameter 'a3' is 16-bytes that is required to be double aligned;
5912         //  the caller skips [OutArg+4] and starts the argument at [OutArg+8].
5913         // Thus the caller generates the correct code to pass the arguments.
5914         // When generating code to receive the arguments we set codeGen->regSet.rsMaskPreSpillRegArg to [r1,r2,r3]
5915         //  and spill these three registers as the first instruction in the prolog.
5916         // Then when we layout the arguments' stack offsets we have an argOffs 0 which
5917         //  points at the location that we spilled r1 into the stack.  For this first
5918         //  struct we take the lvIsRegArg path above with "codeGen->regSet.rsMaskPreSpillRegArg &" matching.
5919         // Next when we calculate the argOffs for the second 16-byte struct we have an argOffs
5920         //  of 16, which appears to be aligned properly so we don't skip a stack slot.
5921         //
5922         // To fix this we must recover the actual OutArg offset by subtracting off the
5923         //  sizeof of the PreSpill register args.
5924         // Then we align this offset to a multiple of 8 and add back the sizeof
5925         //  of the PreSpill register args.
5926         //
5927         // Dev11 Bug 71767: failure of assert(sizeofPreSpillRegArgs <= argOffs)
5928         //
5929         // We have a method with 'this' passed in r0, RetBuf arg in r1, VarArgs cookie
5930         // in r2. The first user arg is a 144 byte struct with double alignment required,
5931         // r3 is skipped, and the struct is passed on the stack. However, 'r3' is added
5932         // to the codeGen->regSet.rsMaskPreSpillRegArg mask by the VarArgs cookie code, since we need to
5933         // home all the potential varargs arguments in registers, even if we don't have
5934         // signature type information for the variadic arguments. However, due to alignment,
5935         // we have skipped a register that doesn't have a corresponding symbol. Make up
5936         // for that by increasing argOffs here.
5937         //
5938
5939         int sizeofPreSpillRegArgs = genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true)) * REGSIZE_BYTES;
5940
5941         if (argOffs < sizeofPreSpillRegArgs)
5942         {
5943             // This can only happen if we skipped the last register spot because current stk arg
5944             // is a struct requiring alignment or a pre-spill alignment was required because the
5945             // first reg arg needed alignment.
5946             //
5947             // Example 1: First Stk Argument requiring alignment in vararg case (same as above comment.)
5948             //            Signature (int a0, int a1, int a2, struct {long} a3, ...)
5949             //
5950             // stk arg    a3             --> argOffs here will be 12 (r0-r2) but pre-spill will be 16.
5951             // ---- Caller SP ----
5952             // r3                        --> Stack slot is skipped in this case.
5953             // r2    int  a2
5954             // r1    int  a1
5955             // r0    int  a0
5956             //
5957             // Example 2: First Reg Argument requiring alignment in no-vararg case.
5958             //            Signature (struct {long} a0, struct {int} a1, int a2, int a3)
5959             //
5960             // stk arg                  --> argOffs here will be 12 {r0-r2} but pre-spill will be 16.
5961             // ---- Caller SP ----
5962             // r3    int             a2 --> pushed (not pre-spilled) for alignment of a0 by lvaInitUserArgs.
5963             // r2    struct { int }  a1
5964             // r0-r1 struct { long } a0
5965             CLANG_FORMAT_COMMENT_ANCHOR;
5966
5967 #ifdef PROFILING_SUPPORTED
5968             // On Arm under profiler, r0-r3 are always prespilled on stack.
5969             // It is possible to have methods that accept only HFAs as parameters e.g. Signature(struct hfa1, struct
5970             // hfa2), in which case hfa1 and hfa2 will be en-registered in co-processor registers and will have an
5971             // argument offset less than size of preSpill.
5972             //
5973             // For this reason the following conditions are asserted when not under profiler.
5974             if (!compIsProfilerHookNeeded())
5975 #endif
5976             {
5977                 bool cond = ((info.compIsVarArgs || opts.compUseSoftFP) &&
5978                              // Does cur stk arg require double alignment?
5979                              ((varDsc->lvType == TYP_STRUCT && varDsc->lvStructDoubleAlign) ||
5980                               (varDsc->lvType == TYP_DOUBLE) || (varDsc->lvType == TYP_LONG))) ||
5981                             // Did first reg arg require alignment?
5982                             (codeGen->regSet.rsMaskPreSpillAlign & genRegMask(REG_ARG_LAST));
5983
5984                 noway_assert(cond);
5985                 noway_assert(sizeofPreSpillRegArgs <=
5986                              argOffs + TARGET_POINTER_SIZE); // at most one register of alignment
5987             }
5988             argOffs = sizeofPreSpillRegArgs;
5989         }
5990
5991         noway_assert(argOffs >= sizeofPreSpillRegArgs);
5992         int argOffsWithoutPreSpillRegArgs = argOffs - sizeofPreSpillRegArgs;
5993
5994         switch (varDsc->lvType)
5995         {
5996             case TYP_STRUCT:
5997                 if (!varDsc->lvStructDoubleAlign)
5998                     break;
5999
6000                 FALLTHROUGH;
6001
6002             case TYP_DOUBLE:
6003             case TYP_LONG:
6004                 // We must align up the argOffset to a multiple of 8
6005                 argOffs =
6006                     roundUp((unsigned)argOffsWithoutPreSpillRegArgs, 2 * TARGET_POINTER_SIZE) + sizeofPreSpillRegArgs;
6007                 break;
6008
6009             default:
6010                 // No alignment of argOffs required
6011                 break;
6012         }
6013 #endif // TARGET_ARM
6014         const bool     isFloatHfa   = (varDsc->lvIsHfa() && (varDsc->GetHfaType() == TYP_FLOAT));
6015         const unsigned argAlignment = eeGetArgSizeAlignment(varDsc->lvType, isFloatHfa);
6016         if (compMacOsArm64Abi())
6017         {
6018             argOffs = roundUp(argOffs, argAlignment);
6019         }
6020
6021         assert((argSize % argAlignment) == 0);
6022         assert((argOffs % argAlignment) == 0);
6023         varDsc->SetStackOffset(argOffs);
6024     }
6025
6026     // For struct promoted parameters we need to set the offsets for both LclVars.
6027     //
6028     // For a dependent promoted struct we also assign the struct fields stack offset
6029     CLANG_FORMAT_COMMENT_ANCHOR;
6030
6031     if (varDsc->lvPromoted)
6032     {
6033         unsigned firstFieldNum = varDsc->lvFieldLclStart;
6034         for (unsigned i = 0; i < varDsc->lvFieldCnt; i++)
6035         {
6036             LclVarDsc* fieldVarDsc = lvaGetDesc(firstFieldNum + i);
6037
6038             JITDUMP("Adjusting offset of dependent V%02u of arg V%02u: parent %u field %u net %u\n", lclNum,
6039                     firstFieldNum + i, varDsc->GetStackOffset(), fieldVarDsc->lvFldOffset,
6040                     varDsc->GetStackOffset() + fieldVarDsc->lvFldOffset);
6041
6042             fieldVarDsc->SetStackOffset(varDsc->GetStackOffset() + fieldVarDsc->lvFldOffset);
6043         }
6044     }
6045
6046     if (info.compArgOrder == Target::ARG_ORDER_R2L && !varDsc->lvIsRegArg)
6047     {
6048         argOffs += argSize;
6049     }
6050
6051     return argOffs;
6052 }
6053 #endif // !UNIX_AMD64_ABI
6054
6055 //-----------------------------------------------------------------------------
6056 // lvaAssignVirtualFrameOffsetsToLocals: compute the virtual stack offsets for
6057 //  all elements on the stackframe.
6058 //
6059 // Notes:
6060 //  Can be called multiple times. Early calls can be used to estimate various
6061 //  frame offsets, but details may change.
6062 //
6063 void Compiler::lvaAssignVirtualFrameOffsetsToLocals()
6064 {
6065     // (1) Account for things that are set up by the prolog and undone by the epilog.
6066     //
6067     int stkOffs              = 0;
6068     int originalFrameStkOffs = 0;
6069     int originalFrameSize    = 0;
6070     // codeGen->isFramePointerUsed is set in regalloc phase. Initialize it to a guess for pre-regalloc layout.
6071     if (lvaDoneFrameLayout <= PRE_REGALLOC_FRAME_LAYOUT)
6072     {
6073         codeGen->setFramePointerUsed(codeGen->isFramePointerRequired());
6074     }
6075
6076 #ifdef TARGET_ARM64
6077     // Decide where to save FP and LR registers. We store FP/LR registers at the bottom of the frame if there is
6078     // a frame pointer used (so we get positive offsets from the frame pointer to access locals), but not if we
6079     // need a GS cookie AND localloc is used, since we need the GS cookie to protect the saved return value,
6080     // and also the saved frame pointer. See CodeGen::genPushCalleeSavedRegisters() for more details about the
6081     // frame types. Since saving FP/LR at high addresses is a relatively rare case, force using it during stress.
6082     // (It should be legal to use these frame types for every frame).
6083
6084     if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 0)
6085     {
6086         // Default configuration
6087         codeGen->SetSaveFpLrWithAllCalleeSavedRegisters((getNeedsGSSecurityCookie() && compLocallocUsed) ||
6088                                                         opts.compDbgEnC || compStressCompile(STRESS_GENERIC_VARN, 20));
6089     }
6090     else if (opts.compJitSaveFpLrWithCalleeSavedRegisters == 1)
6091     {
6092         codeGen->SetSaveFpLrWithAllCalleeSavedRegisters(false); // Disable using new frames
6093     }
6094     else if ((opts.compJitSaveFpLrWithCalleeSavedRegisters == 2) || (opts.compJitSaveFpLrWithCalleeSavedRegisters == 3))
6095     {
6096         codeGen->SetSaveFpLrWithAllCalleeSavedRegisters(true); // Force using new frames
6097     }
6098 #endif // TARGET_ARM64
6099
6100 #ifdef TARGET_XARCH
6101     // On x86/amd64, the return address has already been pushed by the call instruction in the caller.
6102     stkOffs -= TARGET_POINTER_SIZE; // return address;
6103     if (lvaRetAddrVar != BAD_VAR_NUM)
6104     {
6105         lvaTable[lvaRetAddrVar].SetStackOffset(stkOffs);
6106     }
6107 #endif
6108
6109     // If we are an OSR method, we "inherit" the frame of the original method
6110     //
6111     if (opts.IsOSR())
6112     {
6113         originalFrameSize    = info.compPatchpointInfo->TotalFrameSize();
6114         originalFrameStkOffs = stkOffs;
6115         stkOffs -= originalFrameSize;
6116     }
6117
6118 #ifdef TARGET_XARCH
6119     // TODO-AMD64-CQ: for X64 eventually this should be pushed with all the other
6120     // calleeregs.  When you fix this, you'll also need to fix
6121     // the assert at the bottom of this method
6122     if (codeGen->doubleAlignOrFramePointerUsed())
6123     {
6124         stkOffs -= REGSIZE_BYTES;
6125     }
6126 #endif
6127
6128     int  preSpillSize    = 0;
6129     bool mustDoubleAlign = false;
6130
6131 #ifdef TARGET_ARM
6132     mustDoubleAlign = true;
6133     preSpillSize    = genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true)) * REGSIZE_BYTES;
6134 #else // !TARGET_ARM
6135 #if DOUBLE_ALIGN
6136     if (genDoubleAlign())
6137     {
6138         mustDoubleAlign = true; // X86 only
6139     }
6140 #endif
6141 #endif // !TARGET_ARM
6142
6143 #ifdef TARGET_ARM64
6144     // If the frame pointer is used, then we'll save FP/LR at the bottom of the stack.
6145     // Otherwise, we won't store FP, and we'll store LR at the top, with the other callee-save
6146     // registers (if any).
6147
6148     int initialStkOffs = 0;
6149     if (info.compIsVarArgs)
6150     {
6151         // For varargs we always save all of the integer register arguments
6152         // so that they are contiguous with the incoming stack arguments.
6153         initialStkOffs = MAX_REG_ARG * REGSIZE_BYTES;
6154         stkOffs -= initialStkOffs;
6155     }
6156
6157     if (codeGen->IsSaveFpLrWithAllCalleeSavedRegisters() ||
6158         !isFramePointerUsed()) // Note that currently we always have a frame pointer
6159     {
6160         stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES;
6161     }
6162     else
6163     {
6164         // Subtract off FP and LR.
6165         assert(compCalleeRegsPushed >= 2);
6166         stkOffs -= (compCalleeRegsPushed - 2) * REGSIZE_BYTES;
6167     }
6168
6169 #elif defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
6170
6171     assert(compCalleeRegsPushed >= 2);
6172
6173 #else // !TARGET_LOONGARCH64 && !TARGET_RISCV64
6174 #ifdef TARGET_ARM
6175     // On ARM32 LR is part of the pushed registers and is always stored at the
6176     // top.
6177     if (lvaRetAddrVar != BAD_VAR_NUM)
6178     {
6179         lvaTable[lvaRetAddrVar].SetStackOffset(stkOffs - REGSIZE_BYTES);
6180     }
6181 #endif
6182
6183     stkOffs -= compCalleeRegsPushed * REGSIZE_BYTES;
6184 #endif // !TARGET_LOONGARCH64 && !TARGET_RISCV64
6185
6186     // (2) Account for the remainder of the frame
6187     //
6188     // From this point on the code must generally adjust both
6189     // stkOffs and the local frame size. The latter is done via:
6190     //
6191     //   lvaIncrementFrameSize -- for space not associated with a local var
6192     //   lvaAllocLocalAndSetVirtualOffset -- for space associated with a local var
6193     //
6194     // One exception to the above: OSR locals that have offsets within the Tier0
6195     // portion of the frame.
6196     //
6197     compLclFrameSize = 0;
6198
6199 #ifdef TARGET_AMD64
6200     // For methods with patchpoints, the Tier0 method must reserve
6201     // space for all the callee saves, as this area is shared with the
6202     // OSR method, and we have to anticipate that collectively the
6203     // Tier0 and OSR methods end up saving all callee saves.
6204     //
6205     // Currently this is x64 only.
6206     //
6207     if (doesMethodHavePatchpoints() || doesMethodHavePartialCompilationPatchpoints())
6208     {
6209         const unsigned regsPushed    = compCalleeRegsPushed + (codeGen->isFramePointerUsed() ? 1 : 0);
6210         const unsigned extraSlots    = genCountBits(RBM_OSR_INT_CALLEE_SAVED) - regsPushed;
6211         const unsigned extraSlotSize = extraSlots * REGSIZE_BYTES;
6212
6213         JITDUMP("\nMethod has patchpoints and has %u callee saves.\n"
6214                 "Reserving %u extra slots (%u bytes) for potential OSR method callee saves\n",
6215                 regsPushed, extraSlots, extraSlotSize);
6216
6217         stkOffs -= extraSlotSize;
6218         lvaIncrementFrameSize(extraSlotSize);
6219     }
6220
6221     // In case of Amd64 compCalleeRegsPushed does not include float regs (xmm6-xmm31) that
6222     // need to be pushed.  But Amd64 doesn't support push/pop of xmm registers.
6223     // Instead we need to allocate space for them on the stack and save them in prolog.
6224     // Therefore, we consider xmm registers being saved while computing stack offsets
6225     // but space for xmm registers is considered part of compLclFrameSize.
6226     // Notes
6227     //  1) We need to save the entire 128-bits of xmm register to stack, since amd64
6228     //     prolog unwind codes allow encoding of an instruction that stores the entire xmm reg
6229     //     at an offset relative to SP
6230     //  2) We adjust frame size so that SP is aligned at 16-bytes after pushing integer registers.
6231     //     This means while saving the first xmm register to its allocated stack location we might
6232     //     have to skip 8-bytes.  The reason for padding is to use efficient "movaps" to save/restore
6233     //     xmm registers to/from stack to match Jit64 codegen.  Without the aligning on 16-byte
6234     //     boundary we would have to use movups when offset turns out unaligned.  Movaps is more
6235     //     performant than movups.
6236     const unsigned calleeFPRegsSavedSize = genCountBits(compCalleeFPRegsSavedMask) * XMM_REGSIZE_BYTES;
6237
6238     // For OSR the alignment pad computation should not take the original frame into account.
6239     // Original frame size includes the pseudo-saved RA and so is always = 8 mod 16.
6240     const int offsetForAlign = -(stkOffs + originalFrameSize);
6241
6242     if ((calleeFPRegsSavedSize > 0) && ((offsetForAlign % XMM_REGSIZE_BYTES) != 0))
6243     {
6244         // Take care of alignment
6245         int alignPad = (int)AlignmentPad((unsigned)offsetForAlign, XMM_REGSIZE_BYTES);
6246         assert(alignPad != 0);
6247         stkOffs -= alignPad;
6248         lvaIncrementFrameSize(alignPad);
6249     }
6250
6251     stkOffs -= calleeFPRegsSavedSize;
6252     lvaIncrementFrameSize(calleeFPRegsSavedSize);
6253
6254     // Quirk for VS debug-launch scenario to work
6255     if (compVSQuirkStackPaddingNeeded > 0)
6256     {
6257 #ifdef DEBUG
6258         if (verbose)
6259         {
6260             printf("\nAdding VS quirk stack padding of %d bytes between save-reg area and locals\n",
6261                    compVSQuirkStackPaddingNeeded);
6262         }
6263 #endif // DEBUG
6264
6265         stkOffs -= compVSQuirkStackPaddingNeeded;
6266         lvaIncrementFrameSize(compVSQuirkStackPaddingNeeded);
6267     }
6268 #endif // TARGET_AMD64
6269
6270     if (lvaMonAcquired != BAD_VAR_NUM)
6271     {
6272         // For OSR we use the flag set up by the original method.
6273         //
6274         if (opts.IsOSR())
6275         {
6276             assert(info.compPatchpointInfo->HasMonitorAcquired());
6277             int originalOffset = info.compPatchpointInfo->MonitorAcquiredOffset();
6278             int offset         = originalFrameStkOffs + originalOffset;
6279
6280             JITDUMP(
6281                 "---OSR--- V%02u (on tier0 frame, monitor acquired) tier0 FP-rel offset %d tier0 frame offset %d new "
6282                 "virt offset %d\n",
6283                 lvaMonAcquired, originalOffset, originalFrameStkOffs, offset);
6284
6285             lvaTable[lvaMonAcquired].SetStackOffset(offset);
6286         }
6287         else
6288         {
6289             // This var must go first, in what is called the 'frame header' for EnC so that it is
6290             // preserved when remapping occurs.  See vm\eetwain.cpp for detailed comment specifying frame
6291             // layout requirements for EnC to work.
6292             stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaMonAcquired, lvaLclSize(lvaMonAcquired), stkOffs);
6293         }
6294     }
6295
6296 #if defined(FEATURE_EH_FUNCLETS) && (defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64))
6297     if (lvaPSPSym != BAD_VAR_NUM)
6298     {
6299         // On ARM/ARM64, if we need a PSPSym we allocate it early since funclets
6300         // will need to have it at the same caller-SP relative offset so anything
6301         // allocated before this will also leak into the funclet's frame.
6302         noway_assert(codeGen->isFramePointerUsed()); // We need an explicit frame pointer
6303         stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaPSPSym, TARGET_POINTER_SIZE, stkOffs);
6304     }
6305 #endif // FEATURE_EH_FUNCLETS && (TARGET_ARMARCH || TARGET_LOONGARCH64 || TARGET_RISCV64)
6306
6307     if (mustDoubleAlign)
6308     {
6309         if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6310         {
6311             // Allocate a pointer sized stack slot, since we may need to double align here
6312             // when lvaDoneFrameLayout == FINAL_FRAME_LAYOUT
6313             //
6314             lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6315             stkOffs -= TARGET_POINTER_SIZE;
6316
6317             // If we have any TYP_LONG, TYP_DOUBLE or double aligned structs
6318             // then we need to allocate a second pointer sized stack slot,
6319             // since we may need to double align that LclVar when we see it
6320             // in the loop below.  We will just always do this so that the
6321             // offsets that we calculate for the stack frame will always
6322             // be greater (or equal) to what they can be in the final layout.
6323             //
6324             lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6325             stkOffs -= TARGET_POINTER_SIZE;
6326         }
6327         else // FINAL_FRAME_LAYOUT
6328         {
6329             if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
6330             {
6331                 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6332                 stkOffs -= TARGET_POINTER_SIZE;
6333             }
6334             // We should now have a double-aligned (stkOffs+preSpillSize)
6335             noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
6336         }
6337     }
6338
6339 #ifdef JIT32_GCENCODER
6340     if (lvaLocAllocSPvar != BAD_VAR_NUM)
6341     {
6342         noway_assert(codeGen->isFramePointerUsed()); // else offsets of locals of frameless methods will be incorrect
6343         stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaLocAllocSPvar, TARGET_POINTER_SIZE, stkOffs);
6344     }
6345 #endif // JIT32_GCENCODER
6346
6347     // For OSR methods, param type args are always reportable via the root method frame slot.
6348     // (see gcInfoBlockHdrSave) and so do not need a new slot on the frame.
6349     //
6350     // OSR methods may also be able to use the root frame kept alive this, if the root
6351     // method needed to report this.
6352     //
6353     // Inlining done under OSR may introduce new reporting, in which case the OSR frame
6354     // must allocate a slot.
6355     if (lvaReportParamTypeArg())
6356     {
6357 #ifdef JIT32_GCENCODER
6358         noway_assert(codeGen->isFramePointerUsed());
6359 #endif
6360         if (opts.IsOSR())
6361         {
6362             PatchpointInfo* ppInfo = info.compPatchpointInfo;
6363             assert(ppInfo->HasGenericContextArgOffset());
6364             const int originalOffset       = ppInfo->GenericContextArgOffset();
6365             lvaCachedGenericContextArgOffs = originalFrameStkOffs + originalOffset;
6366         }
6367         else
6368         {
6369             // For CORINFO_CALLCONV_PARAMTYPE (if needed)
6370             lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6371             stkOffs -= TARGET_POINTER_SIZE;
6372             lvaCachedGenericContextArgOffs = stkOffs;
6373         }
6374     }
6375 #ifndef JIT32_GCENCODER
6376     else if (lvaKeepAliveAndReportThis())
6377     {
6378         bool canUseExistingSlot = false;
6379         if (opts.IsOSR())
6380         {
6381             PatchpointInfo* ppInfo = info.compPatchpointInfo;
6382             if (ppInfo->HasKeptAliveThis())
6383             {
6384                 const int originalOffset       = ppInfo->KeptAliveThisOffset();
6385                 lvaCachedGenericContextArgOffs = originalFrameStkOffs + originalOffset;
6386                 canUseExistingSlot             = true;
6387             }
6388         }
6389
6390         if (!canUseExistingSlot)
6391         {
6392             // When "this" is also used as generic context arg.
6393             lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6394             stkOffs -= TARGET_POINTER_SIZE;
6395             lvaCachedGenericContextArgOffs = stkOffs;
6396         }
6397     }
6398 #endif
6399
6400 #if !defined(FEATURE_EH_FUNCLETS)
6401     /* If we need space for slots for shadow SP, reserve it now */
6402     if (ehNeedsShadowSPslots())
6403     {
6404         noway_assert(codeGen->isFramePointerUsed()); // else offsets of locals of frameless methods will be incorrect
6405         if (!lvaReportParamTypeArg())
6406         {
6407 #ifndef JIT32_GCENCODER
6408             if (!lvaKeepAliveAndReportThis())
6409 #endif
6410             {
6411                 // In order to keep the gc info encoding smaller, the VM assumes that all methods with EH
6412                 // have also saved space for a ParamTypeArg, so we need to do that here
6413                 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6414                 stkOffs -= TARGET_POINTER_SIZE;
6415             }
6416         }
6417         stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaShadowSPslotsVar, lvaLclSize(lvaShadowSPslotsVar), stkOffs);
6418     }
6419 #endif // !FEATURE_EH_FUNCLETS
6420
6421     if (compGSReorderStackLayout)
6422     {
6423         assert(getNeedsGSSecurityCookie());
6424
6425         if (!opts.IsOSR() || !info.compPatchpointInfo->HasSecurityCookie())
6426         {
6427             stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaGSSecurityCookie, lvaLclSize(lvaGSSecurityCookie), stkOffs);
6428         }
6429     }
6430
6431     /*
6432         If we're supposed to track lifetimes of pointer temps, we'll
6433         assign frame offsets in the following order:
6434
6435             non-pointer local variables (also untracked pointer variables)
6436                 pointer local variables
6437                 pointer temps
6438             non-pointer temps
6439      */
6440
6441     enum Allocation
6442     {
6443         ALLOC_NON_PTRS                 = 0x1, // assign offsets to non-ptr
6444         ALLOC_PTRS                     = 0x2, // Second pass, assign offsets to tracked ptrs
6445         ALLOC_UNSAFE_BUFFERS           = 0x4,
6446         ALLOC_UNSAFE_BUFFERS_WITH_PTRS = 0x8
6447     };
6448     UINT alloc_order[5];
6449
6450     unsigned int cur = 0;
6451
6452     if (compGSReorderStackLayout)
6453     {
6454         noway_assert(getNeedsGSSecurityCookie());
6455
6456         if (codeGen->isFramePointerUsed())
6457         {
6458             alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS;
6459             alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS_WITH_PTRS;
6460         }
6461     }
6462
6463     bool tempsAllocated = false;
6464
6465     if (lvaTempsHaveLargerOffsetThanVars() && !codeGen->isFramePointerUsed())
6466     {
6467         // Because we want the temps to have a larger offset than locals
6468         // and we're not using a frame pointer, we have to place the temps
6469         // above the vars.  Otherwise we place them after the vars (at the
6470         // bottom of the frame).
6471         noway_assert(!tempsAllocated);
6472         stkOffs        = lvaAllocateTemps(stkOffs, mustDoubleAlign);
6473         tempsAllocated = true;
6474     }
6475
6476     alloc_order[cur++] = ALLOC_NON_PTRS;
6477
6478     if (opts.compDbgEnC)
6479     {
6480         /* We will use just one pass, and assign offsets to all variables */
6481         alloc_order[cur - 1] |= ALLOC_PTRS;
6482         noway_assert(compGSReorderStackLayout == false);
6483     }
6484     else
6485     {
6486         alloc_order[cur++] = ALLOC_PTRS;
6487     }
6488
6489     if (!codeGen->isFramePointerUsed() && compGSReorderStackLayout)
6490     {
6491         alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS_WITH_PTRS;
6492         alloc_order[cur++] = ALLOC_UNSAFE_BUFFERS;
6493     }
6494
6495     alloc_order[cur] = 0;
6496
6497     noway_assert(cur < ArrLen(alloc_order));
6498
6499     // Force first pass to happen
6500     UINT assignMore             = 0xFFFFFFFF;
6501     bool have_LclVarDoubleAlign = false;
6502
6503     for (cur = 0; alloc_order[cur]; cur++)
6504     {
6505         if ((assignMore & alloc_order[cur]) == 0)
6506         {
6507             continue;
6508         }
6509
6510         assignMore = 0;
6511
6512         unsigned   lclNum;
6513         LclVarDsc* varDsc;
6514
6515         for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
6516         {
6517             /* Ignore field locals of the promotion type PROMOTION_TYPE_FIELD_DEPENDENT.
6518                In other words, we will not calculate the "base" address of the struct local if
6519                the promotion type is PROMOTION_TYPE_FIELD_DEPENDENT.
6520             */
6521             if (lvaIsFieldOfDependentlyPromotedStruct(varDsc))
6522             {
6523                 continue;
6524             }
6525
6526 #if FEATURE_FIXED_OUT_ARGS
6527             // The scratch mem is used for the outgoing arguments, and it must be absolutely last
6528             if (lclNum == lvaOutgoingArgSpaceVar)
6529             {
6530                 continue;
6531             }
6532 #endif
6533
6534             bool allocateOnFrame = varDsc->lvOnFrame;
6535
6536             if (varDsc->lvRegister && (lvaDoneFrameLayout == REGALLOC_FRAME_LAYOUT) &&
6537                 ((varDsc->TypeGet() != TYP_LONG) || (varDsc->GetOtherReg() != REG_STK)))
6538             {
6539                 allocateOnFrame = false;
6540             }
6541
6542             // For OSR args and locals, we use the slots on the original frame.
6543             //
6544             // Note we must do this even for "non frame" locals, as we sometimes
6545             // will refer to their memory homes.
6546             if (lvaIsOSRLocal(lclNum))
6547             {
6548                 if (varDsc->lvIsStructField)
6549                 {
6550                     const unsigned parentLclNum         = varDsc->lvParentLcl;
6551                     const int      parentOriginalOffset = info.compPatchpointInfo->Offset(parentLclNum);
6552                     const int      offset = originalFrameStkOffs + parentOriginalOffset + varDsc->lvFldOffset;
6553
6554                     JITDUMP("---OSR--- V%02u (promoted field of V%02u; on tier0 frame) tier0 FP-rel offset %d tier0 "
6555                             "frame offset %d field offset %d new virt offset "
6556                             "%d\n",
6557                             lclNum, parentLclNum, parentOriginalOffset, originalFrameStkOffs, varDsc->lvFldOffset,
6558                             offset);
6559
6560                     lvaTable[lclNum].SetStackOffset(offset);
6561                 }
6562                 else
6563                 {
6564                     // Add frampointer-relative offset of this OSR live local in the original frame
6565                     // to the offset of original frame in our new frame.
6566                     const int originalOffset = info.compPatchpointInfo->Offset(lclNum);
6567                     const int offset         = originalFrameStkOffs + originalOffset;
6568
6569                     JITDUMP(
6570                         "---OSR--- V%02u (on tier0 frame) tier0 FP-rel offset %d tier0 frame offset %d new virt offset "
6571                         "%d\n",
6572                         lclNum, originalOffset, originalFrameStkOffs, offset);
6573
6574                     lvaTable[lclNum].SetStackOffset(offset);
6575                 }
6576                 continue;
6577             }
6578
6579             /* Ignore variables that are not on the stack frame */
6580
6581             if (!allocateOnFrame)
6582             {
6583                 /* For EnC, all variables have to be allocated space on the
6584                    stack, even though they may actually be enregistered. This
6585                    way, the frame layout can be directly inferred from the
6586                    locals-sig.
6587                  */
6588
6589                 if (!opts.compDbgEnC)
6590                 {
6591                     continue;
6592                 }
6593                 else if (lclNum >= info.compLocalsCount)
6594                 { // ignore temps for EnC
6595                     continue;
6596                 }
6597             }
6598             else if (lvaGSSecurityCookie == lclNum && getNeedsGSSecurityCookie())
6599             {
6600                 // Special case for OSR. If the original method had a cookie,
6601                 // we use its slot on the original frame.
6602                 if (opts.IsOSR() && info.compPatchpointInfo->HasSecurityCookie())
6603                 {
6604                     int originalOffset = info.compPatchpointInfo->SecurityCookieOffset();
6605                     int offset         = originalFrameStkOffs + originalOffset;
6606
6607                     JITDUMP("---OSR--- V%02u (on tier0 frame, security cookie) tier0 FP-rel offset %d tier0 frame "
6608                             "offset %d new "
6609                             "virt offset %d\n",
6610                             lclNum, originalOffset, originalFrameStkOffs, offset);
6611
6612                     lvaTable[lclNum].SetStackOffset(offset);
6613                 }
6614
6615                 continue;
6616             }
6617
6618             // These need to be located as the very first variables (highest memory address)
6619             // and so they have already been assigned an offset
6620             if (
6621 #if defined(FEATURE_EH_FUNCLETS)
6622                 lclNum == lvaPSPSym ||
6623 #else
6624                 lclNum == lvaShadowSPslotsVar ||
6625 #endif // FEATURE_EH_FUNCLETS
6626 #ifdef JIT32_GCENCODER
6627                 lclNum == lvaLocAllocSPvar ||
6628 #endif // JIT32_GCENCODER
6629                 lclNum == lvaRetAddrVar)
6630             {
6631                 assert(varDsc->GetStackOffset() != BAD_STK_OFFS);
6632                 continue;
6633             }
6634
6635             if (lclNum == lvaMonAcquired)
6636             {
6637                 continue;
6638             }
6639
6640             // This should be low on the stack. Hence, it will be assigned later.
6641             if (lclNum == lvaStubArgumentVar)
6642             {
6643 #ifdef JIT32_GCENCODER
6644                 noway_assert(codeGen->isFramePointerUsed());
6645 #endif
6646                 continue;
6647             }
6648
6649             // This should be low on the stack. Hence, it will be assigned later.
6650             if (lclNum == lvaInlinedPInvokeFrameVar)
6651             {
6652                 noway_assert(codeGen->isFramePointerUsed());
6653                 continue;
6654             }
6655
6656             if (varDsc->lvIsParam)
6657             {
6658 #if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI)
6659
6660                 // On Windows AMD64 we can use the caller-reserved stack area that is already setup
6661                 assert(varDsc->GetStackOffset() != BAD_STK_OFFS);
6662                 continue;
6663
6664 #else // !TARGET_AMD64
6665
6666                 //  A register argument that is not enregistered ends up as
6667                 //  a local variable which will need stack frame space.
6668                 //
6669                 if (!varDsc->lvIsRegArg)
6670                 {
6671                     continue;
6672                 }
6673
6674 #ifdef TARGET_ARM64
6675                 if (info.compIsVarArgs && varDsc->GetArgReg() != theFixedRetBuffArgNum())
6676                 {
6677                     // Stack offset to varargs (parameters) should point to home area which will be preallocated.
6678                     const unsigned regArgNum = genMapIntRegNumToRegArgNum(varDsc->GetArgReg());
6679                     varDsc->SetStackOffset(-initialStkOffs + regArgNum * REGSIZE_BYTES);
6680                     continue;
6681                 }
6682
6683 #endif
6684
6685 #ifdef TARGET_ARM
6686                 // On ARM we spill the registers in codeGen->regSet.rsMaskPreSpillRegArg
6687                 // in the prolog, thus they don't need stack frame space.
6688                 //
6689                 if ((codeGen->regSet.rsMaskPreSpillRegs(false) & genRegMask(varDsc->GetArgReg())) != 0)
6690                 {
6691                     assert(varDsc->GetStackOffset() != BAD_STK_OFFS);
6692                     continue;
6693                 }
6694 #endif
6695
6696 #endif // !TARGET_AMD64
6697             }
6698
6699             /* Make sure the type is appropriate */
6700
6701             if (varDsc->lvIsUnsafeBuffer && compGSReorderStackLayout)
6702             {
6703                 if (varDsc->lvIsPtr)
6704                 {
6705                     if ((alloc_order[cur] & ALLOC_UNSAFE_BUFFERS_WITH_PTRS) == 0)
6706                     {
6707                         assignMore |= ALLOC_UNSAFE_BUFFERS_WITH_PTRS;
6708                         continue;
6709                     }
6710                 }
6711                 else
6712                 {
6713                     if ((alloc_order[cur] & ALLOC_UNSAFE_BUFFERS) == 0)
6714                     {
6715                         assignMore |= ALLOC_UNSAFE_BUFFERS;
6716                         continue;
6717                     }
6718                 }
6719             }
6720             else if (varTypeIsGC(varDsc->TypeGet()) && varDsc->lvTracked)
6721             {
6722                 if ((alloc_order[cur] & ALLOC_PTRS) == 0)
6723                 {
6724                     assignMore |= ALLOC_PTRS;
6725                     continue;
6726                 }
6727             }
6728             else
6729             {
6730                 if ((alloc_order[cur] & ALLOC_NON_PTRS) == 0)
6731                 {
6732                     assignMore |= ALLOC_NON_PTRS;
6733                     continue;
6734                 }
6735             }
6736
6737             /* Need to align the offset? */
6738
6739             if (mustDoubleAlign && (varDsc->lvType == TYP_DOUBLE // Align doubles for ARM and x86
6740 #ifdef TARGET_ARM
6741                                     || varDsc->lvType == TYP_LONG // Align longs for ARM
6742 #endif
6743 #ifndef TARGET_64BIT
6744                                     || varDsc->lvStructDoubleAlign // Align when lvStructDoubleAlign is true
6745 #endif                                                             // !TARGET_64BIT
6746                                     ))
6747             {
6748                 noway_assert((compLclFrameSize % TARGET_POINTER_SIZE) == 0);
6749
6750                 if ((lvaDoneFrameLayout != FINAL_FRAME_LAYOUT) && !have_LclVarDoubleAlign)
6751                 {
6752                     // If this is the first TYP_LONG, TYP_DOUBLE or double aligned struct
6753                     // then we have seen in this loop then we allocate a pointer sized
6754                     // stack slot since we may need to double align this LclVar
6755                     // when lvaDoneFrameLayout == FINAL_FRAME_LAYOUT
6756                     //
6757                     lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6758                     stkOffs -= TARGET_POINTER_SIZE;
6759                 }
6760                 else
6761                 {
6762                     if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
6763                     {
6764                         lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6765                         stkOffs -= TARGET_POINTER_SIZE;
6766                     }
6767
6768                     // We should now have a double-aligned (stkOffs+preSpillSize)
6769                     noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
6770                 }
6771
6772                 // Remember that we had to double align a LclVar
6773                 have_LclVarDoubleAlign = true;
6774             }
6775
6776             // Reserve the stack space for this variable
6777             stkOffs = lvaAllocLocalAndSetVirtualOffset(lclNum, lvaLclSize(lclNum), stkOffs);
6778 #if defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
6779             // If we have an incoming register argument that has a promoted field then we
6780             // need to copy the lvStkOff (the stack home) from the reg arg to the field lclvar
6781             //
6782             if (varDsc->lvIsRegArg && varDsc->lvPromoted)
6783             {
6784                 unsigned firstFieldNum = varDsc->lvFieldLclStart;
6785                 for (unsigned i = 0; i < varDsc->lvFieldCnt; i++)
6786                 {
6787                     LclVarDsc* fieldVarDsc = lvaGetDesc(firstFieldNum + i);
6788                     fieldVarDsc->SetStackOffset(varDsc->GetStackOffset() + fieldVarDsc->lvFldOffset);
6789                 }
6790             }
6791 #endif // defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
6792         }
6793     }
6794
6795     if (getNeedsGSSecurityCookie() && !compGSReorderStackLayout)
6796     {
6797         if (!opts.IsOSR() || !info.compPatchpointInfo->HasSecurityCookie())
6798         {
6799             // LOCALLOC used, but we have no unsafe buffer.  Allocated cookie last, close to localloc buffer.
6800             stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaGSSecurityCookie, lvaLclSize(lvaGSSecurityCookie), stkOffs);
6801         }
6802     }
6803
6804     if (tempsAllocated == false)
6805     {
6806         /*-------------------------------------------------------------------------
6807          *
6808          * Now the temps
6809          *
6810          *-------------------------------------------------------------------------
6811          */
6812         stkOffs = lvaAllocateTemps(stkOffs, mustDoubleAlign);
6813     }
6814
6815     /*-------------------------------------------------------------------------
6816      *
6817      * Now do some final stuff
6818      *
6819      *-------------------------------------------------------------------------
6820      */
6821
6822     // lvaInlinedPInvokeFrameVar and lvaStubArgumentVar need to be assigned last
6823     // Important: The stack walker depends on lvaStubArgumentVar immediately
6824     // following lvaInlinedPInvokeFrameVar in the frame.
6825
6826     if (lvaStubArgumentVar != BAD_VAR_NUM)
6827     {
6828 #ifdef JIT32_GCENCODER
6829         noway_assert(codeGen->isFramePointerUsed());
6830 #endif
6831         stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaStubArgumentVar, lvaLclSize(lvaStubArgumentVar), stkOffs);
6832     }
6833
6834     if (lvaInlinedPInvokeFrameVar != BAD_VAR_NUM)
6835     {
6836         noway_assert(codeGen->isFramePointerUsed());
6837         stkOffs =
6838             lvaAllocLocalAndSetVirtualOffset(lvaInlinedPInvokeFrameVar, lvaLclSize(lvaInlinedPInvokeFrameVar), stkOffs);
6839     }
6840
6841 #ifdef JIT32_GCENCODER
6842     // JIT32 encoder cannot handle GS cookie at fp+0 since NO_GS_COOKIE == 0.
6843     // Add some padding if it is the last allocated local.
6844     if ((lvaGSSecurityCookie != BAD_VAR_NUM) && (lvaGetDesc(lvaGSSecurityCookie)->GetStackOffset() == stkOffs))
6845     {
6846         lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6847         stkOffs -= TARGET_POINTER_SIZE;
6848     }
6849 #endif
6850
6851     if (mustDoubleAlign)
6852     {
6853         if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6854         {
6855             // Allocate a pointer sized stack slot, since we may need to double align here
6856             // when lvaDoneFrameLayout == FINAL_FRAME_LAYOUT
6857             //
6858             lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6859             stkOffs -= TARGET_POINTER_SIZE;
6860
6861             if (have_LclVarDoubleAlign)
6862             {
6863                 // If we have any TYP_LONG, TYP_DOUBLE or double aligned structs
6864                 // the we need to allocate a second pointer sized stack slot,
6865                 // since we may need to double align the last LclVar that we saw
6866                 // in the loop above. We do this so that the offsets that we
6867                 // calculate for the stack frame are always greater than they will
6868                 // be in the final layout.
6869                 //
6870                 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6871                 stkOffs -= TARGET_POINTER_SIZE;
6872             }
6873         }
6874         else // FINAL_FRAME_LAYOUT
6875         {
6876             if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
6877             {
6878                 lvaIncrementFrameSize(TARGET_POINTER_SIZE);
6879                 stkOffs -= TARGET_POINTER_SIZE;
6880             }
6881             // We should now have a double-aligned (stkOffs+preSpillSize)
6882             noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
6883         }
6884     }
6885
6886 #if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_AMD64)
6887     if (lvaPSPSym != BAD_VAR_NUM)
6888     {
6889         // On AMD64, if we need a PSPSym, allocate it last, immediately above the outgoing argument
6890         // space. Any padding will be higher on the stack than this
6891         // (including the padding added by lvaAlignFrame()).
6892         noway_assert(codeGen->isFramePointerUsed()); // We need an explicit frame pointer
6893         stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaPSPSym, TARGET_POINTER_SIZE, stkOffs);
6894     }
6895 #endif // FEATURE_EH_FUNCLETS && defined(TARGET_AMD64)
6896
6897 #ifdef TARGET_ARM64
6898     if (!codeGen->IsSaveFpLrWithAllCalleeSavedRegisters() &&
6899         isFramePointerUsed()) // Note that currently we always have a frame pointer
6900     {
6901         // Create space for saving FP and LR.
6902         stkOffs -= 2 * REGSIZE_BYTES;
6903     }
6904 #endif // TARGET_ARM64
6905
6906 #if FEATURE_FIXED_OUT_ARGS
6907     if (lvaOutgoingArgSpaceSize > 0)
6908     {
6909 #if defined(TARGET_AMD64) && !defined(UNIX_AMD64_ABI) // No 4 slots for outgoing params on System V.
6910         noway_assert(lvaOutgoingArgSpaceSize >= (4 * TARGET_POINTER_SIZE));
6911 #endif
6912         noway_assert((lvaOutgoingArgSpaceSize % TARGET_POINTER_SIZE) == 0);
6913
6914         // Give it a value so we can avoid asserts in CHK builds.
6915         // Since this will always use an SP relative offset of zero
6916         // at the end of lvaFixVirtualFrameOffsets, it will be set to absolute '0'
6917
6918         stkOffs = lvaAllocLocalAndSetVirtualOffset(lvaOutgoingArgSpaceVar, lvaLclSize(lvaOutgoingArgSpaceVar), stkOffs);
6919     }
6920 #endif // FEATURE_FIXED_OUT_ARGS
6921
6922 #if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
6923     // For LoongArch64 and RISCV64, CalleeSavedRegs are at bottom.
6924     int pushedCount = 0;
6925 #else
6926     // compLclFrameSize equals our negated virtual stack offset minus the pushed registers and return address
6927     // and the pushed frame pointer register which for some strange reason isn't part of 'compCalleeRegsPushed'.
6928     int pushedCount = compCalleeRegsPushed;
6929 #endif
6930
6931 #ifdef TARGET_ARM64
6932     if (info.compIsVarArgs)
6933     {
6934         pushedCount += MAX_REG_ARG;
6935     }
6936 #endif
6937
6938 #ifdef TARGET_XARCH
6939     if (codeGen->doubleAlignOrFramePointerUsed())
6940     {
6941         pushedCount += 1; // pushed EBP (frame pointer)
6942     }
6943     pushedCount += 1; // pushed PC (return address)
6944 #endif
6945
6946     noway_assert(compLclFrameSize + originalFrameSize ==
6947                  (unsigned)-(stkOffs + (pushedCount * (int)TARGET_POINTER_SIZE)));
6948 }
6949
6950 int Compiler::lvaAllocLocalAndSetVirtualOffset(unsigned lclNum, unsigned size, int stkOffs)
6951 {
6952     noway_assert(lclNum != BAD_VAR_NUM);
6953
6954     LclVarDsc* lcl = lvaGetDesc(lclNum);
6955 #ifdef TARGET_64BIT
6956     // Before final frame layout, assume the worst case, that every >=8 byte local will need
6957     // maximum padding to be aligned. This is because we generate code based on the stack offset
6958     // computed during tentative frame layout. These offsets cannot get bigger during final
6959     // frame layout, as that would possibly require different code generation (for example,
6960     // using a 4-byte offset instead of a 1-byte offset in an instruction). The offsets can get
6961     // smaller. It is possible there is different alignment at the point locals are allocated
6962     // between tentative and final frame layout which would introduce padding between locals
6963     // and thus increase the offset (from the stack pointer) of one of the locals. Hence the
6964     // need to assume the worst alignment before final frame layout.
6965     // We could probably improve this by sorting all the objects by alignment,
6966     // such that all 8 byte objects are together, 4 byte objects are together, etc., which
6967     // would require at most one alignment padding per group.
6968     //
6969     // TYP_SIMD structs locals have alignment preference given by getSIMDTypeAlignment() for
6970     // better performance.
6971     if ((size >= 8) && ((lvaDoneFrameLayout != FINAL_FRAME_LAYOUT) || ((stkOffs % 8) != 0)
6972 #if defined(FEATURE_SIMD) && ALIGN_SIMD_TYPES
6973                         || varTypeIsSIMD(lcl)
6974 #endif
6975                             ))
6976     {
6977         // Note that stack offsets are negative or equal to zero
6978         assert(stkOffs <= 0);
6979
6980         // alignment padding
6981         unsigned pad = 0;
6982 #if defined(FEATURE_SIMD) && ALIGN_SIMD_TYPES
6983         if (varTypeIsSIMD(lcl))
6984         {
6985             int alignment = getSIMDTypeAlignment(lcl->TypeGet());
6986
6987             if (stkOffs % alignment != 0)
6988             {
6989                 if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
6990                 {
6991                     pad = alignment - 1;
6992                     // Note that all the objects will probably be misaligned, but we'll fix that in final layout.
6993                 }
6994                 else
6995                 {
6996                     pad = alignment + (stkOffs % alignment); // +1 to +(alignment-1) bytes
6997                 }
6998             }
6999         }
7000         else
7001 #endif // FEATURE_SIMD && ALIGN_SIMD_TYPES
7002         {
7003             if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
7004             {
7005                 pad = 7;
7006                 // Note that all the objects will probably be misaligned, but we'll fix that in final layout.
7007             }
7008             else
7009             {
7010                 pad = 8 + (stkOffs % 8); // +1 to +7 bytes
7011             }
7012         }
7013         // Will the pad ever be anything except 4? Do we put smaller-than-4-sized objects on the stack?
7014         lvaIncrementFrameSize(pad);
7015         stkOffs -= pad;
7016
7017 #ifdef DEBUG
7018         if (verbose)
7019         {
7020             printf("Pad ");
7021             gtDispLclVar(lclNum, /*pad*/ false);
7022             printf(", size=%d, stkOffs=%c0x%x, pad=%d\n", size, stkOffs < 0 ? '-' : '+',
7023                    stkOffs < 0 ? -stkOffs : stkOffs, pad);
7024         }
7025 #endif
7026     }
7027 #endif // TARGET_64BIT
7028
7029     /* Reserve space on the stack by bumping the frame size */
7030
7031     lvaIncrementFrameSize(size);
7032     stkOffs -= size;
7033     lcl->SetStackOffset(stkOffs);
7034
7035 #ifdef DEBUG
7036     if (verbose)
7037     {
7038         printf("Assign ");
7039         gtDispLclVar(lclNum, /*pad*/ false);
7040         printf(", size=%d, stkOffs=%c0x%x\n", size, stkOffs < 0 ? '-' : '+', stkOffs < 0 ? -stkOffs : stkOffs);
7041     }
7042 #endif
7043
7044     return stkOffs;
7045 }
7046
7047 #ifdef TARGET_AMD64
7048 /*****************************************************************************
7049  *  lvaIsCalleeSavedIntRegCountEven() :  returns true if the number of integer registers
7050  *  pushed onto stack is even including RBP if used as frame pointer
7051  *
7052  *  Note that this excludes return address (PC) pushed by caller.  To know whether
7053  *  the SP offset after pushing integer registers is aligned, we need to take
7054  *  negation of this routine.
7055  */
7056 bool Compiler::lvaIsCalleeSavedIntRegCountEven()
7057 {
7058     unsigned regsPushed = compCalleeRegsPushed + (codeGen->isFramePointerUsed() ? 1 : 0);
7059     return (regsPushed % (16 / REGSIZE_BYTES)) == 0;
7060 }
7061 #endif // TARGET_AMD64
7062
7063 /*****************************************************************************
7064  *  lvaAlignFrame() :  After allocating everything on the frame, reserve any
7065  *  extra space needed to keep the frame aligned
7066  */
7067 void Compiler::lvaAlignFrame()
7068 {
7069 #if defined(TARGET_AMD64)
7070
7071     // Leaf frames do not need full alignment, but the unwind info is smaller if we
7072     // are at least 8 byte aligned (and we assert as much)
7073     if ((compLclFrameSize % 8) != 0)
7074     {
7075         lvaIncrementFrameSize(8 - (compLclFrameSize % 8));
7076     }
7077     else if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
7078     {
7079         // If we are not doing final layout, we don't know the exact value of compLclFrameSize
7080         // and thus do not know how much we will need to add in order to be aligned.
7081         // We add 8 so compLclFrameSize is still a multiple of 8.
7082         lvaIncrementFrameSize(8);
7083     }
7084     assert((compLclFrameSize % 8) == 0);
7085
7086     // Ensure that the stack is always 16-byte aligned by grabbing an unused QWORD
7087     // if needed, but off by 8 because of the return value.
7088     // And don't forget that compCalleeRegsPused does *not* include RBP if we are
7089     // using it as the frame pointer.
7090     //
7091     bool regPushedCountAligned = lvaIsCalleeSavedIntRegCountEven();
7092     bool lclFrameSizeAligned   = (compLclFrameSize % 16) == 0;
7093
7094     // If this isn't the final frame layout, assume we have to push an extra QWORD
7095     // Just so the offsets are true upper limits.
7096     CLANG_FORMAT_COMMENT_ANCHOR;
7097
7098 #ifdef UNIX_AMD64_ABI
7099     // The compNeedToAlignFrame flag  is indicating if there is a need to align the frame.
7100     // On AMD64-Windows, if there are calls, 4 slots for the outgoing ars are allocated, except for
7101     // FastTailCall. This slots makes the frame size non-zero, so alignment logic will be called.
7102     // On AMD64-Unix, there are no such slots. There is a possibility to have calls in the method with frame size of 0.
7103     // The frame alignment logic won't kick in. This flags takes care of the AMD64-Unix case by remembering that there
7104     // are calls and making sure the frame alignment logic is executed.
7105     bool stackNeedsAlignment = (compLclFrameSize != 0 || opts.compNeedToAlignFrame);
7106 #else  // !UNIX_AMD64_ABI
7107     bool stackNeedsAlignment = compLclFrameSize != 0;
7108 #endif // !UNIX_AMD64_ABI
7109     if ((!codeGen->isFramePointerUsed() && (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)) ||
7110         (stackNeedsAlignment && (regPushedCountAligned == lclFrameSizeAligned)))
7111     {
7112         lvaIncrementFrameSize(REGSIZE_BYTES);
7113     }
7114
7115 #elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
7116
7117     // The stack on ARM64/LoongArch64 must be 16 byte aligned.
7118
7119     // First, align up to 8.
7120     if ((compLclFrameSize % 8) != 0)
7121     {
7122         lvaIncrementFrameSize(8 - (compLclFrameSize % 8));
7123     }
7124     else if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
7125     {
7126         // If we are not doing final layout, we don't know the exact value of compLclFrameSize
7127         // and thus do not know how much we will need to add in order to be aligned.
7128         // We add 8 so compLclFrameSize is still a multiple of 8.
7129         lvaIncrementFrameSize(8);
7130     }
7131     assert((compLclFrameSize % 8) == 0);
7132
7133     // Ensure that the stack is always 16-byte aligned by grabbing an unused QWORD
7134     // if needed.
7135     bool regPushedCountAligned = (compCalleeRegsPushed % (16 / REGSIZE_BYTES)) == 0;
7136     bool lclFrameSizeAligned   = (compLclFrameSize % 16) == 0;
7137
7138     // If this isn't the final frame layout, assume we have to push an extra QWORD
7139     // Just so the offsets are true upper limits.
7140     if ((lvaDoneFrameLayout != FINAL_FRAME_LAYOUT) || (regPushedCountAligned != lclFrameSizeAligned))
7141     {
7142         lvaIncrementFrameSize(REGSIZE_BYTES);
7143     }
7144
7145 #elif defined(TARGET_ARM)
7146
7147     // Ensure that stack offsets will be double-aligned by grabbing an unused DWORD if needed.
7148     //
7149     bool lclFrameSizeAligned   = (compLclFrameSize % sizeof(double)) == 0;
7150     bool regPushedCountAligned = ((compCalleeRegsPushed + genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true))) %
7151                                   (sizeof(double) / TARGET_POINTER_SIZE)) == 0;
7152
7153     if (regPushedCountAligned != lclFrameSizeAligned)
7154     {
7155         lvaIncrementFrameSize(TARGET_POINTER_SIZE);
7156     }
7157
7158 #elif defined(TARGET_X86)
7159
7160 #if DOUBLE_ALIGN
7161     if (genDoubleAlign())
7162     {
7163         // Double Frame Alignment for x86 is handled in Compiler::lvaAssignVirtualFrameOffsetsToLocals()
7164
7165         if (compLclFrameSize == 0)
7166         {
7167             // This can only happen with JitStress=1 or JitDoubleAlign=2
7168             lvaIncrementFrameSize(TARGET_POINTER_SIZE);
7169         }
7170     }
7171 #endif
7172
7173     if (STACK_ALIGN > REGSIZE_BYTES)
7174     {
7175         if (lvaDoneFrameLayout != FINAL_FRAME_LAYOUT)
7176         {
7177             // If we are not doing final layout, we don't know the exact value of compLclFrameSize
7178             // and thus do not know how much we will need to add in order to be aligned.
7179             // We add the maximum pad that we could ever have (which is 12)
7180             lvaIncrementFrameSize(STACK_ALIGN - REGSIZE_BYTES);
7181         }
7182
7183         // Align the stack with STACK_ALIGN value.
7184         int  adjustFrameSize = compLclFrameSize;
7185 #if defined(UNIX_X86_ABI)
7186         bool isEbpPushed     = codeGen->isFramePointerUsed();
7187 #if DOUBLE_ALIGN
7188         isEbpPushed |= genDoubleAlign();
7189 #endif
7190         // we need to consider spilled register(s) plus return address and/or EBP
7191         int adjustCount = compCalleeRegsPushed + 1 + (isEbpPushed ? 1 : 0);
7192         adjustFrameSize += (adjustCount * REGSIZE_BYTES) % STACK_ALIGN;
7193 #endif
7194         if ((adjustFrameSize % STACK_ALIGN) != 0)
7195         {
7196             lvaIncrementFrameSize(STACK_ALIGN - (adjustFrameSize % STACK_ALIGN));
7197         }
7198     }
7199
7200 #else
7201     NYI("TARGET specific lvaAlignFrame");
7202 #endif // !TARGET_AMD64
7203 }
7204
7205 /*****************************************************************************
7206  *  lvaAssignFrameOffsetsToPromotedStructs() :  Assign offsets to fields
7207  *  within a promoted struct (worker for lvaAssignFrameOffsets).
7208  */
7209 void Compiler::lvaAssignFrameOffsetsToPromotedStructs()
7210 {
7211     LclVarDsc* varDsc = lvaTable;
7212     for (unsigned lclNum = 0; lclNum < lvaCount; lclNum++, varDsc++)
7213     {
7214         // For promoted struct fields that are params, we will
7215         // assign their offsets in lvaAssignVirtualFrameOffsetToArg().
7216         // This is not true for the System V systems since there is no
7217         // outgoing args space. Assign the dependently promoted fields properly.
7218         //
7219         CLANG_FORMAT_COMMENT_ANCHOR;
7220
7221 #if defined(UNIX_AMD64_ABI) || defined(TARGET_ARM) || defined(TARGET_X86)
7222         // ARM: lo/hi parts of a promoted long arg need to be updated.
7223         //
7224         // For System V platforms there is no outgoing args space.
7225         //
7226         // For System V and x86, a register passed struct arg is homed on the stack in a separate local var.
7227         // The offset of these structs is already calculated in lvaAssignVirtualFrameOffsetToArg method.
7228         // Make sure the code below is not executed for these structs and the offset is not changed.
7229         //
7230         const bool mustProcessParams = true;
7231 #else
7232         // OSR must also assign offsets here.
7233         //
7234         const bool mustProcessParams = opts.IsOSR();
7235 #endif // defined(UNIX_AMD64_ABI) || defined(TARGET_ARM) || defined(TARGET_X86)
7236
7237         if (varDsc->lvIsStructField && (!varDsc->lvIsParam || mustProcessParams))
7238         {
7239             LclVarDsc*       parentvarDsc  = lvaGetDesc(varDsc->lvParentLcl);
7240             lvaPromotionType promotionType = lvaGetPromotionType(parentvarDsc);
7241
7242             if (promotionType == PROMOTION_TYPE_INDEPENDENT)
7243             {
7244                 // The stack offset for these field locals must have been calculated
7245                 // by the normal frame offset assignment.
7246                 continue;
7247             }
7248             else
7249             {
7250                 noway_assert(promotionType == PROMOTION_TYPE_DEPENDENT);
7251                 noway_assert(varDsc->lvOnFrame);
7252                 if (parentvarDsc->lvOnFrame)
7253                 {
7254                     JITDUMP("Adjusting offset of dependent V%02u of V%02u: parent %u field %u net %u\n", lclNum,
7255                             varDsc->lvParentLcl, parentvarDsc->GetStackOffset(), varDsc->lvFldOffset,
7256                             parentvarDsc->GetStackOffset() + varDsc->lvFldOffset);
7257                     varDsc->SetStackOffset(parentvarDsc->GetStackOffset() + varDsc->lvFldOffset);
7258                 }
7259                 else
7260                 {
7261                     varDsc->lvOnFrame = false;
7262                     noway_assert(varDsc->lvRefCnt() == 0);
7263                 }
7264             }
7265         }
7266     }
7267 }
7268
7269 /*****************************************************************************
7270  *  lvaAllocateTemps() :  Assign virtual offsets to temps (always negative).
7271  */
7272 int Compiler::lvaAllocateTemps(int stkOffs, bool mustDoubleAlign)
7273 {
7274     unsigned spillTempSize = 0;
7275
7276     if (lvaDoneFrameLayout == FINAL_FRAME_LAYOUT)
7277     {
7278         int preSpillSize = 0;
7279 #ifdef TARGET_ARM
7280         preSpillSize = genCountBits(codeGen->regSet.rsMaskPreSpillRegs(true)) * TARGET_POINTER_SIZE;
7281 #endif
7282
7283         /* Allocate temps */
7284
7285         assert(codeGen->regSet.tmpAllFree());
7286
7287         for (TempDsc* temp = codeGen->regSet.tmpListBeg(); temp != nullptr; temp = codeGen->regSet.tmpListNxt(temp))
7288         {
7289             var_types tempType = temp->tdTempType();
7290             unsigned  size     = temp->tdTempSize();
7291
7292             /* Figure out and record the stack offset of the temp */
7293
7294             /* Need to align the offset? */
7295             CLANG_FORMAT_COMMENT_ANCHOR;
7296
7297 #ifdef TARGET_64BIT
7298             if (varTypeIsGC(tempType) && ((stkOffs % TARGET_POINTER_SIZE) != 0))
7299             {
7300                 // Calculate 'pad' as the number of bytes to align up 'stkOffs' to be a multiple of TARGET_POINTER_SIZE
7301                 // In practice this is really just a fancy way of writing 4. (as all stack locations are at least 4-byte
7302                 // aligned). Note stkOffs is always negative, so (stkOffs % TARGET_POINTER_SIZE) yields a negative
7303                 // value.
7304                 //
7305                 int alignPad = (int)AlignmentPad((unsigned)-stkOffs, TARGET_POINTER_SIZE);
7306
7307                 spillTempSize += alignPad;
7308                 lvaIncrementFrameSize(alignPad);
7309                 stkOffs -= alignPad;
7310
7311                 noway_assert((stkOffs % TARGET_POINTER_SIZE) == 0);
7312             }
7313 #endif
7314
7315             if (mustDoubleAlign && (tempType == TYP_DOUBLE)) // Align doubles for x86 and ARM
7316             {
7317                 noway_assert((compLclFrameSize % TARGET_POINTER_SIZE) == 0);
7318
7319                 if (((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) != 0)
7320                 {
7321                     spillTempSize += TARGET_POINTER_SIZE;
7322                     lvaIncrementFrameSize(TARGET_POINTER_SIZE);
7323                     stkOffs -= TARGET_POINTER_SIZE;
7324                 }
7325                 // We should now have a double-aligned (stkOffs+preSpillSize)
7326                 noway_assert(((stkOffs + preSpillSize) % (2 * TARGET_POINTER_SIZE)) == 0);
7327             }
7328
7329             spillTempSize += size;
7330             lvaIncrementFrameSize(size);
7331             stkOffs -= size;
7332             temp->tdSetTempOffs(stkOffs);
7333         }
7334 #ifdef TARGET_ARM
7335         // Only required for the ARM platform that we have an accurate estimate for the spillTempSize
7336         noway_assert(spillTempSize <= lvaGetMaxSpillTempSize());
7337 #endif
7338     }
7339     else // We haven't run codegen, so there are no Spill temps yet!
7340     {
7341         unsigned size = lvaGetMaxSpillTempSize();
7342
7343         lvaIncrementFrameSize(size);
7344         stkOffs -= size;
7345     }
7346
7347     return stkOffs;
7348 }
7349
7350 #ifdef DEBUG
7351
7352 /*****************************************************************************
7353  *
7354  *  Dump the register a local is in right now. It is only the current location, since the location changes and it
7355  *  is updated throughout code generation based on LSRA register assignments.
7356  */
7357
7358 void Compiler::lvaDumpRegLocation(unsigned lclNum)
7359 {
7360     const LclVarDsc* varDsc = lvaGetDesc(lclNum);
7361
7362 #ifdef TARGET_ARM
7363     if (varDsc->TypeGet() == TYP_DOUBLE)
7364     {
7365         // The assigned registers are `lvRegNum:RegNext(lvRegNum)`
7366         printf("%3s:%-3s    ", getRegName(varDsc->GetRegNum()), getRegName(REG_NEXT(varDsc->GetRegNum())));
7367     }
7368     else
7369 #endif // TARGET_ARM
7370     {
7371         printf("%3s        ", getRegName(varDsc->GetRegNum()));
7372     }
7373 }
7374
7375 /*****************************************************************************
7376  *
7377  *  Dump the frame location assigned to a local.
7378  *  It's the home location, even though the variable doesn't always live
7379  *  in its home location.
7380  */
7381
7382 void Compiler::lvaDumpFrameLocation(unsigned lclNum)
7383 {
7384     int       offset;
7385     regNumber baseReg;
7386
7387 #ifdef TARGET_ARM
7388     offset = lvaFrameAddress(lclNum, compLocallocUsed, &baseReg, 0, /* isFloatUsage */ false);
7389 #else
7390     bool EBPbased;
7391     offset  = lvaFrameAddress(lclNum, &EBPbased);
7392     baseReg = EBPbased ? REG_FPBASE : REG_SPBASE;
7393 #endif
7394
7395     printf("[%2s%1s0x%02X] ", getRegName(baseReg), (offset < 0 ? "-" : "+"), (offset < 0 ? -offset : offset));
7396 }
7397
7398 /*****************************************************************************
7399  *
7400  *  dump a single lvaTable entry
7401  */
7402
7403 void Compiler::lvaDumpEntry(unsigned lclNum, FrameLayoutState curState, size_t refCntWtdWidth)
7404 {
7405     LclVarDsc* varDsc = lvaGetDesc(lclNum);
7406     var_types  type   = varDsc->TypeGet();
7407
7408     if (curState == INITIAL_FRAME_LAYOUT)
7409     {
7410         printf(";  ");
7411         gtDispLclVar(lclNum);
7412
7413         printf(" %7s ", varTypeName(type));
7414         gtDispLclVarStructType(lclNum);
7415     }
7416     else
7417     {
7418         if (varDsc->lvRefCnt() == 0)
7419         {
7420             // Print this with a special indicator that the variable is unused. Even though the
7421             // variable itself is unused, it might be a struct that is promoted, so seeing it
7422             // can be useful when looking at the promoted struct fields. It's also weird to see
7423             // missing var numbers if these aren't printed.
7424             printf(";* ");
7425         }
7426 #if FEATURE_FIXED_OUT_ARGS
7427         // Since lvaOutgoingArgSpaceSize is a PhasedVar we can't read it for Dumping until
7428         // after we set it to something.
7429         else if ((lclNum == lvaOutgoingArgSpaceVar) && lvaOutgoingArgSpaceSize.HasFinalValue() &&
7430                  (lvaOutgoingArgSpaceSize == 0))
7431         {
7432             // Similar to above; print this anyway.
7433             printf(";# ");
7434         }
7435 #endif // FEATURE_FIXED_OUT_ARGS
7436         else
7437         {
7438             printf(";  ");
7439         }
7440
7441         gtDispLclVar(lclNum);
7442
7443         printf("[V%02u", lclNum);
7444         if (varDsc->lvTracked)
7445         {
7446             printf(",T%02u]", varDsc->lvVarIndex);
7447         }
7448         else
7449         {
7450             printf("    ]");
7451         }
7452
7453         printf(" (%3u,%*s)", varDsc->lvRefCnt(lvaRefCountState), (int)refCntWtdWidth,
7454                refCntWtd2str(varDsc->lvRefCntWtd(lvaRefCountState), /* padForDecimalPlaces */ true));
7455
7456         printf(" %7s ", varTypeName(type));
7457         if (genTypeSize(type) == 0)
7458         {
7459             printf("(%2d) ", lvaLclSize(lclNum));
7460         }
7461         else
7462         {
7463             printf(" ->  ");
7464         }
7465
7466         // The register or stack location field is 11 characters wide.
7467         if ((varDsc->lvRefCnt(lvaRefCountState) == 0) && !varDsc->lvImplicitlyReferenced)
7468         {
7469             printf("zero-ref   ");
7470         }
7471         else if (varDsc->lvRegister != 0)
7472         {
7473             // It's always a register, and always in the same register.
7474             lvaDumpRegLocation(lclNum);
7475         }
7476         else if (varDsc->lvOnFrame == 0)
7477         {
7478             printf("registers  ");
7479         }
7480         else
7481         {
7482             // For RyuJIT backend, it might be in a register part of the time, but it will definitely have a stack home
7483             // location. Otherwise, it's always on the stack.
7484             if (lvaDoneFrameLayout != NO_FRAME_LAYOUT)
7485             {
7486                 lvaDumpFrameLocation(lclNum);
7487             }
7488         }
7489     }
7490
7491     if (varDsc->lvIsHfa())
7492     {
7493         printf(" HFA(%s) ", varTypeName(varDsc->GetHfaType()));
7494     }
7495
7496     if (varDsc->lvDoNotEnregister)
7497     {
7498         printf(" do-not-enreg[");
7499         if (varDsc->IsAddressExposed())
7500         {
7501             printf("X");
7502         }
7503         if (varDsc->IsHiddenBufferStructArg())
7504         {
7505             printf("H");
7506         }
7507         if (varTypeIsStruct(varDsc))
7508         {
7509             printf("S");
7510         }
7511         if (varDsc->GetDoNotEnregReason() == DoNotEnregisterReason::VMNeedsStackAddr)
7512         {
7513             printf("V");
7514         }
7515         if (lvaEnregEHVars && varDsc->lvLiveInOutOfHndlr)
7516         {
7517             printf("%c", varDsc->lvSingleDefDisqualifyReason);
7518         }
7519         if (varDsc->GetDoNotEnregReason() == DoNotEnregisterReason::LocalField)
7520         {
7521             printf("F");
7522         }
7523         if (varDsc->GetDoNotEnregReason() == DoNotEnregisterReason::BlockOp)
7524         {
7525             printf("B");
7526         }
7527         if (varDsc->lvIsMultiRegArg)
7528         {
7529             printf("A");
7530         }
7531         if (varDsc->lvIsMultiRegRet)
7532         {
7533             printf("R");
7534         }
7535 #ifdef JIT32_GCENCODER
7536         if (varDsc->lvPinned)
7537             printf("P");
7538 #endif // JIT32_GCENCODER
7539         printf("]");
7540     }
7541
7542     if (varDsc->lvIsMultiRegArg)
7543     {
7544         printf(" multireg-arg");
7545     }
7546     if (varDsc->lvIsMultiRegRet)
7547     {
7548         printf(" multireg-ret");
7549     }
7550     if (varDsc->lvMustInit)
7551     {
7552         printf(" must-init");
7553     }
7554     if (varDsc->IsAddressExposed())
7555     {
7556         printf(" addr-exposed");
7557     }
7558     if (varDsc->IsHiddenBufferStructArg())
7559     {
7560         printf(" hidden-struct-arg");
7561     }
7562     if (varDsc->lvHasLdAddrOp)
7563     {
7564         printf(" ld-addr-op");
7565     }
7566     if (lvaIsOriginalThisArg(lclNum))
7567     {
7568         printf(" this");
7569     }
7570     if (varDsc->lvPinned)
7571     {
7572         printf(" pinned");
7573     }
7574     if (varDsc->lvClassHnd != NO_CLASS_HANDLE)
7575     {
7576         printf(" class-hnd");
7577     }
7578     if (varDsc->lvClassIsExact)
7579     {
7580         printf(" exact");
7581     }
7582     if (varDsc->lvLiveInOutOfHndlr)
7583     {
7584         printf(" EH-live");
7585     }
7586     if (varDsc->lvSpillAtSingleDef)
7587     {
7588         printf(" spill-single-def");
7589     }
7590     else if (varDsc->lvSingleDefRegCandidate)
7591     {
7592         printf(" single-def");
7593     }
7594     if (lvaIsOSRLocal(lclNum) && varDsc->lvOnFrame)
7595     {
7596         printf(" tier0-frame");
7597     }
7598
7599 #ifndef TARGET_64BIT
7600     if (varDsc->lvStructDoubleAlign)
7601         printf(" double-align");
7602 #endif // !TARGET_64BIT
7603
7604     if (compGSReorderStackLayout && !varDsc->lvRegister)
7605     {
7606         if (varDsc->lvIsPtr)
7607         {
7608             printf(" ptr");
7609         }
7610         if (varDsc->lvIsUnsafeBuffer)
7611         {
7612             printf(" unsafe-buffer");
7613         }
7614     }
7615
7616     if (varDsc->lvReason != nullptr)
7617     {
7618         printf(" \"%s\"", varDsc->lvReason);
7619     }
7620
7621     if (varDsc->lvIsStructField)
7622     {
7623         LclVarDsc*       parentVarDsc  = lvaGetDesc(varDsc->lvParentLcl);
7624         lvaPromotionType promotionType = lvaGetPromotionType(parentVarDsc);
7625         switch (promotionType)
7626         {
7627             case PROMOTION_TYPE_NONE:
7628                 printf(" P-NONE");
7629                 break;
7630             case PROMOTION_TYPE_DEPENDENT:
7631                 printf(" P-DEP");
7632                 break;
7633             case PROMOTION_TYPE_INDEPENDENT:
7634                 printf(" P-INDEP");
7635                 break;
7636         }
7637     }
7638
7639     if (varDsc->lvClassHnd != NO_CLASS_HANDLE)
7640     {
7641         printf(" <%s>", eeGetClassName(varDsc->lvClassHnd));
7642     }
7643     else if (varTypeIsStruct(varDsc->TypeGet()))
7644     {
7645         ClassLayout* layout = varDsc->GetLayout();
7646         if (layout != nullptr && !layout->IsBlockLayout())
7647         {
7648             printf(" <%s>", layout->GetClassName());
7649         }
7650     }
7651
7652     printf("\n");
7653 }
7654
7655 /*****************************************************************************
7656 *
7657 *  dump the lvaTable
7658 */
7659
7660 void Compiler::lvaTableDump(FrameLayoutState curState)
7661 {
7662     if (curState == NO_FRAME_LAYOUT)
7663     {
7664         curState = lvaDoneFrameLayout;
7665         if (curState == NO_FRAME_LAYOUT)
7666         {
7667             // Still no layout? Could be a bug, but just display the initial layout
7668             curState = INITIAL_FRAME_LAYOUT;
7669         }
7670     }
7671
7672     if (curState == INITIAL_FRAME_LAYOUT)
7673     {
7674         printf("; Initial");
7675     }
7676     else if (curState == PRE_REGALLOC_FRAME_LAYOUT)
7677     {
7678         printf("; Pre-RegAlloc");
7679     }
7680     else if (curState == REGALLOC_FRAME_LAYOUT)
7681     {
7682         printf("; RegAlloc");
7683     }
7684     else if (curState == TENTATIVE_FRAME_LAYOUT)
7685     {
7686         printf("; Tentative");
7687     }
7688     else if (curState == FINAL_FRAME_LAYOUT)
7689     {
7690         printf("; Final");
7691     }
7692     else
7693     {
7694         printf("UNKNOWN FrameLayoutState!");
7695         unreached();
7696     }
7697
7698     printf(" local variable assignments\n");
7699     printf(";\n");
7700
7701     unsigned   lclNum;
7702     LclVarDsc* varDsc;
7703
7704     // Figure out some sizes, to help line things up
7705
7706     size_t refCntWtdWidth = 6; // Use 6 as the minimum width
7707
7708     if (curState != INITIAL_FRAME_LAYOUT) // don't need this info for INITIAL_FRAME_LAYOUT
7709     {
7710         for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
7711         {
7712             size_t width = strlen(refCntWtd2str(varDsc->lvRefCntWtd(lvaRefCountState), /* padForDecimalPlaces */ true));
7713             if (width > refCntWtdWidth)
7714             {
7715                 refCntWtdWidth = width;
7716             }
7717         }
7718     }
7719
7720     // Do the actual output
7721
7722     for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
7723     {
7724         lvaDumpEntry(lclNum, curState, refCntWtdWidth);
7725     }
7726
7727     //-------------------------------------------------------------------------
7728     // Display the code-gen temps
7729
7730     assert(codeGen->regSet.tmpAllFree());
7731     for (TempDsc* temp = codeGen->regSet.tmpListBeg(); temp != nullptr; temp = codeGen->regSet.tmpListNxt(temp))
7732     {
7733         printf(";  TEMP_%02u %26s%*s%7s  -> ", -temp->tdTempNum(), " ", refCntWtdWidth, " ",
7734                varTypeName(temp->tdTempType()));
7735         int offset = temp->tdTempOffs();
7736         printf(" [%2s%1s0x%02X]\n", isFramePointerUsed() ? STR_FPBASE : STR_SPBASE, (offset < 0 ? "-" : "+"),
7737                (offset < 0 ? -offset : offset));
7738     }
7739
7740     if (curState >= TENTATIVE_FRAME_LAYOUT)
7741     {
7742         printf(";\n");
7743         printf("; Lcl frame size = %d\n", compLclFrameSize);
7744     }
7745 }
7746 #endif // DEBUG
7747
7748 /*****************************************************************************
7749  *
7750  *  Conservatively estimate the layout of the stack frame.
7751  *
7752  *  This function is only used before final frame layout. It conservatively estimates the
7753  *  number of callee-saved registers that must be saved, then calls lvaAssignFrameOffsets().
7754  *  To do final frame layout, the callee-saved registers are known precisely, so
7755  *  lvaAssignFrameOffsets() is called directly.
7756  *
7757  *  Returns the (conservative, that is, overly large) estimated size of the frame,
7758  *  including the callee-saved registers. This is only used by the emitter during code
7759  *  generation when estimating the size of the offset of instructions accessing temps,
7760  *  and only if temps have a larger offset than variables.
7761  */
7762
7763 unsigned Compiler::lvaFrameSize(FrameLayoutState curState)
7764 {
7765     assert(curState < FINAL_FRAME_LAYOUT);
7766
7767     unsigned result;
7768
7769     /* Layout the stack frame conservatively.
7770        Assume all callee-saved registers are spilled to stack */
7771
7772     compCalleeRegsPushed = CNT_CALLEE_SAVED;
7773
7774 #if defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
7775     if (compFloatingPointUsed)
7776         compCalleeRegsPushed += CNT_CALLEE_SAVED_FLOAT;
7777
7778     compCalleeRegsPushed++; // we always push LR or RA. See genPushCalleeSavedRegisters
7779 #elif defined(TARGET_AMD64)
7780     if (compFloatingPointUsed)
7781     {
7782         compCalleeFPRegsSavedMask = RBM_FLT_CALLEE_SAVED;
7783     }
7784     else
7785     {
7786         compCalleeFPRegsSavedMask = RBM_NONE;
7787     }
7788 #endif
7789
7790 #if DOUBLE_ALIGN
7791     if (genDoubleAlign())
7792     {
7793         // X86 only - account for extra 4-byte pad that may be created by "and  esp, -8"  instruction
7794         compCalleeRegsPushed++;
7795     }
7796 #endif
7797
7798 #ifdef TARGET_XARCH
7799     // Since FP/EBP is included in the SAVED_REG_MAXSZ we need to
7800     // subtract 1 register if codeGen->isFramePointerUsed() is true.
7801     if (codeGen->isFramePointerUsed())
7802     {
7803         compCalleeRegsPushed--;
7804     }
7805 #endif
7806
7807     lvaAssignFrameOffsets(curState);
7808
7809     unsigned calleeSavedRegMaxSz = CALLEE_SAVED_REG_MAXSZ;
7810 #if defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
7811     if (compFloatingPointUsed)
7812     {
7813         calleeSavedRegMaxSz += CALLEE_SAVED_FLOAT_MAXSZ;
7814     }
7815     calleeSavedRegMaxSz += REGSIZE_BYTES; // we always push LR or RA. See genPushCalleeSavedRegisters
7816 #endif
7817
7818     result = compLclFrameSize + calleeSavedRegMaxSz;
7819     return result;
7820 }
7821
7822 //------------------------------------------------------------------------
7823 // lvaGetSPRelativeOffset: Given a variable, return the offset of that
7824 // variable in the frame from the stack pointer. This number will be positive,
7825 // since the stack pointer must be at a lower address than everything on the
7826 // stack.
7827 //
7828 // This can't be called for localloc functions, since the stack pointer
7829 // varies, and thus there is no fixed offset to a variable from the stack pointer.
7830 //
7831 // Arguments:
7832 //    varNum - the variable number
7833 //
7834 // Return Value:
7835 //    The offset.
7836
7837 int Compiler::lvaGetSPRelativeOffset(unsigned varNum)
7838 {
7839     assert(!compLocallocUsed);
7840     assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7841     const LclVarDsc* varDsc = lvaGetDesc(varNum);
7842     assert(varDsc->lvOnFrame);
7843     int spRelativeOffset;
7844
7845     if (varDsc->lvFramePointerBased)
7846     {
7847         // The stack offset is relative to the frame pointer, so convert it to be
7848         // relative to the stack pointer (which makes no sense for localloc functions).
7849         spRelativeOffset = varDsc->GetStackOffset() + codeGen->genSPtoFPdelta();
7850     }
7851     else
7852     {
7853         spRelativeOffset = varDsc->GetStackOffset();
7854     }
7855
7856     assert(spRelativeOffset >= 0);
7857     return spRelativeOffset;
7858 }
7859
7860 /*****************************************************************************
7861  *
7862  *  Return the caller-SP-relative stack offset of a local/parameter.
7863  *  Requires the local to be on the stack and frame layout to be complete.
7864  */
7865
7866 int Compiler::lvaGetCallerSPRelativeOffset(unsigned varNum)
7867 {
7868     assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7869     const LclVarDsc* varDsc = lvaGetDesc(varNum);
7870     assert(varDsc->lvOnFrame);
7871
7872     return lvaToCallerSPRelativeOffset(varDsc->GetStackOffset(), varDsc->lvFramePointerBased);
7873 }
7874
7875 //-----------------------------------------------------------------------------
7876 // lvaToCallerSPRelativeOffset: translate a frame offset into an offset from
7877 //    the caller's stack pointer.
7878 //
7879 // Arguments:
7880 //    offset - frame offset
7881 //    isFpBase - if true, offset is from FP, otherwise offset is from SP
7882 //    forRootFrame - if the current method is an OSR method, adjust the offset
7883 //      to be relative to the SP for the root method, instead of being relative
7884 //      to the SP for the OSR method.
7885 //
7886 // Returins:
7887 //    suitable offset
7888 //
7889 int Compiler::lvaToCallerSPRelativeOffset(int offset, bool isFpBased, bool forRootFrame) const
7890 {
7891     assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7892
7893     if (isFpBased)
7894     {
7895         offset += codeGen->genCallerSPtoFPdelta();
7896     }
7897     else
7898     {
7899         offset += codeGen->genCallerSPtoInitialSPdelta();
7900     }
7901
7902 #if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
7903     if (forRootFrame && opts.IsOSR())
7904     {
7905         const PatchpointInfo* const ppInfo = info.compPatchpointInfo;
7906
7907 #if defined(TARGET_AMD64)
7908         // The offset computed above already includes the OSR frame adjustment, plus the
7909         // pop of the "pseudo return address" from the OSR frame.
7910         //
7911         // To get to root method caller-SP, we need to subtract off the tier0 frame
7912         // size and the pushed return address and RBP for the tier0 frame (which we know is an
7913         // RPB frame).
7914         //
7915         // ppInfo's TotalFrameSize also accounts for the popped pseudo return address
7916         // between the tier0 method frame and the OSR frame. So the net adjustment
7917         // is simply TotalFrameSize plus one register.
7918         //
7919         const int adjustment = ppInfo->TotalFrameSize() + REGSIZE_BYTES;
7920
7921 #elif defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
7922
7923         const int adjustment = ppInfo->TotalFrameSize();
7924 #endif
7925
7926         offset -= adjustment;
7927     }
7928 #else
7929     // OSR NYI for other targets.
7930     assert(!opts.IsOSR());
7931 #endif
7932
7933     return offset;
7934 }
7935
7936 /*****************************************************************************
7937  *
7938  *  Return the Initial-SP-relative stack offset of a local/parameter.
7939  *  Requires the local to be on the stack and frame layout to be complete.
7940  */
7941
7942 int Compiler::lvaGetInitialSPRelativeOffset(unsigned varNum)
7943 {
7944     assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7945     const LclVarDsc* varDsc = lvaGetDesc(varNum);
7946     assert(varDsc->lvOnFrame);
7947
7948     return lvaToInitialSPRelativeOffset(varDsc->GetStackOffset(), varDsc->lvFramePointerBased);
7949 }
7950
7951 // Given a local variable offset, and whether that offset is frame-pointer based, return its offset from Initial-SP.
7952 // This is used, for example, to figure out the offset of the frame pointer from Initial-SP.
7953 int Compiler::lvaToInitialSPRelativeOffset(unsigned offset, bool isFpBased)
7954 {
7955     assert(lvaDoneFrameLayout == FINAL_FRAME_LAYOUT);
7956 #ifdef TARGET_AMD64
7957     if (isFpBased)
7958     {
7959         // Currently, the frame starts by pushing ebp, ebp points to the saved ebp
7960         // (so we have ebp pointer chaining). Add the fixed-size frame size plus the
7961         // size of the callee-saved regs (not including ebp itself) to find Initial-SP.
7962
7963         assert(codeGen->isFramePointerUsed());
7964         offset += codeGen->genSPtoFPdelta();
7965     }
7966     else
7967     {
7968         // The offset is correct already!
7969     }
7970 #else  // !TARGET_AMD64
7971     NYI("lvaToInitialSPRelativeOffset");
7972 #endif // !TARGET_AMD64
7973
7974     return offset;
7975 }
7976
7977 /*****************************************************************************/
7978
7979 #ifdef DEBUG
7980 //-----------------------------------------------------------------------------
7981 // lvaStressLclFldPadding: Pick a padding size at "random".
7982 //
7983 // Returns:
7984 //   Padding amoount in bytes
7985 //
7986 unsigned Compiler::lvaStressLclFldPadding(unsigned lclNum)
7987 {
7988     // TODO: make this a bit more random, eg:
7989     // return (lclNum ^ info.compMethodHash() ^ getJitStressLevel()) % 8;
7990
7991     // Convert every 2nd variable
7992     if (lclNum % 2)
7993     {
7994         return 0;
7995     }
7996
7997     // Pick a padding size at "random"
7998     unsigned size = lclNum % 7;
7999
8000     return size;
8001 }
8002
8003 //-----------------------------------------------------------------------------
8004 // lvaStressLclFldCB: Convert GT_LCL_VAR's to GT_LCL_FLD's
8005 //
8006 // Arguments:
8007 //    pTree -- pointer to tree to possibly convert
8008 //    data  -- walker data
8009 //
8010 // Notes:
8011 //    The stress mode does 2 passes.
8012 //
8013 //    In the first pass we will mark the locals where we CAN't apply the stress mode.
8014 //    In the second pass we will do the appropriate morphing wherever we've not determined we can't do it.
8015 //
8016 Compiler::fgWalkResult Compiler::lvaStressLclFldCB(GenTree** pTree, fgWalkData* data)
8017 {
8018     GenTree* const       tree = *pTree;
8019     GenTreeLclVarCommon* lcl  = tree->OperIsAnyLocal() ? tree->AsLclVarCommon() : nullptr;
8020
8021     if (lcl == nullptr)
8022     {
8023         return WALK_CONTINUE;
8024     }
8025
8026     Compiler* const  pComp      = ((lvaStressLclFldArgs*)data->pCallbackData)->m_pCompiler;
8027     bool const       bFirstPass = ((lvaStressLclFldArgs*)data->pCallbackData)->m_bFirstPass;
8028     unsigned const   lclNum     = lcl->GetLclNum();
8029     LclVarDsc* const varDsc     = pComp->lvaGetDesc(lclNum);
8030     var_types const  lclType    = lcl->TypeGet();
8031     var_types const  varType    = varDsc->TypeGet();
8032
8033     if (varDsc->lvNoLclFldStress)
8034     {
8035         // Already determined we can't do anything for this var
8036         return WALK_CONTINUE;
8037     }
8038
8039     if (bFirstPass)
8040     {
8041         // Ignore locals that already have field appearances
8042         if (lcl->OperIs(GT_LCL_FLD, GT_STORE_LCL_FLD) ||
8043             (lcl->OperIs(GT_LCL_ADDR) && (lcl->AsLclFld()->GetLclOffs() != 0)))
8044         {
8045             varDsc->lvNoLclFldStress = true;
8046             return WALK_CONTINUE;
8047         }
8048
8049         // Ignore arguments and temps
8050         if (varDsc->lvIsParam || lclNum >= pComp->info.compLocalsCount)
8051         {
8052             varDsc->lvNoLclFldStress = true;
8053             return WALK_CONTINUE;
8054         }
8055
8056         // Ignore OSR locals; if in memory, they will live on the
8057         // Tier0 frame and so can't have their storage adjusted.
8058         //
8059         if (pComp->lvaIsOSRLocal(lclNum))
8060         {
8061             varDsc->lvNoLclFldStress = true;
8062             return WALK_CONTINUE;
8063         }
8064
8065         // Likewise for Tier0 methods with patchpoints --
8066         // if we modify them we'll misreport their locations in the patchpoint info.
8067         //
8068         if (pComp->doesMethodHavePatchpoints() || pComp->doesMethodHavePartialCompilationPatchpoints())
8069         {
8070             varDsc->lvNoLclFldStress = true;
8071             return WALK_CONTINUE;
8072         }
8073
8074         // Converting tail calls to loops may require insertion of explicit
8075         // zero initialization for IL locals. The JIT does not support this for
8076         // TYP_BLK locals.
8077         // TODO-Cleanup: Can probably be removed now since TYP_BLK does not
8078         // exist anymore.
8079         if (pComp->compMayConvertTailCallToLoop)
8080         {
8081             varDsc->lvNoLclFldStress = true;
8082             return WALK_CONTINUE;
8083         }
8084
8085         // Fix for lcl_fld stress mode
8086         if (varDsc->lvKeepType)
8087         {
8088             varDsc->lvNoLclFldStress = true;
8089             return WALK_CONTINUE;
8090         }
8091
8092         // Can't have GC ptrs in block layouts.
8093         if (!varTypeIsArithmetic(lclType))
8094         {
8095             varDsc->lvNoLclFldStress = true;
8096             return WALK_CONTINUE;
8097         }
8098
8099         // The noway_assert in the second pass below, requires that these types match
8100         //
8101         if (varType != lclType)
8102         {
8103             varDsc->lvNoLclFldStress = true;
8104             return WALK_CONTINUE;
8105         }
8106
8107         // Weed out "small" types like TYP_BYTE as we don't mark the GT_LCL_VAR
8108         // node with the accurate small type. If we bash lvaTable[].lvType,
8109         // then there will be no indication that it was ever a small type.
8110
8111         if (genTypeSize(varType) != genTypeSize(genActualType(varType)))
8112         {
8113             varDsc->lvNoLclFldStress = true;
8114             return WALK_CONTINUE;
8115         }
8116
8117         // Offset some of the local variable by a "random" non-zero amount
8118
8119         unsigned padding = pComp->lvaStressLclFldPadding(lclNum);
8120         if (padding == 0)
8121         {
8122             varDsc->lvNoLclFldStress = true;
8123             return WALK_CONTINUE;
8124         }
8125     }
8126     else
8127     {
8128         // Do the morphing
8129         noway_assert((varType == lclType) || ((varType == TYP_STRUCT) && varDsc->GetLayout()->IsBlockLayout()));
8130
8131         // Calculate padding
8132         unsigned padding = pComp->lvaStressLclFldPadding(lclNum);
8133
8134 #if defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
8135         // We need to support alignment requirements to access memory.
8136         // Be conservative and use the maximally aligned type here.
8137         padding = roundUp(padding, genTypeSize(TYP_DOUBLE));
8138 #endif // defined(TARGET_ARMARCH) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
8139
8140         if (varType != TYP_STRUCT)
8141         {
8142             // Change the variable to a block struct
8143             ClassLayout* layout =
8144                 pComp->typGetBlkLayout(roundUp(padding + pComp->lvaLclSize(lclNum), TARGET_POINTER_SIZE));
8145             varDsc->lvType = TYP_STRUCT;
8146             varDsc->SetLayout(layout);
8147             pComp->lvaSetVarAddrExposed(lclNum DEBUGARG(AddressExposedReason::STRESS_LCL_FLD));
8148
8149             JITDUMP("Converting V%02u to %u sized block with LCL_FLD at offset (padding %u)\n", lclNum,
8150                     layout->GetSize(), padding);
8151         }
8152
8153         tree->gtFlags |= GTF_GLOB_REF;
8154
8155         // Update the trees
8156         if (tree->OperIs(GT_LCL_VAR))
8157         {
8158             tree->SetOper(GT_LCL_FLD);
8159         }
8160         else if (tree->OperIs(GT_STORE_LCL_VAR))
8161         {
8162             tree->SetOper(GT_STORE_LCL_FLD);
8163         }
8164
8165         tree->AsLclFld()->SetLclOffs(padding);
8166
8167         if (tree->OperIs(GT_STORE_LCL_FLD) && tree->IsPartialLclFld(pComp))
8168         {
8169             tree->gtFlags |= GTF_VAR_USEASG;
8170         }
8171     }
8172
8173     return WALK_CONTINUE;
8174 }
8175
8176 /*****************************************************************************/
8177
8178 void Compiler::lvaStressLclFld()
8179 {
8180     if (!compStressCompile(STRESS_LCL_FLDS, 5))
8181     {
8182         return;
8183     }
8184
8185     lvaStressLclFldArgs Args;
8186     Args.m_pCompiler  = this;
8187     Args.m_bFirstPass = true;
8188
8189     // Do First pass
8190     fgWalkAllTreesPre(lvaStressLclFldCB, &Args);
8191
8192     // Second pass
8193     Args.m_bFirstPass = false;
8194     fgWalkAllTreesPre(lvaStressLclFldCB, &Args);
8195 }
8196
8197 #endif // DEBUG
8198
8199 /*****************************************************************************
8200  *
8201  *  A little routine that displays a local variable bitset.
8202  *  'set' is mask of variables that have to be displayed
8203  *  'allVars' is the complete set of interesting variables (blank space is
8204  *    inserted if its corresponding bit is not in 'set').
8205  */
8206
8207 #ifdef DEBUG
8208 void Compiler::lvaDispVarSet(VARSET_VALARG_TP set)
8209 {
8210     VARSET_TP allVars(VarSetOps::MakeEmpty(this));
8211     lvaDispVarSet(set, allVars);
8212 }
8213
8214 void Compiler::lvaDispVarSet(VARSET_VALARG_TP set, VARSET_VALARG_TP allVars)
8215 {
8216     printf("{");
8217
8218     bool needSpace = false;
8219
8220     for (unsigned index = 0; index < lvaTrackedCount; index++)
8221     {
8222         if (VarSetOps::IsMember(this, set, index))
8223         {
8224             unsigned   lclNum;
8225             LclVarDsc* varDsc;
8226
8227             /* Look for the matching variable */
8228
8229             for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
8230             {
8231                 if ((varDsc->lvVarIndex == index) && varDsc->lvTracked)
8232                 {
8233                     break;
8234                 }
8235             }
8236
8237             if (needSpace)
8238             {
8239                 printf(" ");
8240             }
8241             else
8242             {
8243                 needSpace = true;
8244             }
8245
8246             printf("V%02u", lclNum);
8247         }
8248         else if (VarSetOps::IsMember(this, allVars, index))
8249         {
8250             if (needSpace)
8251             {
8252                 printf(" ");
8253             }
8254             else
8255             {
8256                 needSpace = true;
8257             }
8258
8259             printf("   ");
8260         }
8261     }
8262
8263     printf("}");
8264 }
8265
8266 #endif // DEBUG