1
0
Fork 0
mirror of https://github.com/VSadov/Satori.git synced 2025-06-11 18:20:26 +09:00

JIT: defer creating throw helper code until we know it's needed (#104819)

* JIT: defer creating throw helper code until we know it's needed

Defer creation of throw helper code until the stack level setter analysis has\
determined the throw helper is needed. Defer finalizing the outgoing arg size
as well.

The throw helper support now goes through the steps:
* early on phases can request that a throw helper block get created
* we create the blocks (but not the code) before final block layout so
they get proper placement
* lowering adds references to throw helpers that are actually needed
* stack level setter then materializes code for the needed helpers, and
removes the blocks for the unneeded ones
* we can then finalize the outgoing arg space size

Closes #104658.
This commit is contained in:
Andy Ayers 2024-07-12 20:07:20 -07:00 committed by GitHub
parent 663dc18eba
commit a69b4c8eb1
Signed by: github
GPG key ID: B5690EEEBB952194
6 changed files with 100 additions and 67 deletions

View file

@ -5322,6 +5322,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
// Set stack levels and analyze throw helper usage.
StackLevelSetter stackLevelSetter(this);
stackLevelSetter.Run();
m_pLowering->FinalizeOutgoingArgSpace();
// We can not add any new tracked variables after this point.
lvaTrackedFixed = true;

View file

@ -6785,6 +6785,7 @@ private:
void fgAddCodeRef(BasicBlock* srcBlk, SpecialCodeKind kind);
PhaseStatus fgCreateThrowHelperBlocks();
public:
AddCodeDsc* fgFindExcptnTarget(SpecialCodeKind kind, unsigned refData);
@ -6795,6 +6796,8 @@ public:
return fgAddCodeList;
}
void fgCreateThrowHelperBlockCode(AddCodeDsc* add);
private:
bool fgIsThrowHlpBlk(BasicBlock* block);

View file

@ -7,6 +7,8 @@
#pragma hdrstop
#endif
#include "lower.h" // for LowerRange()
// Flowgraph Miscellany
//------------------------------------------------------------------------
@ -3534,64 +3536,6 @@ PhaseStatus Compiler::fgCreateThrowHelperBlocks()
// graph optimizations. Note that no target block points to these blocks.
//
newBlk->SetFlags(BBF_IMPORTED | BBF_DONT_REMOVE);
// Figure out what code to insert
//
int helper = CORINFO_HELP_UNDEF;
switch (add->acdKind)
{
case SCK_RNGCHK_FAIL:
helper = CORINFO_HELP_RNGCHKFAIL;
break;
case SCK_DIV_BY_ZERO:
helper = CORINFO_HELP_THROWDIVZERO;
break;
case SCK_ARITH_EXCPN:
helper = CORINFO_HELP_OVERFLOW;
noway_assert(SCK_OVERFLOW == SCK_ARITH_EXCPN);
break;
case SCK_ARG_EXCPN:
helper = CORINFO_HELP_THROW_ARGUMENTEXCEPTION;
break;
case SCK_ARG_RNG_EXCPN:
helper = CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION;
break;
case SCK_FAIL_FAST:
helper = CORINFO_HELP_FAIL_FAST;
break;
default:
noway_assert(!"unexpected code addition kind");
}
noway_assert(helper != CORINFO_HELP_UNDEF);
// Add the appropriate helper call.
//
GenTreeCall* tree = gtNewHelperCallNode(helper, TYP_VOID);
// There are no args here but fgMorphArgs has side effects
// such as setting the outgoing arg area (which is necessary
// on AMD if there are any calls).
//
tree = fgMorphArgs(tree);
// Store the tree in the new basic block.
//
if (fgNodeThreading != NodeThreading::LIR)
{
fgInsertStmtAtEnd(newBlk, fgNewStmtFromTree(tree));
}
else
{
LIR::AsRange(newBlk).InsertAtEnd(LIR::SeqTree(this, tree));
}
}
fgRngChkThrowAdded = true;
@ -3599,6 +3543,79 @@ PhaseStatus Compiler::fgCreateThrowHelperBlocks()
return PhaseStatus::MODIFIED_EVERYTHING;
}
//------------------------------------------------------------------------
// fgCreateThrowHelperBlockCode: create the code for throw helper blocks
//
void Compiler::fgCreateThrowHelperBlockCode(AddCodeDsc* add)
{
assert(add->acdUsed);
// Find the block created earlier. It should be empty.
//
BasicBlock* const block = add->acdDstBlk;
assert(block->isEmpty());
// Figure out what code to insert
//
int helper = CORINFO_HELP_UNDEF;
switch (add->acdKind)
{
case SCK_RNGCHK_FAIL:
helper = CORINFO_HELP_RNGCHKFAIL;
break;
case SCK_DIV_BY_ZERO:
helper = CORINFO_HELP_THROWDIVZERO;
break;
case SCK_ARITH_EXCPN:
helper = CORINFO_HELP_OVERFLOW;
noway_assert(SCK_OVERFLOW == SCK_ARITH_EXCPN);
break;
case SCK_ARG_EXCPN:
helper = CORINFO_HELP_THROW_ARGUMENTEXCEPTION;
break;
case SCK_ARG_RNG_EXCPN:
helper = CORINFO_HELP_THROW_ARGUMENTOUTOFRANGEEXCEPTION;
break;
case SCK_FAIL_FAST:
helper = CORINFO_HELP_FAIL_FAST;
break;
default:
noway_assert(!"unexpected code addition kind");
}
noway_assert(helper != CORINFO_HELP_UNDEF);
// Add the appropriate helper call.
//
GenTreeCall* tree = gtNewHelperCallNode(helper, TYP_VOID);
// There are no args here but fgMorphArgs has side effects
// such as setting the outgoing arg area (which is necessary
// on AMD if there are any calls).
//
tree = fgMorphArgs(tree);
// Store the tree in the new basic block.
//
if (fgNodeThreading != NodeThreading::LIR)
{
fgInsertStmtAtEnd(block, fgNewStmtFromTree(tree));
}
else
{
LIR::AsRange(block).InsertAtEnd(tree);
LIR::ReadOnlyRange range(tree, tree);
m_pLowering->LowerRange(block, range);
}
}
//------------------------------------------------------------------------
// fgFindExcptnTarget: finds the block to jump to that will throw a given kind of exception
//

View file

@ -7690,8 +7690,6 @@ PhaseStatus Lowering::DoPhase()
}
#endif
FinalizeOutgoingArgSpace();
// Recompute local var ref counts before potentially sorting for liveness.
// Note this does minimal work in cases where we are not going to sort.
const bool isRecompute = true;

View file

@ -44,6 +44,8 @@ public:
lowerer.LowerRange(range);
}
void FinalizeOutgoingArgSpace();
private:
// LowerRange handles new code that is introduced by or after Lowering.
void LowerRange(LIR::ReadOnlyRange& range)
@ -602,7 +604,6 @@ private:
}
void RequireOutgoingArgSpace(GenTree* node, unsigned numBytes);
void FinalizeOutgoingArgSpace();
LinearScan* m_lsra;
unsigned vtableCallTemp; // local variable we use as a temp for vtable calls

View file

@ -66,26 +66,39 @@ PhaseStatus StackLevelSetter::DoPhase()
if (comp->opts.OptimizationEnabled())
{
comp->compUsesThrowHelper = false;
for (Compiler::AddCodeDsc* add = comp->fgGetAdditionalCodeDescriptors(); add != nullptr; add = add->acdNext)
{
if (add->acdUsed)
{
continue;
// Create the helper call
//
comp->fgCreateThrowHelperBlockCode(add);
comp->compUsesThrowHelper = true;
}
else
{
// Remove the helper call block
//
BasicBlock* const block = add->acdDstBlk;
assert(block->isEmpty());
JITDUMP("Throw help block " FMT_BB " is unused\n", block->bbNum);
block->RemoveFlags(BBF_DONT_REMOVE);
comp->fgRemoveBlock(block, /* unreachable */ true);
}
BasicBlock* const block = add->acdDstBlk;
JITDUMP("Throw help block " FMT_BB " is unused\n", block->bbNum);
block->RemoveFlags(BBF_DONT_REMOVE);
comp->fgRemoveBlock(block, /* unreachable */ true);
madeChanges = true;
}
}
else
{
// Mark all the throw helpers as used to avoid asserts later.
// Assume all helpers used. Fill in all helper block code.
//
for (Compiler::AddCodeDsc* add = comp->fgGetAdditionalCodeDescriptors(); add != nullptr; add = add->acdNext)
{
add->acdUsed = true;
comp->fgCreateThrowHelperBlockCode(add);
madeChanges = true;
}
}