Sync may31 release/8.0-tizen (#510)
[platform/upstream/dotnet/runtime.git] / src / coreclr / jit / regalloc.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3
4 #include "jitpch.h"
5 #ifdef _MSC_VER
6 #pragma hdrstop
7 #endif
8 #include "regalloc.h"
9
10 #if DOUBLE_ALIGN
11 DWORD Compiler::getCanDoubleAlign()
12 {
13 #ifdef DEBUG
14     if (compStressCompile(STRESS_DBL_ALN, 20))
15         return MUST_DOUBLE_ALIGN;
16
17     return JitConfig.JitDoubleAlign();
18 #else
19     return DEFAULT_DOUBLE_ALIGN;
20 #endif
21 }
22
23 //------------------------------------------------------------------------
24 // shouldDoubleAlign: Determine whether to double-align the frame
25 //
26 // Arguments:
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).
33 //
34 // Return Value:
35 //    Returns true if this method estimates that a double-aligned frame would be beneficial
36 //
37 // Notes:
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:
44 //        MOV EBP,ESP,
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).
48 //
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.
52 //
53 bool Compiler::shouldDoubleAlign(
54     unsigned refCntStk, unsigned refCntEBP, weight_t refCntWtdEBP, unsigned refCntStkParam, weight_t refCntWtdStkDbl)
55 {
56     bool           doDoubleAlign        = false;
57     const unsigned DBL_ALIGN_SETUP_SIZE = 7;
58
59     unsigned bytesUsed         = refCntStk + refCntEBP - refCntStkParam + DBL_ALIGN_SETUP_SIZE;
60     unsigned misaligned_weight = 4;
61
62     if (compCodeOpt() == Compiler::SMALL_CODE)
63         misaligned_weight = 0;
64
65     if (compCodeOpt() == Compiler::FAST_CODE)
66         misaligned_weight *= 4;
67
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);
72
73     if (((weight_t)bytesUsed) > ((refCntWtdStkDbl * misaligned_weight) / BB_UNITY_WEIGHT))
74     {
75         JITDUMP("    Predicting not to double-align ESP to save %d bytes of code.\n", bytesUsed);
76     }
77     else if (refCntWtdEBP > refCntWtdStkDbl * 2)
78     {
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.
85
86         JITDUMP("    Predicting not to double-align ESP to allow EBP to be used to enregister variables.\n");
87     }
88     else
89     {
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");
92         doDoubleAlign = true;
93     }
94     return doDoubleAlign;
95 }
96 #endif // DOUBLE_ALIGN
97
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)
101 {
102     regNumber inArgReg  = argDsc->GetArgReg();
103     regMaskTP inArgMask = genRegMask(inArgReg);
104
105     if (regState->rsIsFloat)
106     {
107         noway_assert(inArgMask & RBM_FLTARG_REGS);
108     }
109     else //  regState is for the integer registers
110     {
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()))
114         {
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);
119         }
120         else // we have a regular arg
121         {
122             noway_assert(inArgMask & RBM_ARG_REGS);
123         }
124     }
125
126     regState->rsCalleeRegArgMaskLiveIn |= inArgMask;
127
128 #ifdef TARGET_ARM
129     if (argDsc->lvType == TYP_DOUBLE)
130     {
131         if (info.compIsVarArgs || opts.compUseSoftFP)
132         {
133             assert((inArgReg == REG_R0) || (inArgReg == REG_R2));
134             assert(!regState->rsIsFloat);
135         }
136         else
137         {
138             assert(regState->rsIsFloat);
139             assert(emitter::isDoubleReg(inArgReg));
140         }
141         regState->rsCalleeRegArgMaskLiveIn |= genRegMask((regNumber)(inArgReg + 1));
142     }
143     else if (argDsc->lvType == TYP_LONG)
144     {
145         assert((inArgReg == REG_R0) || (inArgReg == REG_R2));
146         assert(!regState->rsIsFloat);
147         regState->rsCalleeRegArgMaskLiveIn |= genRegMask((regNumber)(inArgReg + 1));
148     }
149 #endif // TARGET_ARM
150
151 #if FEATURE_MULTIREG_ARGS
152     if (varTypeIsStruct(argDsc->lvType))
153     {
154         if (argDsc->lvIsHfaRegArg())
155         {
156             assert(regState->rsIsFloat);
157             unsigned cSlots = argDsc->lvHfaSlots();
158             for (unsigned i = 1; i < cSlots; i++)
159             {
160                 assert(inArgReg + i <= LAST_FP_ARGREG);
161                 regState->rsCalleeRegArgMaskLiveIn |= genRegMask(static_cast<regNumber>(inArgReg + i));
162             }
163         }
164         else
165         {
166             assert(!regState->rsIsFloat);
167             unsigned cSlots = argDsc->lvSize() / TARGET_POINTER_SIZE;
168             for (unsigned i = 1; i < cSlots; i++)
169             {
170                 regNumber nextArgReg = (regNumber)(inArgReg + i);
171                 if (nextArgReg > REG_ARG_LAST)
172                 {
173                     break;
174                 }
175                 regState->rsCalleeRegArgMaskLiveIn |= genRegMask(nextArgReg);
176             }
177         }
178     }
179 #endif // FEATURE_MULTIREG_ARGS
180
181     return inArgReg;
182 }
183
184 //------------------------------------------------------------------------
185 // rpMustCreateEBPFrame:
186 //   Returns true when we must create an EBP frame
187 //
188 // Parameters:
189 //   wbReason - [out] Detailed reason why a frame must be created. Only valid if
190 //                    the function returns true.
191 //
192 // Remarks:
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
196 //
197 bool Compiler::rpMustCreateEBPFrame(INDEBUG(const char** wbReason))
198 {
199     bool result = false;
200 #ifdef DEBUG
201     const char* reason = nullptr;
202 #endif
203
204 #if ETW_EBP_FRAMED
205     if (!result && opts.OptimizationDisabled())
206     {
207         INDEBUG(reason = "Debug Code");
208         result = true;
209     }
210     if (!result && (info.compMethodInfo->ILCodeSize > DEFAULT_MAX_INLINE_SIZE))
211     {
212         INDEBUG(reason = "IL Code Size");
213         result = true;
214     }
215     if (!result && (fgBBcount > 3))
216     {
217         INDEBUG(reason = "BasicBlock Count");
218         result = true;
219     }
220     if (!result && fgHasLoops)
221     {
222         INDEBUG(reason = "Method has Loops");
223         result = true;
224     }
225     if (!result && (optCallCount >= 2))
226     {
227         INDEBUG(reason = "Call Count");
228         result = true;
229     }
230     if (!result && (optIndirectCallCount >= 1))
231     {
232         INDEBUG(reason = "Indirect Call");
233         result = true;
234     }
235 #endif // ETW_EBP_FRAMED
236
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))
240     {
241         INDEBUG(reason = "Uses PInvoke");
242         result = true;
243     }
244
245 #ifdef TARGET_ARM64
246     // TODO-ARM64-NYI: This is temporary: force a frame pointer-based frame until genFnProlog can handle non-frame
247     // pointer frames.
248     if (!result)
249     {
250         INDEBUG(reason = "Temporary ARM64 force frame pointer");
251         result = true;
252     }
253 #endif // TARGET_ARM64
254
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.
258     if (!result)
259     {
260         INDEBUG(reason = "Temporary LOONGARCH64 force frame pointer");
261         result = true;
262     }
263 #endif // TARGET_LOONGARCH64
264
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.
268     if (!result)
269     {
270         INDEBUG(reason = "Temporary RISCV64 force frame pointer");
271         result = true;
272     }
273 #endif // TARGET_RISCV64
274
275 #ifdef DEBUG
276     if ((result == true) && (wbReason != nullptr))
277     {
278         *wbReason = reason;
279     }
280 #endif
281
282     return result;
283 }
284
285 //------------------------------------------------------------------------
286 // raMarkStkVars:
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).
289 //
290 void Compiler::raMarkStkVars()
291 {
292     unsigned   lclNum;
293     LclVarDsc* varDsc;
294
295     for (lclNum = 0, varDsc = lvaTable; lclNum < lvaCount; lclNum++, varDsc++)
296     {
297         // lvOnFrame is set by LSRA, except in the case of zero-ref, which is set below.
298
299         if (lvaIsFieldOfDependentlyPromotedStruct(varDsc))
300         {
301             noway_assert(!varDsc->lvRegister);
302             goto ON_STK;
303         }
304
305         // Fully enregistered variables don't need any frame space
306
307         if (varDsc->lvRegister)
308         {
309             goto NOT_STK;
310         }
311         // Unused variables typically don't get any frame space
312         else if (varDsc->lvRefCnt() == 0)
313         {
314 #ifdef DEBUG
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)
320             {
321                 for (unsigned scopeNum = 0; scopeNum < info.compVarScopesCount; scopeNum++)
322                 {
323                     noway_assert(info.compVarScopes[scopeNum].vsdVarNum != lclNum);
324                 }
325             }
326 #endif
327
328             varDsc->lvOnFrame = false;
329             // Clear the lvMustInit flag in case it is set
330             varDsc->lvMustInit = false;
331
332             goto NOT_STK;
333         }
334
335         if (!varDsc->lvOnFrame)
336         {
337             goto NOT_STK;
338         }
339
340     ON_STK:
341         // The variable (or part of it) lives on the stack frame
342
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
349
350         varDsc->lvOnFrame = true; // Our prediction is that the final home for this local variable will be in the
351                                   // stack frame
352
353     NOT_STK:;
354         varDsc->lvFramePointerBased = codeGen->isFramePointerUsed();
355
356 #if DOUBLE_ALIGN
357
358         if (codeGen->doDoubleAlign())
359         {
360             noway_assert(codeGen->isFramePointerUsed() == false);
361
362             /* All arguments are off of EBP with double-aligned frames */
363
364             if (varDsc->lvIsParam && !varDsc->lvIsRegArg)
365             {
366                 varDsc->lvFramePointerBased = true;
367             }
368         }
369
370 #endif
371
372         // Some basic checks
373
374         // It must be in a register, on frame, or have zero references.
375
376         noway_assert(varDsc->lvIsInReg() || varDsc->lvOnFrame || varDsc->lvRefCnt() == 0);
377
378         // We can't have both lvRegister and lvOnFrame
379         noway_assert(!varDsc->lvRegister || !varDsc->lvOnFrame);
380
381 #ifdef DEBUG
382
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
387         // struct).
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
390         // not be correct.
391
392         if (varDsc->lvIsParam && raIsVarargsStackArg(lclNum))
393         {
394             if (!varDsc->lvPromoted && !varDsc->lvIsStructField)
395             {
396                 noway_assert(varDsc->lvRefCnt() == 0 && !varDsc->lvRegister && !varDsc->lvOnFrame);
397             }
398         }
399 #endif
400     }
401 }