diff options
author | Sergey Andreenko <seandree@microsoft.com> | 2018-02-14 15:38:58 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-02-14 15:38:58 -0800 |
commit | 94510ef330ad1ecb31540a949a68d4e09c164c64 (patch) | |
tree | 37161f66b2cf10ad90232a23c1700026b3734e27 /src | |
parent | 7910a37a1cf5b963997994b6884475c224585e90 (diff) | |
download | coreclr-94510ef330ad1ecb31540a949a68d4e09c164c64.tar.gz coreclr-94510ef330ad1ecb31540a949a68d4e09c164c64.tar.bz2 coreclr-94510ef330ad1ecb31540a949a68d4e09c164c64.zip |
[RyuJit] Stack level setter (#15597)
* create a new phase: StackLevelSetter
* add repro
* Fix grammar mistakes
* use the default hash
* delete values from the map.
* create gentree::OperIsPutArgStkOrSplit
* fix more comments
* delete an extra condition that is always true
* use GTSTRUCT_2_SPECIAL for PutArgStk
* extract fgUseThrowHelperBlocks
* optimize memory for amd64 and additional checks for x86
* change checks
The previous version was wrong, because morph can call fgAddCodeRef several times for the same instruction during different phases.
* fix comments
* fix genJumpToThrowHlpBlk
* small ref in genJumpToThrowHlpBlk
* fix rebase problems.
* use fgUseThrowHelperBlocks instead of !opts.compDbgCode
* add throwHelperBlocksUsed for throughput.
Diffstat (limited to 'src')
-rw-r--r-- | src/jit/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/jit/codegencommon.cpp | 68 | ||||
-rw-r--r-- | src/jit/codegeninterface.h | 11 | ||||
-rw-r--r-- | src/jit/compiler.cpp | 4 | ||||
-rw-r--r-- | src/jit/compiler.h | 40 | ||||
-rw-r--r-- | src/jit/compphases.h | 1 | ||||
-rw-r--r-- | src/jit/flowgraph.cpp | 37 | ||||
-rw-r--r-- | src/jit/gentree.h | 5 | ||||
-rw-r--r-- | src/jit/gtstructs.h | 6 | ||||
-rw-r--r-- | src/jit/jit.settings.targets | 1 | ||||
-rw-r--r-- | src/jit/stacklevelsetter.cpp | 268 | ||||
-rw-r--r-- | src/jit/stacklevelsetter.h | 42 |
12 files changed, 438 insertions, 46 deletions
diff --git a/src/jit/CMakeLists.txt b/src/jit/CMakeLists.txt index f4626ec562..8d87336948 100644 --- a/src/jit/CMakeLists.txt +++ b/src/jit/CMakeLists.txt @@ -75,6 +75,7 @@ set( JIT_SOURCES unwind.cpp utils.cpp valuenum.cpp + stacklevelsetter.cpp ) # Add header files to Visual Studio vcxproj, not required for unixes diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 103ec69768..be29a2a10d 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -2787,17 +2787,21 @@ void CodeGen::genExitCode(BasicBlock* block) genReserveEpilog(block); } -/***************************************************************************** - * - * Generate code for an out-of-line exception. - * For debuggable code, we generate the 'throw' inline. - * For non-dbg code, we share the helper blocks created by fgAddCodeRef(). - */ - +//------------------------------------------------------------------------ +// genJumpToThrowHlpBlk: Generate code for an out-of-line exception. +// +// Notes: +// For code that uses throw helper blocks, we share the helper blocks created by fgAddCodeRef(). +// Otherwise, we generate the 'throw' inline. +// +// Arguments: +// jumpKind - jump kind to generate; +// codeKind - the special throw-helper kind; +// failBlk - optional fail target block, if it is already known; +// void CodeGen::genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKind, GenTree* failBlk) { - bool useThrowHlpBlk = !compiler->opts.compDbgCode; - + bool useThrowHlpBlk = compiler->fgUseThrowHelperBlocks(); #if defined(UNIX_X86_ABI) && FEATURE_EH_FUNCLETS // Inline exception-throwing code in funclet to make it possible to unwind funclet frames. useThrowHlpBlk = useThrowHlpBlk && (compiler->funCurrentFunc()->funKind == FUNC_ROOT); @@ -2805,41 +2809,47 @@ void CodeGen::genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKi if (useThrowHlpBlk) { - /* For non-debuggable code, find and use the helper block for - raising the exception. The block may be shared by other trees too. */ + // For code with throw helper blocks, find and use the helper block for + // raising the exception. The block may be shared by other trees too. - BasicBlock* tgtBlk; + BasicBlock* excpRaisingBlock; - if (failBlk) + if (failBlk != nullptr) { - /* We already know which block to jump to. Use that. */ + // We already know which block to jump to. Use that. + assert(failBlk->gtOper == GT_LABEL); + excpRaisingBlock = failBlk->gtLabel.gtLabBB; - noway_assert(failBlk->gtOper == GT_LABEL); - tgtBlk = failBlk->gtLabel.gtLabBB; - noway_assert( - tgtBlk == - compiler->fgFindExcptnTarget(codeKind, compiler->bbThrowIndex(compiler->compCurBB))->acdDstBlk); +#ifdef DEBUG + Compiler::AddCodeDsc* add = + compiler->fgFindExcptnTarget(codeKind, compiler->bbThrowIndex(compiler->compCurBB)); + assert(excpRaisingBlock == add->acdDstBlk); +#if !FEATURE_FIXED_OUT_ARGS + assert(add->acdStkLvlInit || isFramePointerUsed()); +#endif // !FEATURE_FIXED_OUT_ARGS +#endif // DEBUG } else { - /* Find the helper-block which raises the exception. */ - + // Find the helper-block which raises the exception. Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->bbThrowIndex(compiler->compCurBB)); PREFIX_ASSUME_MSG((add != nullptr), ("ERROR: failed to find exception throw block")); - tgtBlk = add->acdDstBlk; + excpRaisingBlock = add->acdDstBlk; +#if !FEATURE_FIXED_OUT_ARGS + assert(add->acdStkLvlInit || isFramePointerUsed()); +#endif // !FEATURE_FIXED_OUT_ARGS } - noway_assert(tgtBlk); - - // Jump to the excption-throwing block on error. + noway_assert(excpRaisingBlock != nullptr); - inst_JMP(jumpKind, tgtBlk); + // Jump to the exception-throwing block on error. + inst_JMP(jumpKind, excpRaisingBlock); } else { - /* The code to throw the exception will be generated inline, and - we will jump around it in the normal non-exception case */ + // The code to throw the exception will be generated inline, and + // we will jump around it in the normal non-exception case. BasicBlock* tgtBlk = nullptr; emitJumpKind reverseJumpKind = emitter::emitReverseJumpKind(jumpKind); @@ -2851,7 +2861,7 @@ void CodeGen::genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKi genEmitHelperCall(compiler->acdHelper(codeKind), 0, EA_UNKNOWN); - /* Define the spot for the normal non-exception case to jump to */ + // Define the spot for the normal non-exception case to jump to. if (tgtBlk != nullptr) { assert(reverseJumpKind != jumpKind); diff --git a/src/jit/codegeninterface.h b/src/jit/codegeninterface.h index b0eecbd9f4..604e3aea8d 100644 --- a/src/jit/codegeninterface.h +++ b/src/jit/codegeninterface.h @@ -225,10 +225,21 @@ public: { return m_cgFramePointerRequired; } + void setFramePointerRequired(bool value) { m_cgFramePointerRequired = value; } + + //------------------------------------------------------------------------ + // resetWritePhaseForFramePointerRequired: Return m_cgFramePointerRequired into the write phase. + // It is used only before the first phase, that locks this value, currently it is LSRA. + // Use it if you want to skip checks that set this value to true if the value is already true. + void resetWritePhaseForFramePointerRequired() + { + m_cgFramePointerRequired.ResetWritePhase(); + } + void setFramePointerRequiredEH(bool value); void setFramePointerRequiredGCInfo(bool value) diff --git a/src/jit/compiler.cpp b/src/jit/compiler.cpp index ce9b3dca13..cec1dd361d 100644 --- a/src/jit/compiler.cpp +++ b/src/jit/compiler.cpp @@ -22,6 +22,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX #ifndef LEGACY_BACKEND #include "lower.h" +#include "stacklevelsetter.h" #endif // !LEGACY_BACKEND #include "jittelemetry.h" @@ -5002,6 +5003,9 @@ void Compiler::compCompile(void** methodCodePtr, ULONG* methodCodeSize, JitFlags m_pLowering = new (this, CMK_LSRA) Lowering(this, m_pLinearScan); // PHASE_LOWERING m_pLowering->Run(); + StackLevelSetter stackLevelSetter(this); // PHASE_STACK_LEVEL_SETTER + stackLevelSetter.Run(); + assert(lvaSortAgain == false); // We should have re-run fgLocalVarLiveness() in lower.Run() lvaTrackedFixed = true; // We can not add any new tracked variables after this point. diff --git a/src/jit/compiler.h b/src/jit/compiler.h index a309d9e867..a401c47aa8 100644 --- a/src/jit/compiler.h +++ b/src/jit/compiler.h @@ -4826,8 +4826,32 @@ private: unsigned fgPtrArgCntCur; unsigned fgPtrArgCntMax; - hashBv* fgOutgoingArgTemps; - hashBv* fgCurrentlyInUseArgTemps; + +public: + //------------------------------------------------------------------------ + // fgGetPtrArgCntMax: Return the maximum number of pointer-sized stack arguments that calls inside this method + // can push on the stack. This value is calculated during morph. + // + // Return Value: + // Returns fgPtrArgCntMax, that is a private field. + // + unsigned fgGetPtrArgCntMax() const + { + return fgPtrArgCntMax; + } + + //------------------------------------------------------------------------ + // fgSetPtrArgCntMax: Set the maximum number of pointer-sized stack arguments that calls inside this method + // can push on the stack. This function is used during StackLevelSetter to fix incorrect morph calculations. + // + void fgSetPtrArgCntMax(unsigned argCntMax) + { + fgPtrArgCntMax = argCntMax; + } + +private: + hashBv* fgOutgoingArgTemps; + hashBv* fgCurrentlyInUseArgTemps; bool compCanEncodePtrArgCntMax(); @@ -5012,7 +5036,10 @@ public: BasicBlock* acdDstBlk; // block to which we jump unsigned acdData; SpecialCodeKind acdKind; // what kind of a special block is this? - unsigned short acdStkLvl; +#if !FEATURE_FIXED_OUT_ARGS + bool acdStkLvlInit; // has acdStkLvl value been already set? + unsigned acdStkLvl; +#endif // !FEATURE_FIXED_OUT_ARGS }; private: @@ -5030,6 +5057,13 @@ private: public: AddCodeDsc* fgFindExcptnTarget(SpecialCodeKind kind, unsigned refData); + bool fgUseThrowHelperBlocks(); + + AddCodeDsc* fgGetAdditionalCodeDescriptors() + { + return fgAddCodeList; + } + private: bool fgIsCodeAdded(); diff --git a/src/jit/compphases.h b/src/jit/compphases.h index fe19d91970..61db5f3c6c 100644 --- a/src/jit/compphases.h +++ b/src/jit/compphases.h @@ -92,6 +92,7 @@ CompPhaseNameMacro(PHASE_RA_ASSIGN_VARS, "RA assign vars", CompPhaseNameMacro(PHASE_LOWERING_DECOMP, "Lowering decomposition", "LWR-DEC", false, -1, false) CompPhaseNameMacro(PHASE_LOWERING, "Lowering nodeinfo", "LWR-INFO", false, -1, true) #ifndef LEGACY_BACKEND +CompPhaseNameMacro(PHASE_STACK_LEVEL_SETTER, "Calculate stack level slots", "STK-SET", false, -1, false) CompPhaseNameMacro(PHASE_LINEAR_SCAN, "Linear scan register alloc", "LSRA", true, -1, true) CompPhaseNameMacro(PHASE_LINEAR_SCAN_BUILD, "LSRA build intervals", "LSRA-BLD", false, PHASE_LINEAR_SCAN, false) CompPhaseNameMacro(PHASE_LINEAR_SCAN_ALLOC, "LSRA allocate", "LSRA-ALL", false, PHASE_LINEAR_SCAN, false) diff --git a/src/jit/flowgraph.cpp b/src/jit/flowgraph.cpp index dc714191ef..f3a0bf2ced 100644 --- a/src/jit/flowgraph.cpp +++ b/src/jit/flowgraph.cpp @@ -18201,9 +18201,7 @@ BasicBlock* Compiler::fgAddCodeRef(BasicBlock* srcBlk, unsigned refData, Special // arg slots on the stack frame if there are no other calls. compUsesThrowHelper = true; - // For debuggable code, genJumpToThrowHlpBlk() will generate the 'throw' - // code inline. It has to be kept consistent with fgAddCodeRef() - if (opts.compDbgCode) + if (!fgUseThrowHelperBlocks()) { return nullptr; } @@ -18226,7 +18224,7 @@ BasicBlock* Compiler::fgAddCodeRef(BasicBlock* srcBlk, unsigned refData, Special if (add) // found it { -#ifdef _TARGET_X86_ +#if !FEATURE_FIXED_OUT_ARGS // If different range checks happen at different stack levels, // they can't all jump to the same "call @rngChkFailed" AND have // frameless methods, as the rngChkFailed may need to unwind the @@ -18261,19 +18259,22 @@ BasicBlock* Compiler::fgAddCodeRef(BasicBlock* srcBlk, unsigned refData, Special codeGen->setFramePointerRequiredGCInfo(true); } #endif // !defined(UNIX_X86_ABI) -#endif // _TARGET_X86_ +#endif // !FEATURE_FIXED_OUT_ARGS return add->acdDstBlk; } /* We have to allocate a new entry and prepend it to the list */ - add = new (this, CMK_Unknown) AddCodeDsc; - add->acdData = refData; - add->acdKind = kind; - add->acdStkLvl = (unsigned short)stkDepth; - noway_assert(add->acdStkLvl == stkDepth); - add->acdNext = fgAddCodeList; + add = new (this, CMK_Unknown) AddCodeDsc; + add->acdData = refData; + add->acdKind = kind; + add->acdNext = fgAddCodeList; +#if !FEATURE_FIXED_OUT_ARGS + add->acdStkLvl = stkDepth; + add->acdStkLvlInit = false; +#endif // !FEATURE_FIXED_OUT_ARGS + fgAddCodeList = add; /* Create the target basic block */ @@ -18424,7 +18425,7 @@ BasicBlock* Compiler::fgAddCodeRef(BasicBlock* srcBlk, unsigned refData, Special Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(SpecialCodeKind kind, unsigned refData) { - assert(!opts.compDbgCode); + assert(fgUseThrowHelperBlocks()); if (!(fgExcptnTargetCache[kind] && // Try the cached value first fgExcptnTargetCache[kind]->acdData == refData)) { @@ -25904,3 +25905,15 @@ bool Compiler::fgNeedReturnSpillTemp() assert(compIsForInlining()); return (lvaInlineeReturnSpillTemp != BAD_VAR_NUM); } + +//------------------------------------------------------------------------ +// fgUseThrowHelperBlocks: Determinate does compiler use throw helper blocks. +// +// Note: +// For debuggable code, codegen will generate the 'throw' code inline. +// Return Value: +// true if 'throw' helper block should be created. +bool Compiler::fgUseThrowHelperBlocks() +{ + return !opts.compDbgCode; +} diff --git a/src/jit/gentree.h b/src/jit/gentree.h index 5f534db8f1..ed5664efb2 100644 --- a/src/jit/gentree.h +++ b/src/jit/gentree.h @@ -1294,6 +1294,11 @@ public: return gtOper == GT_PUTARG_STK; } + bool OperIsPutArgStkOrSplit() const + { + return OperIsPutArgStk() || OperIsPutArgSplit(); + } + bool OperIsPutArgReg() const { return gtOper == GT_PUTARG_REG; diff --git a/src/jit/gtstructs.h b/src/jit/gtstructs.h index f3ff0217f2..6cfb92c98e 100644 --- a/src/jit/gtstructs.h +++ b/src/jit/gtstructs.h @@ -109,10 +109,12 @@ GTSTRUCT_1(Qmark , GT_QMARK) GTSTRUCT_1(PhiArg , GT_PHI_ARG) GTSTRUCT_1(StoreInd , GT_STOREIND) GTSTRUCT_N(Indir , GT_STOREIND, GT_IND, GT_NULLCHECK, GT_BLK, GT_STORE_BLK, GT_OBJ, GT_STORE_OBJ, GT_DYN_BLK, GT_STORE_DYN_BLK) +#if defined(LEGACY_BACKEND) || !defined(_TARGET_ARM_) GTSTRUCT_1(PutArgStk , GT_PUTARG_STK) -#if !defined(LEGACY_BACKEND) && defined(_TARGET_ARM_) +#else // defined(_TARGET_ARM_) && !defined(LEGACY_BACKEND) +GTSTRUCT_2_SPECIAL(PutArgStk, GT_PUTARG_STK, GT_PUTARG_SPLIT) GTSTRUCT_1(PutArgSplit , GT_PUTARG_SPLIT) -#endif +#endif // defined(LEGACY_BACKEND) || !defined(_TARGET_ARM_) GTSTRUCT_1(PhysReg , GT_PHYSREG) #ifdef FEATURE_SIMD GTSTRUCT_1(SIMD , GT_SIMD) diff --git a/src/jit/jit.settings.targets b/src/jit/jit.settings.targets index 220c00fea6..467930e051 100644 --- a/src/jit/jit.settings.targets +++ b/src/jit/jit.settings.targets @@ -87,6 +87,7 @@ <CppCompile Include="..\hostallocator.cpp" /> <CppCompile Include="..\objectalloc.cpp" /> <CppCompile Include="..\sideeffects.cpp" /> + <CppCompile Include="..\stacklevelsetter.cpp" /> <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='True'" Include="..\CodeGenLegacy.cpp" /> <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\Lower.cpp" /> <CppCompile Condition="'$(ClDefines.Contains(`LEGACY_BACKEND`))'=='False'" Include="..\LSRA.cpp" /> diff --git a/src/jit/stacklevelsetter.cpp b/src/jit/stacklevelsetter.cpp new file mode 100644 index 0000000000..a3d9259257 --- /dev/null +++ b/src/jit/stacklevelsetter.cpp @@ -0,0 +1,268 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "jitpch.h" +#ifdef _MSC_VER +#pragma hdrstop +#endif + +#ifndef LEGACY_BACKEND // This file is ONLY used for the RyuJIT backend that uses the linear scan register allocator + +#include "stacklevelsetter.h" + +StackLevelSetter::StackLevelSetter(Compiler* compiler) + : Phase(compiler, "StackLevelSetter", PHASE_STACK_LEVEL_SETTER) + , currentStackLevel(0) + , maxStackLevel(0) + , memAllocator(compiler, CMK_fgArgInfoPtrArr) + , putArgNumSlots(&memAllocator) +#if !FEATURE_FIXED_OUT_ARGS + , framePointerRequired(compiler->codeGen->isFramePointerRequired()) + , throwHelperBlocksUsed(comp->fgUseThrowHelperBlocks() && comp->compUsesThrowHelper) +#endif // !FEATURE_FIXED_OUT_ARGS +{ +} + +//------------------------------------------------------------------------ +// DoPhase: Calculate stack slots numbers for outgoing args. +// +// Notes: +// For non-x86 platforms it calculates the max number of slots +// that calls inside this method can push on the stack. +// This value is used for sanity checks in the emitter. +// +// Stack slots are pointer-sized: 4 bytes for 32-bit platforms, 8 bytes for 64-bit platforms. +// +// For x86 it also sets throw-helper blocks incoming stack depth and set +// framePointerRequired when it is necessary. These values are used to pop +// pushed args when an exception occurs. +void StackLevelSetter::DoPhase() +{ + for (BasicBlock* block = comp->fgFirstBB; block != nullptr; block = block->bbNext) + { + ProcessBlock(block); + } +#if !FEATURE_FIXED_OUT_ARGS + + if (framePointerRequired && !comp->codeGen->isFramePointerRequired()) + { + JITDUMP("framePointerRequired is not set when it is required\n"); + comp->codeGen->resetWritePhaseForFramePointerRequired(); + comp->codeGen->setFramePointerRequired(true); + } +#endif // !FEATURE_FIXED_OUT_ARGS + assert(maxStackLevel <= comp->fgGetPtrArgCntMax()); + if (maxStackLevel != comp->fgGetPtrArgCntMax()) + { + JITDUMP("fgPtrArgCntMax was calculated wrong during the morph, the old value: %u, the right value: %u.\n", + comp->fgGetPtrArgCntMax(), maxStackLevel); + comp->fgSetPtrArgCntMax(maxStackLevel); + } +} + +//------------------------------------------------------------------------ +// ProcessBlock: Do stack level calculations for one block. +// +// Notes: +// Block starts and ends with an empty outgoing stack. +// Nodes in blocks are iterated in the reverse order to memorize GT_PUTARG_STK +// and GT_PUTARG_SPLIT stack sizes. +// +// Arguments: +// block - the block to process. +// +void StackLevelSetter::ProcessBlock(BasicBlock* block) +{ + assert(currentStackLevel == 0); + LIR::ReadOnlyRange& range = LIR::AsRange(block); + for (auto i = range.rbegin(); i != range.rend(); ++i) + { + GenTree* node = *i; + if (node->OperIsPutArgStkOrSplit()) + { + GenTreePutArgStk* putArg = node->AsPutArgStk(); + unsigned numSlots = putArgNumSlots[putArg]; + putArgNumSlots.Remove(putArg); + SubStackLevel(numSlots); + } + +#if !FEATURE_FIXED_OUT_ARGS + // Set throw blocks incoming stack depth for x86. + if (throwHelperBlocksUsed && !framePointerRequired) + { + bool operMightThrow = ((node->gtFlags & GTF_EXCEPT) != 0); + if (operMightThrow) + { + SetThrowHelperBlocks(node, block); + } + } +#endif // !FEATURE_FIXED_OUT_ARGS + + if (node->IsCall()) + { + GenTreeCall* call = node->AsCall(); + + unsigned usedStackSlotsCount = PopArgumentsFromCall(call); +#if defined(UNIX_X86_ABI) + assert(call->fgArgInfo->GetStkSizeBytes() == usedStackSlotsCount * TARGET_POINTER_SIZE); + call->fgArgInfo->SetStkSizeBytes(usedStackSlotsCount * TARGET_POINTER_SIZE); +#endif // UNIX_X86_ABI + } + } + assert(currentStackLevel == 0); +} + +#if !FEATURE_FIXED_OUT_ARGS +//------------------------------------------------------------------------ +// SetThrowHelperBlocks: Set throw helper blocks incoming stack levels targeted +// from the node. +// +// Notes: +// one node can target several helper blocks. +// +// Arguments: +// node - the node to process; +// block - the source block for the node. +void StackLevelSetter::SetThrowHelperBlocks(GenTree* node, BasicBlock* block) +{ + // Check that it uses throw block, find its kind, find the block, set level. + switch (node->OperGet()) + { + case GT_ARR_BOUNDS_CHECK: +#ifdef FEATURE_SIMD + case GT_SIMD_CHK: +#endif // FEATURE_SIMD + { + GenTreeBoundsChk* bndsChk = node->AsBoundsChk(); + SetThrowHelperBlock(bndsChk->gtThrowKind, block); + } + break; + case GT_INDEX_ADDR: + case GT_ARR_ELEM: + case GT_ARR_INDEX: + { + SetThrowHelperBlock(SCK_RNGCHK_FAIL, block); + } + break; + + case GT_CKFINITE: + { + SetThrowHelperBlock(SCK_ARITH_EXCPN, block); + } + break; + } + if (node->gtOverflowEx()) + { + SetThrowHelperBlock(SCK_OVERFLOW, block); + } +} + +//------------------------------------------------------------------------ +// SetThrowHelperBlock: Set throw helper block incoming stack levels targeted +// from the block with this kind. +// +// Notes: +// Set framePointerRequired if finds that the block has several incoming edges +// with different stack levels. +// +// Arguments: +// kind - the special throw-helper kind; +// block - the source block that targets helper. +void StackLevelSetter::SetThrowHelperBlock(SpecialCodeKind kind, BasicBlock* block) +{ + Compiler::AddCodeDsc* add = comp->fgFindExcptnTarget(kind, comp->bbThrowIndex(block)); + assert(add != nullptr); + if (add->acdStkLvlInit) + { + if (add->acdStkLvl != currentStackLevel) + { + framePointerRequired = true; + } + } + else + { + add->acdStkLvlInit = true; + if (add->acdStkLvl != currentStackLevel) + { + JITDUMP("Wrong stack level was set for block %d\n", add->acdDstBlk->bbNum); + } +#ifdef DEBUG + add->acdDstBlk->bbTgtStkDepth = currentStackLevel; +#endif // Debug + add->acdStkLvl = currentStackLevel; + } +} + +#endif // !FEATURE_FIXED_OUT_ARGS + +//------------------------------------------------------------------------ +// PopArgumentsFromCall: Calculate the number of stack arguments that are used by the call. +// +// Notes: +// memorize number of slots that each stack argument use. +// +// Arguments: +// call - the call to process. +// +// Return value: +// the number of stack slots in stack arguments for the call. +unsigned StackLevelSetter::PopArgumentsFromCall(GenTreeCall* call) +{ + unsigned usedStackSlotsCount = 0; + fgArgInfo* argInfo = call->fgArgInfo; + if (argInfo->HasStackArgs()) + { + for (unsigned i = 0; i < argInfo->ArgCount(); ++i) + { + fgArgTabEntry* argTab = argInfo->ArgTable()[i]; + if (argTab->numSlots != 0) + { + GenTree* node = argTab->node; + assert(node->OperIsPutArgStkOrSplit()); + + GenTreePutArgStk* putArg = node->AsPutArgStk(); + +#if !FEATURE_FIXED_OUT_ARGS + assert(argTab->numSlots == putArg->gtNumSlots); +#endif // !FEATURE_FIXED_OUT_ARGS + + putArgNumSlots.Set(putArg, argTab->numSlots); + + usedStackSlotsCount += argTab->numSlots; + AddStackLevel(argTab->numSlots); + } + } + } + return usedStackSlotsCount; +} + +//------------------------------------------------------------------------ +// SubStackLevel: Reflect pushing to the stack. +// +// Arguments: +// value - a positive value to add. +// +void StackLevelSetter::AddStackLevel(unsigned value) +{ + currentStackLevel += value; + + if (currentStackLevel > maxStackLevel) + { + maxStackLevel = currentStackLevel; + } +} + +//------------------------------------------------------------------------ +// SubStackLevel: Reflect popping from the stack. +// +// Arguments: +// value - a positive value to subtract. +// +void StackLevelSetter::SubStackLevel(unsigned value) +{ + assert(currentStackLevel >= value); + currentStackLevel -= value; +} + +#endif // !LEGACY_BACKEND diff --git a/src/jit/stacklevelsetter.h b/src/jit/stacklevelsetter.h new file mode 100644 index 0000000000..6a5b61dbb1 --- /dev/null +++ b/src/jit/stacklevelsetter.h @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#pragma once + +#include "compiler.h" +#include "phase.h" + +class StackLevelSetter : public Phase +{ +public: + StackLevelSetter(Compiler* compiler); + + virtual void DoPhase() override; + +private: + void ProcessBlock(BasicBlock* block); + +#if !FEATURE_FIXED_OUT_ARGS + void SetThrowHelperBlocks(GenTree* node, BasicBlock* block); + void SetThrowHelperBlock(SpecialCodeKind kind, BasicBlock* block); +#endif // !FEATURE_FIXED_OUT_ARGS + + unsigned PopArgumentsFromCall(GenTreeCall* call); + void AddStackLevel(unsigned value); + void SubStackLevel(unsigned value); + +private: + unsigned currentStackLevel; // current number of stack slots used by arguments. + unsigned maxStackLevel; // max number of stack slots for arguments. + + CompAllocator memAllocator; + + typedef JitHashTable<GenTreePutArgStk*, JitPtrKeyFuncs<GenTreePutArgStk>, unsigned> PutArgNumSlotsMap; + PutArgNumSlotsMap putArgNumSlots; // The hash table keeps stack slot sizes for active GT_PUTARG_STK nodes. + +#if !FEATURE_FIXED_OUT_ARGS + bool framePointerRequired; // Is frame pointer required based on the analysis made by this phase. + bool throwHelperBlocksUsed; // Were any throw helper blocks created for this method. +#endif // !FEATURE_FIXED_OUT_ARGS +}; |