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.
10 #ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator
12 #include "stacklevelsetter.h"
14 StackLevelSetter::StackLevelSetter(Compiler* compiler)
15 : Phase(compiler, "StackLevelSetter", PHASE_STACK_LEVEL_SETTER)
16 , currentStackLevel(0)
18 , memAllocator(compiler, CMK_fgArgInfoPtrArr)
19 , putArgNumSlots(&memAllocator)
20 #if !FEATURE_FIXED_OUT_ARGS
21 , framePointerRequired(compiler->codeGen->isFramePointerRequired())
22 , throwHelperBlocksUsed(comp->fgUseThrowHelperBlocks() && comp->compUsesThrowHelper)
23 #endif // !FEATURE_FIXED_OUT_ARGS
27 //------------------------------------------------------------------------
28 // DoPhase: Calculate stack slots numbers for outgoing args.
31 // For non-x86 platforms it calculates the max number of slots
32 // that calls inside this method can push on the stack.
33 // This value is used for sanity checks in the emitter.
35 // Stack slots are pointer-sized: 4 bytes for 32-bit platforms, 8 bytes for 64-bit platforms.
37 // For x86 it also sets throw-helper blocks incoming stack depth and set
38 // framePointerRequired when it is necessary. These values are used to pop
39 // pushed args when an exception occurs.
40 void StackLevelSetter::DoPhase()
42 for (BasicBlock* block = comp->fgFirstBB; block != nullptr; block = block->bbNext)
46 #if !FEATURE_FIXED_OUT_ARGS
48 if (framePointerRequired && !comp->codeGen->isFramePointerRequired())
50 JITDUMP("framePointerRequired is not set when it is required\n");
51 comp->codeGen->resetWritePhaseForFramePointerRequired();
52 comp->codeGen->setFramePointerRequired(true);
54 #endif // !FEATURE_FIXED_OUT_ARGS
55 if (maxStackLevel != comp->fgGetPtrArgCntMax())
57 JITDUMP("fgPtrArgCntMax was calculated wrong during the morph, the old value: %u, the right value: %u.\n",
58 comp->fgGetPtrArgCntMax(), maxStackLevel);
59 comp->fgSetPtrArgCntMax(maxStackLevel);
63 //------------------------------------------------------------------------
64 // ProcessBlock: Do stack level calculations for one block.
67 // Block starts and ends with an empty outgoing stack.
68 // Nodes in blocks are iterated in the reverse order to memorize GT_PUTARG_STK
69 // and GT_PUTARG_SPLIT stack sizes.
72 // block - the block to process.
74 void StackLevelSetter::ProcessBlock(BasicBlock* block)
76 assert(currentStackLevel == 0);
77 LIR::ReadOnlyRange& range = LIR::AsRange(block);
78 for (auto i = range.rbegin(); i != range.rend(); ++i)
81 if (node->OperIsPutArgStkOrSplit())
83 GenTreePutArgStk* putArg = node->AsPutArgStk();
84 unsigned numSlots = putArgNumSlots[putArg];
85 putArgNumSlots.Remove(putArg);
86 SubStackLevel(numSlots);
89 #if !FEATURE_FIXED_OUT_ARGS
90 // Set throw blocks incoming stack depth for x86.
91 if (throwHelperBlocksUsed && !framePointerRequired)
93 if (node->OperMayThrow(comp))
95 SetThrowHelperBlocks(node, block);
98 #endif // !FEATURE_FIXED_OUT_ARGS
102 GenTreeCall* call = node->AsCall();
104 unsigned usedStackSlotsCount = PopArgumentsFromCall(call);
105 #if defined(UNIX_X86_ABI)
106 assert(call->fgArgInfo->GetStkSizeBytes() == usedStackSlotsCount * TARGET_POINTER_SIZE);
107 call->fgArgInfo->SetStkSizeBytes(usedStackSlotsCount * TARGET_POINTER_SIZE);
108 #endif // UNIX_X86_ABI
111 assert(currentStackLevel == 0);
114 #if !FEATURE_FIXED_OUT_ARGS
115 //------------------------------------------------------------------------
116 // SetThrowHelperBlocks: Set throw helper blocks incoming stack levels targeted
120 // one node can target several helper blocks, but not all operands that throw do this.
121 // So the function can set 0-2 throw blocks depends on oper and overflow flag.
124 // node - the node to process;
125 // block - the source block for the node.
126 void StackLevelSetter::SetThrowHelperBlocks(GenTree* node, BasicBlock* block)
128 assert(node->OperMayThrow(comp));
130 // Check that it uses throw block, find its kind, find the block, set level.
131 switch (node->OperGet())
133 case GT_ARR_BOUNDS_CHECK:
136 #endif // FEATURE_SIMD
137 #ifdef FEATURE_HW_INTRINSICS
138 case GT_HW_INTRINSIC_CHK:
139 #endif // FEATURE_HW_INTRINSICS
141 GenTreeBoundsChk* bndsChk = node->AsBoundsChk();
142 SetThrowHelperBlock(bndsChk->gtThrowKind, block);
149 SetThrowHelperBlock(SCK_RNGCHK_FAIL, block);
155 SetThrowHelperBlock(SCK_ARITH_EXCPN, block);
158 default: // Other opers can target throw only due to overflow.
161 if (node->gtOverflowEx())
163 SetThrowHelperBlock(SCK_OVERFLOW, block);
167 //------------------------------------------------------------------------
168 // SetThrowHelperBlock: Set throw helper block incoming stack levels targeted
169 // from the block with this kind.
172 // Set framePointerRequired if finds that the block has several incoming edges
173 // with different stack levels.
176 // kind - the special throw-helper kind;
177 // block - the source block that targets helper.
178 void StackLevelSetter::SetThrowHelperBlock(SpecialCodeKind kind, BasicBlock* block)
180 Compiler::AddCodeDsc* add = comp->fgFindExcptnTarget(kind, comp->bbThrowIndex(block));
181 assert(add != nullptr);
182 if (add->acdStkLvlInit)
184 if (add->acdStkLvl != currentStackLevel)
186 framePointerRequired = true;
191 add->acdStkLvlInit = true;
192 if (add->acdStkLvl != currentStackLevel)
194 JITDUMP("Wrong stack level was set for block %d\n", add->acdDstBlk->bbNum);
197 add->acdDstBlk->bbTgtStkDepth = currentStackLevel;
199 add->acdStkLvl = currentStackLevel;
203 #endif // !FEATURE_FIXED_OUT_ARGS
205 //------------------------------------------------------------------------
206 // PopArgumentsFromCall: Calculate the number of stack arguments that are used by the call.
209 // memorize number of slots that each stack argument use.
212 // call - the call to process.
215 // the number of stack slots in stack arguments for the call.
216 unsigned StackLevelSetter::PopArgumentsFromCall(GenTreeCall* call)
218 unsigned usedStackSlotsCount = 0;
219 fgArgInfo* argInfo = call->fgArgInfo;
220 if (argInfo->HasStackArgs())
222 for (unsigned i = 0; i < argInfo->ArgCount(); ++i)
224 fgArgTabEntry* argTab = argInfo->ArgTable()[i];
225 if (argTab->numSlots != 0)
227 GenTree* node = argTab->node;
228 assert(node->OperIsPutArgStkOrSplit());
230 GenTreePutArgStk* putArg = node->AsPutArgStk();
232 #if !FEATURE_FIXED_OUT_ARGS
233 assert(argTab->numSlots == putArg->gtNumSlots);
234 #endif // !FEATURE_FIXED_OUT_ARGS
236 putArgNumSlots.Set(putArg, argTab->numSlots);
238 usedStackSlotsCount += argTab->numSlots;
239 AddStackLevel(argTab->numSlots);
243 return usedStackSlotsCount;
246 //------------------------------------------------------------------------
247 // SubStackLevel: Reflect pushing to the stack.
250 // value - a positive value to add.
252 void StackLevelSetter::AddStackLevel(unsigned value)
254 currentStackLevel += value;
256 if (currentStackLevel > maxStackLevel)
258 maxStackLevel = currentStackLevel;
262 //------------------------------------------------------------------------
263 // SubStackLevel: Reflect popping from the stack.
266 // value - a positive value to subtract.
268 void StackLevelSetter::SubStackLevel(unsigned value)
270 assert(currentStackLevel >= value);
271 currentStackLevel -= value;
274 #endif // !LEGACY_BACKEND