1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
11 DWORD Compiler::getCanDoubleAlign()
14 if (compStressCompile(STRESS_DBL_ALN, 20))
15 return MUST_DOUBLE_ALIGN;
17 return JitConfig.JitDoubleAlign();
19 return DEFAULT_DOUBLE_ALIGN;
23 //------------------------------------------------------------------------
24 // shouldDoubleAlign: Determine whether to double-align the frame
27 // refCntStk - sum of ref counts for all stack based variables
28 // refCntEBP - sum of ref counts for EBP enregistered variables
29 // refCntWtdEBP - sum of wtd ref counts for EBP enregistered variables
30 // refCntStkParam - sum of ref counts for all stack based parameters
31 // refCntWtdStkDbl - sum of wtd ref counts for stack based doubles (including structs
32 // with double fields).
35 // Returns true if this method estimates that a double-aligned frame would be beneficial
38 // The impact of a double-aligned frame is computed as follows:
39 // - We save a byte of code for each parameter reference (they are frame-pointer relative)
40 // - We pay a byte of code for each non-parameter stack reference.
41 // - We save the misalignment penalty and possible cache-line crossing penalty.
42 // This is estimated as 0 for SMALL_CODE, 16 for FAST_CODE and 4 otherwise.
43 // - We pay 7 extra bytes for:
45 // LEA ESP,[EBP-offset]
46 // AND ESP,-8 to double align ESP
47 // - We pay one extra memory reference for each variable that could have been enregistered in EBP (refCntWtdEBP).
49 // If the misalignment penalty is estimated to be less than the bytes used, we don't double align.
50 // Otherwise, we compare the weighted ref count of ebp-enregistered variables against double the
51 // ref count for double-aligned values.
53 bool Compiler::shouldDoubleAlign(
54 unsigned refCntStk, unsigned refCntEBP, weight_t refCntWtdEBP, unsigned refCntStkParam, weight_t refCntWtdStkDbl)
56 bool doDoubleAlign = false;
57 const unsigned DBL_ALIGN_SETUP_SIZE = 7;
59 unsigned bytesUsed = refCntStk + refCntEBP - refCntStkParam + DBL_ALIGN_SETUP_SIZE;
60 unsigned misaligned_weight = 4;
62 if (compCodeOpt() == Compiler::SMALL_CODE)
63 misaligned_weight = 0;
65 if (compCodeOpt() == Compiler::FAST_CODE)
66 misaligned_weight *= 4;
68 JITDUMP("\nDouble alignment:\n");
69 JITDUMP(" Bytes that could be saved by not using EBP frame: %i\n", bytesUsed);
70 JITDUMP(" Sum of weighted ref counts for EBP enregistered variables: %f\n", refCntWtdEBP);
71 JITDUMP(" Sum of weighted ref counts for weighted stack based doubles: %f\n", refCntWtdStkDbl);
73 if (((weight_t)bytesUsed) > ((refCntWtdStkDbl * misaligned_weight) / BB_UNITY_WEIGHT))
75 JITDUMP(" Predicting not to double-align ESP to save %d bytes of code.\n", bytesUsed);
77 else if (refCntWtdEBP > refCntWtdStkDbl * 2)
79 // TODO-CQ: On P4 2 Proc XEON's, SciMark.FFT degrades if SciMark.FFT.transform_internal is
80 // not double aligned.
81 // Here are the numbers that make this not double-aligned.
82 // refCntWtdStkDbl = 0x164
83 // refCntWtdEBP = 0x1a4
84 // We think we do need to change the heuristic to be in favor of double-align.
86 JITDUMP(" Predicting not to double-align ESP to allow EBP to be used to enregister variables.\n");
90 // OK we passed all of the benefit tests, so we'll predict a double aligned frame.
91 JITDUMP(" Predicting to create a double-aligned frame\n");
96 #endif // DOUBLE_ALIGN
98 // The code to set the regState for each arg is outlined for shared use
99 // by linear scan. (It is not shared for System V AMD64 platform.)
100 regNumber Compiler::raUpdateRegStateForArg(RegState* regState, LclVarDsc* argDsc)
102 regNumber inArgReg = argDsc->GetArgReg();
103 regMaskTP inArgMask = genRegMask(inArgReg);
105 if (regState->rsIsFloat)
107 noway_assert(inArgMask & RBM_FLTARG_REGS);
109 else // regState is for the integer registers
111 // This might be the fixed return buffer register argument (on ARM64)
112 // We check and allow inArgReg to be theFixedRetBuffReg
113 if (hasFixedRetBuffReg() && (inArgReg == theFixedRetBuffReg()))
115 // We should have a TYP_BYREF or TYP_I_IMPL arg and not a TYP_STRUCT arg
116 noway_assert(argDsc->lvType == TYP_BYREF || argDsc->lvType == TYP_I_IMPL);
117 // We should have recorded the variable number for the return buffer arg
118 noway_assert(info.compRetBuffArg != BAD_VAR_NUM);
120 else // we have a regular arg
122 noway_assert(inArgMask & RBM_ARG_REGS);
126 regState->rsCalleeRegArgMaskLiveIn |= inArgMask;
129 if (argDsc->lvType == TYP_DOUBLE)
131 if (info.compIsVarArgs || opts.compUseSoftFP)
133 assert((inArgReg == REG_R0) || (inArgReg == REG_R2));
134 assert(!regState->rsIsFloat);
138 assert(regState->rsIsFloat);
139 assert(emitter::isDoubleReg(inArgReg));
141 regState->rsCalleeRegArgMaskLiveIn |= genRegMask((regNumber)(inArgReg + 1));
143 else if (argDsc->lvType == TYP_LONG)
145 assert((inArgReg == REG_R0) || (inArgReg == REG_R2));
146 assert(!regState->rsIsFloat);
147 regState->rsCalleeRegArgMaskLiveIn |= genRegMask((regNumber)(inArgReg + 1));
151 #if FEATURE_MULTIREG_ARGS
152 if (varTypeIsStruct(argDsc->lvType))
154 if (argDsc->lvIsHfaRegArg())
156 assert(regState->rsIsFloat);
157 unsigned cSlots = argDsc->lvHfaSlots();
158 for (unsigned i = 1; i < cSlots; i++)
160 assert(inArgReg + i <= LAST_FP_ARGREG);
161 regState->rsCalleeRegArgMaskLiveIn |= genRegMask(static_cast<regNumber>(inArgReg + i));
166 assert(!regState->rsIsFloat);
167 unsigned cSlots = argDsc->lvSize() / TARGET_POINTER_SIZE;
168 for (unsigned i = 1; i < cSlots; i++)
170 regNumber nextArgReg = (regNumber)(inArgReg + i);
171 if (nextArgReg > REG_ARG_LAST)
175 regState->rsCalleeRegArgMaskLiveIn |= genRegMask(nextArgReg);
179 #endif // FEATURE_MULTIREG_ARGS
184 //------------------------------------------------------------------------
185 // rpMustCreateEBPFrame:
186 // Returns true when we must create an EBP frame
189 // wbReason - [out] Detailed reason why a frame must be created. Only valid if
190 // the function returns true.
193 // This is used to force most managed methods to have EBP based frames
194 // which allows the ETW kernel stackwalker to walk the stacks of managed code
195 // this allows the kernel to perform light weight profiling
197 bool Compiler::rpMustCreateEBPFrame(INDEBUG(const char** wbReason))
201 const char* reason = nullptr;
205 if (!result && opts.OptimizationDisabled())
207 INDEBUG(reason = "Debug Code");
210 if (!result && (info.compMethodInfo->ILCodeSize > DEFAULT_MAX_INLINE_SIZE))
212 INDEBUG(reason = "IL Code Size");
215 if (!result && (fgBBcount > 3))
217 INDEBUG(reason = "BasicBlock Count");
220 if (!result && fgHasLoops)
222 INDEBUG(reason = "Method has Loops");
225 if (!result && (optCallCount >= 2))
227 INDEBUG(reason = "Call Count");
230 if (!result && (optIndirectCallCount >= 1))
232 INDEBUG(reason = "Indirect Call");
235 #endif // ETW_EBP_FRAMED
237 // VM wants to identify the containing frame of an InlinedCallFrame always
238 // via the frame register never the stack register so we need a frame.
239 if (!result && (optNativeCallCount != 0))
241 INDEBUG(reason = "Uses PInvoke");
246 // TODO-ARM64-NYI: This is temporary: force a frame pointer-based frame until genFnProlog can handle non-frame
250 INDEBUG(reason = "Temporary ARM64 force frame pointer");
253 #endif // TARGET_ARM64
255 #ifdef TARGET_LOONGARCH64
256 // TODO-LOONGARCH64-NYI: This is temporary: force a frame pointer-based frame until genFnProlog
257 // can handle non-frame pointer frames.
260 INDEBUG(reason = "Temporary LOONGARCH64 force frame pointer");
263 #endif // TARGET_LOONGARCH64
265 #ifdef TARGET_RISCV64
266 // TODO-RISCV64-NYI: This is temporary: force a frame pointer-based frame until genFnProlog
267 // can handle non-frame pointer frames.
270 INDEBUG(reason = "Temporary RISCV64 force frame pointer");
273 #endif // TARGET_RISCV64
276 if ((result == true) && (wbReason != nullptr))
285 //------------------------------------------------------------------------
287 // Mark all variables as to whether they live on the stack frame
288 // (part or whole), and if so what the base is (FP or SP).
290 void Compiler::raMarkStkVars()
295 for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
297 // lvOnFrame is set by LSRA, except in the case of zero-ref, which is set below.
299 if (lvaIsFieldOfDependentlyPromotedStruct(varDsc))
301 noway_assert(!varDsc->lvRegister);
305 // Fully enregistered variables don't need any frame space
307 if (varDsc->lvRegister)
311 // Unused variables typically don't get any frame space
312 else if (varDsc->lvRefCnt() == 0)
315 // For debugging, note that we have to reserve space even for
316 // unused variables if they are ever in scope. However, this is not
317 // an issue as fgExtendDbgLifetimes() adds an initialization and
318 // variables in scope will not have a zero ref-cnt.
319 if (opts.compDbgCode && !varDsc->lvIsParam && varDsc->lvTracked)
321 for (unsigned scopeNum = 0; scopeNum < info.compVarScopesCount; scopeNum++)
323 noway_assert(info.compVarScopes[scopeNum].vsdVarNum != lclNum);
328 varDsc->lvOnFrame = false;
329 // Clear the lvMustInit flag in case it is set
330 varDsc->lvMustInit = false;
335 if (!varDsc->lvOnFrame)
341 // The variable (or part of it) lives on the stack frame
343 noway_assert((varDsc->lvType != TYP_UNDEF) && (varDsc->lvType != TYP_VOID) && (varDsc->lvType != TYP_UNKNOWN));
344 #if FEATURE_FIXED_OUT_ARGS
345 noway_assert((lclNum == lvaOutgoingArgSpaceVar) || lvaLclSize(lclNum) != 0);
346 #else // FEATURE_FIXED_OUT_ARGS
347 noway_assert(lvaLclSize(lclNum) != 0);
348 #endif // FEATURE_FIXED_OUT_ARGS
350 varDsc->lvOnFrame = true; // Our prediction is that the final home for this local variable will be in the
354 varDsc->lvFramePointerBased = codeGen->isFramePointerUsed();
358 if (codeGen->doDoubleAlign())
360 noway_assert(codeGen->isFramePointerUsed() == false);
362 /* All arguments are off of EBP with double-aligned frames */
364 if (varDsc->lvIsParam && !varDsc->lvIsRegArg)
366 varDsc->lvFramePointerBased = true;
374 // It must be in a register, on frame, or have zero references.
376 noway_assert(varDsc->lvIsInReg() || varDsc->lvOnFrame || varDsc->lvRefCnt() == 0);
378 // We can't have both lvRegister and lvOnFrame
379 noway_assert(!varDsc->lvRegister || !varDsc->lvOnFrame);
383 // For varargs functions, there should be no direct references to
384 // parameter variables except for 'this' (because these were morphed
385 // in the importer) and the 'arglist' parameter (which is not a GC
386 // pointer). and the return buffer argument (if we are returning a
388 // This is important because we don't want to try to report them
389 // to the GC, as the frame offsets in these local variables would
392 if (varDsc->lvIsParam && raIsVarargsStackArg(lclNum))
394 if (!varDsc->lvPromoted && !varDsc->lvIsStructField)
396 noway_assert(varDsc->lvRefCnt() == 0 && !varDsc->lvRegister && !varDsc->lvOnFrame);