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