From 3da0c4ae697e12a9f26e5c1bb80405639942757a Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 29 Jan 2026 12:26:53 -0800 Subject: [PATCH 1/5] JIT: remove anticipated demand for throw helpers Historically morph would signal to the backend that a throw helper might be needed from a certain block by calling `fgAddCodeRef`. This scheme is less viable now that we have targets like Wasm where null checks must be explicit. Modify the JIT so that throw helper demand and throw helper block/call creation is all done during the stack level setting phase, so there is no need to anticipate if throw helpers will be needed in advance. Also, always minimize the set of common throw helpers needed if not generating debuggable code (where throw helper calls are "in line"). --- src/coreclr/jit/codegencommon.cpp | 2 +- src/coreclr/jit/compiler.cpp | 4 - src/coreclr/jit/compiler.h | 11 +- src/coreclr/jit/flowgraph.cpp | 206 +++++++++++---------------- src/coreclr/jit/lower.cpp | 2 +- src/coreclr/jit/morph.cpp | 45 +----- src/coreclr/jit/stacklevelsetter.cpp | 27 ++-- 7 files changed, 107 insertions(+), 190 deletions(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 892933a516b183..d25de9feadbdd7 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -1623,7 +1623,7 @@ void CodeGen::genExitCode(BasicBlock* block) // 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(). +// For code that uses throw helper blocks, we share the helper blocks. // Otherwise, we generate the 'throw' inline. // // Arguments: diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 485fe0cef0fdbb..f7b4f1559f4d56 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -4898,10 +4898,6 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl // Insert GC Polls DoPhase(this, PHASE_INSERT_GC_POLLS, &Compiler::fgInsertGCPolls); - // Create any throw helper blocks that might be needed - // - DoPhase(this, PHASE_CREATE_THROW_HELPERS, &Compiler::fgCreateThrowHelperBlocks); - if (opts.OptimizationEnabled()) { // Conditional to switch conversion, and switch peeling diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 45c7bb022f7fa2..f2e532ba86ed82 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6774,7 +6774,7 @@ class Compiler struct AddCodeDsc { - // After fgCreateThrowHelperBlocks, the block to which + // The block to which // we jump to raise the exception. BasicBlock* acdDstBlk; @@ -6838,16 +6838,15 @@ class Compiler private: static unsigned acdHelper(SpecialCodeKind codeKind); - bool fgRngChkThrowAdded = false; AddCodeDscMap* fgAddCodeDscMap = nullptr; - - void fgAddCodeRef(BasicBlock* srcBlk, SpecialCodeKind kind); - PhaseStatus fgCreateThrowHelperBlocks(); + AddCodeDsc* fgCreateAddCodeDsc(BasicBlock* fromBlock, SpecialCodeKind kind); + void fgCreateThrowHelperBlock(AddCodeDsc* add); public: + bool fgRngChkThrowAdded = false; bool fgHasAddCodeDscMap() const { return fgAddCodeDscMap != nullptr; } - AddCodeDsc* fgFindExcptnTarget(SpecialCodeKind kind, BasicBlock* fromBlock); + AddCodeDsc* fgFindExcptnTarget(SpecialCodeKind kind, BasicBlock* fromBlock, bool createIfNeeded = false); bool fgUseThrowHelperBlocks(); void fgCreateThrowHelperBlockCode(AddCodeDsc* add); void fgSequenceLocals(Statement* stmt); diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index b255293417d487..2587f2803fed26 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3384,7 +3384,7 @@ Compiler::AddCodeDscMap* Compiler::fgGetAddCodeDscMap() } //------------------------------------------------------------------------ -// fgAddCodeRef: Indicate that a particular throw helper block will +// fgCreateAddCodeDsc: Indicate that a particular throw helper block will // be needed by the method. // // Arguments: @@ -3395,42 +3395,18 @@ Compiler::AddCodeDscMap* Compiler::fgGetAddCodeDscMap() // You can call this method after throw helpers have been created, // but it will assert if this entails creation of a new helper. // -void Compiler::fgAddCodeRef(BasicBlock* srcBlk, SpecialCodeKind kind) +Compiler::AddCodeDsc* Compiler::fgCreateAddCodeDsc(BasicBlock* srcBlk, SpecialCodeKind kind) { - // Record that the code will call a THROW_HELPER - // so on Windows Amd64 we can allocate the 4 outgoing - // arg slots on the stack frame if there are no other calls. - // - compUsesThrowHelper = true; - - if (!fgUseThrowHelperBlocks() && (kind != SCK_FAIL_FAST)) - { - // FailFast will still use a common throw helper, even in debuggable modes. - // - return; - } + assert(!fgRngChkThrowAdded); // Fetch block data and designator // AcdKeyDesignator dsg = AcdKeyDesignator::KD_NONE; unsigned const refData = (kind == SCK_FAIL_FAST) ? 0 : bbThrowIndex(srcBlk, &dsg); - // Look for an existing entry that matches what we're looking for - // - AddCodeDsc* add = fgFindExcptnTarget(kind, srcBlk); - - if (add != nullptr) - { - JITDUMP(FMT_BB " requires throw helper block for %s, sharing ACD%u (data 0x%08x)\n", srcBlk->bbNum, - sckName(kind), add->acdNum, refData); - return; - } - - assert(!fgRngChkThrowAdded); - // Allocate a new entry and prepend it to the list // - add = new (this, CMK_Unknown) AddCodeDsc; + AddCodeDsc* add = new (this, CMK_BasicBlock) AddCodeDsc; add->acdDstBlk = nullptr; add->acdTryIndex = srcBlk->bbTryIndex; @@ -3467,25 +3443,26 @@ void Compiler::fgAddCodeRef(BasicBlock* srcBlk, SpecialCodeKind kind) assert(map->Lookup(key2, &add2)); assert(add == add2); #endif + + return add; } //------------------------------------------------------------------------ -// fgCreateThrowHelperBlocks: create the needed throw helpers +// fgCreateThrowHelperBlock: create a throw helper block // -// Returns: -// Suitable phase status +// Arguments: +// add -- addCodeDesc describing the block to create // -PhaseStatus Compiler::fgCreateThrowHelperBlocks() +// Notes: +// Creates a new block if necessary, and sets the add->acdDstBlk to the new block. +// +void Compiler::fgCreateThrowHelperBlock(AddCodeDsc* add) { - if (fgAddCodeDscMap == nullptr) + if (add->acdDstBlk != nullptr) { - return PhaseStatus::MODIFIED_NOTHING; + return; } - // We should not have added throw helper blocks yet. - // - assert(!fgRngChkThrowAdded); - const static BBKinds jumpKinds[] = { BBJ_ALWAYS, // SCK_NONE BBJ_THROW, // SCK_RNGCHK_FAIL @@ -3499,89 +3476,82 @@ PhaseStatus Compiler::fgCreateThrowHelperBlocks() noway_assert(sizeof(jumpKinds) == SCK_COUNT); // sanity check - for (AddCodeDsc* const add : AddCodeDscMap::ValueIteration(fgAddCodeDscMap)) - { - // Create the target basic block in the region indicated by the acd info - // - assert(add->acdKind != SCK_NONE); - bool const putInFilter = (add->acdKeyDsg == AcdKeyDesignator::KD_FLT); - BasicBlock* const newBlk = fgNewBBinRegion(jumpKinds[add->acdKind], add->acdTryIndex, add->acdHndIndex, - /* nearBlk */ nullptr, putInFilter, - /* runRarely */ true, /* insertAtEnd */ true); + // Create the target basic block in the region indicated by the acd info + // + assert(add->acdKind != SCK_NONE); + bool const putInFilter = (add->acdKeyDsg == AcdKeyDesignator::KD_FLT); + BasicBlock* const newBlk = fgNewBBinRegion(jumpKinds[add->acdKind], add->acdTryIndex, add->acdHndIndex, + /* nearBlk */ nullptr, putInFilter, + /* runRarely */ true, /* insertAtEnd */ true); - // Update the descriptor so future lookups can find the block - // - add->acdDstBlk = newBlk; + // Update the descriptor so future lookups can find the block + // + add->acdDstBlk = newBlk; #ifdef DEBUG - if (verbose) + if (verbose) + { + const char* msgWhere = ""; + switch (add->acdKeyDsg) { - const char* msgWhere = ""; - switch (add->acdKeyDsg) - { - case AcdKeyDesignator::KD_NONE: - msgWhere = "non-EH region"; - break; - - case AcdKeyDesignator::KD_HND: - msgWhere = "handler"; - break; + case AcdKeyDesignator::KD_NONE: + msgWhere = "non-EH region"; + break; - case AcdKeyDesignator::KD_TRY: - msgWhere = "try"; - break; + case AcdKeyDesignator::KD_HND: + msgWhere = "handler"; + break; - case AcdKeyDesignator::KD_FLT: - msgWhere = "filter"; - break; + case AcdKeyDesignator::KD_TRY: + msgWhere = "try"; + break; - default: - msgWhere = "? unexpected"; - } + case AcdKeyDesignator::KD_FLT: + msgWhere = "filter"; + break; - const char* msg; - switch (add->acdKind) - { - case SCK_RNGCHK_FAIL: - msg = " for RNGCHK_FAIL"; - break; - case SCK_DIV_BY_ZERO: - msg = " for DIV_BY_ZERO"; - break; - case SCK_OVERFLOW: - msg = " for OVERFLOW"; - break; - case SCK_ARG_EXCPN: - msg = " for ARG_EXCPN"; - break; - case SCK_ARG_RNG_EXCPN: - msg = " for ARG_RNG_EXCPN"; - break; - case SCK_FAIL_FAST: - msg = " for FAIL_FAST"; - break; - case SCK_NULL_CHECK: - msg = " for NULL_CHECK"; - break; - default: - msg = " for ??"; - break; - } + default: + msgWhere = "? unexpected"; + } - printf("\nAdding throw helper " FMT_BB " for ACD%u %s in %s%s\n", newBlk->bbNum, add->acdNum, - sckName(add->acdKind), msgWhere, msg); + const char* msg; + switch (add->acdKind) + { + case SCK_RNGCHK_FAIL: + msg = " for RNGCHK_FAIL"; + break; + case SCK_DIV_BY_ZERO: + msg = " for DIV_BY_ZERO"; + break; + case SCK_OVERFLOW: + msg = " for OVERFLOW"; + break; + case SCK_ARG_EXCPN: + msg = " for ARG_EXCPN"; + break; + case SCK_ARG_RNG_EXCPN: + msg = " for ARG_RNG_EXCPN"; + break; + case SCK_FAIL_FAST: + msg = " for FAIL_FAST"; + break; + case SCK_NULL_CHECK: + msg = " for NULL_CHECK"; + break; + default: + msg = " for ??"; + break; } -#endif // DEBUG - // Mark the block as added by the compiler and not removable by future flow - // graph optimizations. Note that no target block points to these blocks. - // - newBlk->SetFlags(BBF_IMPORTED | BBF_DONT_REMOVE); + printf("\nAdding throw helper " FMT_BB " for ACD%u %s in %s%s\n", newBlk->bbNum, add->acdNum, + sckName(add->acdKind), msgWhere, msg); } +#endif // DEBUG - fgRngChkThrowAdded = true; - - return PhaseStatus::MODIFIED_EVERYTHING; + // Mark the block as added by the compiler and not removable by future flow + // graph optimizations. Note that no target block points to these blocks. + // + newBlk->SetFlags(BBF_IMPORTED | BBF_DONT_REMOVE); } //------------------------------------------------------------------------ @@ -3668,7 +3638,7 @@ void Compiler::fgCreateThrowHelperBlockCode(AddCodeDsc* add) // Code descriptor for the appropriate throw helper block, or nullptr if no such // descriptor exists. // -Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(SpecialCodeKind kind, BasicBlock* fromBlock) +Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(SpecialCodeKind kind, BasicBlock* fromBlock, bool createIfNeeded) { assert(fgUseThrowHelperBlocks() || (kind == SCK_FAIL_FAST)); AddCodeDsc* add = nullptr; @@ -3678,20 +3648,14 @@ Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(SpecialCodeKind kind, BasicBl if (add == nullptr) { - // We shouldn't be asking for these blocks late in compilation - // unless we know there are entries to be found. - if (fgRngChkThrowAdded) - { - JITDUMP(FMT_BB ": unexpected request for new throw helper: kind %d (%s), data 0x%08x\n", fromBlock->bbNum, - kind, sckName(kind), key.Data()); + add = fgCreateAddCodeDsc(fromBlock, kind); - if (kind == SCK_NULL_CHECK) - { - NYI_WASM("Missing null check demand"); - } + // Create the block... + // + if (createIfNeeded) + { + fgCreateThrowHelperBlock(add); } - - assert(!fgRngChkThrowAdded); } return add; diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index de0064ca3a1b4d..1af33251457f5f 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -12115,7 +12115,7 @@ void Lowering::FinalizeOutgoingArgSpace() // Finish computing the outgoing args area size // // Need to make sure the MIN_ARG_AREA_FOR_CALL space is added to the frame if: - // 1. there are calls to THROW_HELPER methods. + // 1. there may be calls to THROW_HELPER methods. // 2. we are generating profiling Enter/Leave/TailCall hooks. This will ensure // that even methods without any calls will have outgoing arg area space allocated. // 3. We will be generating calls to PInvoke helpers. TODO: This shouldn't be required because diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 96d72b392793e0..ab9ede0e15e61f 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -2978,13 +2978,6 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr) indexAddr->Index() = fgMorphTree(indexAddr->Index()); indexAddr->AddAllEffectsFlags(indexAddr->Arr(), indexAddr->Index()); - // Mark the indirection node as needing a range check if necessary. - // Note this will always be true unless JitSkipArrayBoundCheck() is used - if (indexAddr->IsBoundsChecked()) - { - fgAddCodeRef(compCurBB, SCK_RNGCHK_FAIL); - } - return indexAddr; } @@ -3213,7 +3206,6 @@ GenTree* Compiler::fgMorphIndexAddr(GenTreeIndexAddr* indexAddr) addr->SetHasOrderingSideEffect(); tree = gtNewOperNode(GT_COMMA, tree->TypeGet(), boundsCheck, tree); - fgAddCodeRef(compCurBB, boundsCheck->gtThrowKind); } if (indexDefn != nullptr) @@ -7799,10 +7791,6 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA { return tree; } - if (tree->OperIs(GT_CAST) && tree->gtOverflow()) - { - fgAddCodeRef(compCurBB, SCK_OVERFLOW); - } typ = tree->TypeGet(); oper = tree->OperGet(); @@ -8031,21 +8019,13 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA if ((oper == GT_DIV) || (oper == GT_MOD)) { - if ((exSetFlags & ExceptionSetFlags::ArithmeticException) != ExceptionSetFlags::None) - { - fgAddCodeRef(compCurBB, SCK_OVERFLOW); - } - else + if ((exSetFlags & ExceptionSetFlags::ArithmeticException) == ExceptionSetFlags::None) { tree->gtFlags |= GTF_DIV_MOD_NO_OVERFLOW; } } - if ((exSetFlags & ExceptionSetFlags::DivideByZeroException) != ExceptionSetFlags::None) - { - fgAddCodeRef(compCurBB, SCK_DIV_BY_ZERO); - } - else + if ((exSetFlags & ExceptionSetFlags::DivideByZeroException) == ExceptionSetFlags::None) { tree->gtFlags |= GTF_DIV_MOD_NO_BY_ZERO; } @@ -8110,12 +8090,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA CM_OVF_OP: if (tree->gtOverflow()) { - // Add the excptn-throwing basic block to jump to on overflow - - fgAddCodeRef(compCurBB, SCK_OVERFLOW); - // We can't do any commutative morphing for overflow instructions - break; } @@ -8211,13 +8186,10 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA case GT_CKFINITE: noway_assert(varTypeIsFloating(op1->TypeGet())); - - fgAddCodeRef(compCurBB, SCK_ARITH_EXCPN); break; case GT_BOUNDS_CHECK: setMethodHasBoundsChecks(); - fgAddCodeRef(compCurBB, tree->AsBoundsChk()->gtThrowKind); break; case GT_IND: @@ -11366,14 +11338,6 @@ GenTree* Compiler::fgMorphHWIntrinsic(GenTreeHWIntrinsic* tree) hasImmediateOperand = true; } -#ifdef TARGET_XARCH - if (intrinsicId == NI_Vector128_op_Division || intrinsicId == NI_Vector256_op_Division) - { - fgAddCodeRef(compCurBB, SCK_DIV_BY_ZERO); - fgAddCodeRef(compCurBB, SCK_OVERFLOW); - } -#endif // TARGET_XARCH - // ------------------------------------------------------------------------ // Process the operands, if any // @@ -12576,11 +12540,6 @@ GenTree* Compiler::fgMorphTree(GenTree* tree, MorphAddrContext* mac) { tree->gtFlags |= tree->AsArrElem()->gtArrInds[dim]->gtFlags & GTF_ALL_EFFECT; } - - if (fgGlobalMorph) - { - fgAddCodeRef(compCurBB, SCK_RNGCHK_FAIL); - } break; case GT_PHI: diff --git a/src/coreclr/jit/stacklevelsetter.cpp b/src/coreclr/jit/stacklevelsetter.cpp index a7729322e0f942..0555c8c15bc8e5 100644 --- a/src/coreclr/jit/stacklevelsetter.cpp +++ b/src/coreclr/jit/stacklevelsetter.cpp @@ -14,7 +14,7 @@ StackLevelSetter::StackLevelSetter(Compiler* compiler) , maxStackLevel(0) , memAllocator(compiler->getAllocator(CMK_CallArgs)) , putArgNumSlots(memAllocator) - , throwHelperBlocksUsed(comp->fgUseThrowHelperBlocks() && comp->compUsesThrowHelper) + , throwHelperBlocksUsed(comp->fgUseThrowHelperBlocks()) #if !FEATURE_FIXED_OUT_ARGS , framePointerRequired(compiler->codeGen->isFramePointerRequired()) #endif // !FEATURE_FIXED_OUT_ARGS @@ -50,11 +50,12 @@ PhaseStatus StackLevelSetter::DoPhase() // bool madeChanges = false; + comp->compUsesThrowHelper = false; + if (comp->fgHasAddCodeDscMap()) { if (comp->opts.OptimizationEnabled()) { - comp->compUsesThrowHelper = false; for (Compiler::AddCodeDsc* const add : Compiler::AddCodeDscMap::ValueIteration(comp->fgGetAddCodeDscMap())) { if (add->acdUsed) @@ -84,13 +85,17 @@ PhaseStatus StackLevelSetter::DoPhase() // for (Compiler::AddCodeDsc* const add : Compiler::AddCodeDscMap::ValueIteration(comp->fgGetAddCodeDscMap())) { - add->acdUsed = true; + comp->compUsesThrowHelper = true; + add->acdUsed = true; comp->fgCreateThrowHelperBlockCode(add); madeChanges = true; } } } + // We have added whatever throw helpers are needed, so set this flag + comp->fgRngChkThrowAdded = true; + return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; } @@ -103,7 +108,7 @@ void StackLevelSetter::ProcessBlocks() // Outside x86 we do not need to compute pushed/popped stack slots. // However, we do optimize throw-helpers and need to process the blocks for // that, but only when optimizing. - if (!throwHelperBlocksUsed || comp->opts.OptimizationDisabled()) + if (!throwHelperBlocksUsed) { return; } @@ -169,7 +174,7 @@ void StackLevelSetter::ProcessBlock(BasicBlock* block) // morph are likely still accurate, so we don't bother checking // if helpers are indeed used. // - bool checkForHelpers = comp->opts.OptimizationEnabled(); + bool checkForHelpers = true; #if !FEATURE_FIXED_OUT_ARGS // Even if not optimizing, if we have a moving SP frame, a shared helper may @@ -303,7 +308,7 @@ void StackLevelSetter::SetThrowHelperBlocks(GenTree* node, BasicBlock* block) // void StackLevelSetter::SetThrowHelperBlock(SpecialCodeKind kind, BasicBlock* block) { - Compiler::AddCodeDsc* add = comp->fgFindExcptnTarget(kind, block); + Compiler::AddCodeDsc* add = comp->fgFindExcptnTarget(kind, block, /* createIfNeeded */ true); assert(add != nullptr); // We expect we'll actually need this helper. @@ -326,15 +331,9 @@ void StackLevelSetter::SetThrowHelperBlock(SpecialCodeKind kind, BasicBlock* blo // For Linux/x86, we possibly need to insert stack alignment adjustment // before the first stack argument pushed for every call. But we // don't know what the stack alignment adjustment will be when - // we morph a tree that calls fgAddCodeRef(), so the stack depth + // we morph a tree that calls a throw helper, so the stack depth // number will be incorrect. For now, simply force all functions with - // these helpers to have EBP frames. It might be possible to make - // this less conservative. E.g., for top-level (not nested) calls - // without stack args, the stack pointer hasn't changed and stack - // depth will be known to be zero. Or, figure out a way to update - // or generate all required helpers after all stack alignment - // has been added, and the stack level at each call to fgAddCodeRef() - // is known, or can be recalculated. + // these helpers to have EBP frames. #if defined(UNIX_X86_ABI) framePointerRequired = true; #else // !defined(UNIX_X86_ABI) From 71aa49b48f35b2f2c42db8c6558f3d0cc65f97fa Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Thu, 29 Jan 2026 19:03:33 -0800 Subject: [PATCH 2/5] fixes for wasm --- src/coreclr/jit/flowgraph.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 2587f2803fed26..8ce68fecec4c42 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3597,6 +3597,10 @@ void Compiler::fgCreateThrowHelperBlockCode(AddCodeDsc* add) helper = CORINFO_HELP_FAIL_FAST; break; + case SCK_NULL_CHECK: + helper = CORINFO_HELP_THROWNULLREF; + break; + default: noway_assert(!"unexpected code addition kind"); } @@ -3607,7 +3611,9 @@ void Compiler::fgCreateThrowHelperBlockCode(AddCodeDsc* add) // GenTreeCall* tree = gtNewHelperCallNode(helper, TYP_VOID); - // There are no args here but fgMorphArgs has side effects + // For Wasm we may add an arg to the throw helper. + // + // Also fgMorphArgs has side effects // such as setting the outgoing arg area (which is necessary // on AMD if there are any calls). // @@ -3621,8 +3627,8 @@ void Compiler::fgCreateThrowHelperBlockCode(AddCodeDsc* add) } else { - LIR::AsRange(block).InsertAtEnd(tree); - LIR::ReadOnlyRange range(tree, tree); + LIR::Range range = LIR::SeqTree(this, tree); + LIR::AsRange(block).InsertAtEnd(std::move(range)); m_pLowering->LowerRange(block, range); } } From b866da324ae92848b1a3fec3943fa4faab0aa0b4 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 30 Jan 2026 08:00:13 -0800 Subject: [PATCH 3/5] review feedback --- src/coreclr/jit/codegenarm64.cpp | 2 +- src/coreclr/jit/codegencommon.cpp | 4 ++-- src/coreclr/jit/codegenloongarch64.cpp | 4 ++-- src/coreclr/jit/codegenriscv64.cpp | 4 ++-- src/coreclr/jit/codegenwasm.cpp | 2 +- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/flowgraph.cpp | 7 ++++--- src/coreclr/jit/stacklevelsetter.cpp | 7 ++++--- 8 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index 3aca70997906dc..ebe7245fb424af 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -5937,7 +5937,7 @@ BasicBlock* CodeGen::genGetThrowHelper(SpecialCodeKind codeKind) { // 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. - Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->compCurBB); + Compiler::AddCodeDsc* add = compiler->fgGetExcptnTarget(codeKind, compiler->compCurBB); assert((add != nullptr) && ("ERROR: failed to find exception throw block")); assert(add->acdUsed); excpRaisingBlock = add->acdDstBlk; diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index d25de9feadbdd7..2e383354652941 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -1653,7 +1653,7 @@ void CodeGen::genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKi excpRaisingBlock = failBlk; #ifdef DEBUG - Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->compCurBB); + Compiler::AddCodeDsc* add = compiler->fgGetExcptnTarget(codeKind, compiler->compCurBB); assert(add->acdUsed); assert(excpRaisingBlock == add->acdDstBlk); #if !FEATURE_FIXED_OUT_ARGS @@ -1664,7 +1664,7 @@ void CodeGen::genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKi else { // Find the helper-block which raises the exception. - Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->compCurBB); + Compiler::AddCodeDsc* add = compiler->fgGetExcptnTarget(codeKind, compiler->compCurBB); assert((add != nullptr) && ("ERROR: failed to find exception throw block")); assert(add->acdUsed); excpRaisingBlock = add->acdDstBlk; diff --git a/src/coreclr/jit/codegenloongarch64.cpp b/src/coreclr/jit/codegenloongarch64.cpp index f05e22fbcd0510..25968b11ee863c 100644 --- a/src/coreclr/jit/codegenloongarch64.cpp +++ b/src/coreclr/jit/codegenloongarch64.cpp @@ -6514,7 +6514,7 @@ inline void CodeGen::genJumpToThrowHlpBlk_la( excpRaisingBlock = failBlk; #ifdef DEBUG - Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->compCurBB); + Compiler::AddCodeDsc* add = compiler->fgGetExcptnTarget(codeKind, compiler->compCurBB); assert(add->acdUsed); assert(excpRaisingBlock == add->acdDstBlk); #if !FEATURE_FIXED_OUT_ARGS @@ -6525,7 +6525,7 @@ inline void CodeGen::genJumpToThrowHlpBlk_la( else { // Find the helper-block which raises the exception. - Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->compCurBB); + Compiler::AddCodeDsc* add = compiler->fgGetExcptnTarget(codeKind, compiler->compCurBB); assert((add != nullptr) && ("ERROR: failed to find exception throw block")); assert(add->acdUsed); excpRaisingBlock = add->acdDstBlk; diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 3b107cebce0aa1..d5aba4d3d59ec0 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -6202,7 +6202,7 @@ void CodeGen::genJumpToThrowHlpBlk_la( excpRaisingBlock = failBlk; #ifdef DEBUG - Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->compCurBB); + Compiler::AddCodeDsc* add = compiler->fgGetExcptnTarget(codeKind, compiler->compCurBB); assert(add->acdUsed); assert(excpRaisingBlock == add->acdDstBlk); #if !FEATURE_FIXED_OUT_ARGS @@ -6213,7 +6213,7 @@ void CodeGen::genJumpToThrowHlpBlk_la( else { // Find the helper-block which raises the exception. - Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->compCurBB); + Compiler::AddCodeDsc* add = compiler->fgGetExcptnTarget(codeKind, compiler->compCurBB); assert((add != nullptr) && ("ERROR: failed to find exception throw block")); assert(add->acdUsed); excpRaisingBlock = add->acdDstBlk; diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 3f9520e66cf165..db5bdaede2f718 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -1192,7 +1192,7 @@ void CodeGen::genCodeForNullCheck(GenTreeIndir* tree) // TODO-WASM: refactor once we have implemented other cases invoking throw helpers if (compiler->fgUseThrowHelperBlocks()) { - Compiler::AddCodeDsc* const add = compiler->fgFindExcptnTarget(SCK_NULL_CHECK, compiler->compCurBB); + Compiler::AddCodeDsc* const add = compiler->fgGetExcptnTarget(SCK_NULL_CHECK, compiler->compCurBB); if (add == nullptr) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index f2e532ba86ed82..0a0614908a8e27 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -6846,7 +6846,7 @@ class Compiler bool fgRngChkThrowAdded = false; bool fgHasAddCodeDscMap() const { return fgAddCodeDscMap != nullptr; } - AddCodeDsc* fgFindExcptnTarget(SpecialCodeKind kind, BasicBlock* fromBlock, bool createIfNeeded = false); + AddCodeDsc* fgGetExcptnTarget(SpecialCodeKind kind, BasicBlock* fromBlock, bool createIfNeeded = false); bool fgUseThrowHelperBlocks(); void fgCreateThrowHelperBlockCode(AddCodeDsc* add); void fgSequenceLocals(Statement* stmt); diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 8ce68fecec4c42..980e644e472814 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3634,17 +3634,18 @@ void Compiler::fgCreateThrowHelperBlockCode(AddCodeDsc* add) } //------------------------------------------------------------------------ -// fgFindExcptnTarget: finds the block to jump to that will throw a given kind of exception +// fgGetExcptnTarget: finds the block to jump to that will throw a given kind of exception // // Arguments: // kind -- kind of exception to throw // fromBlock -- block that will jump to the throw helper +// createIfNeeded -- create the block if it does not already exist // // Return Value: // Code descriptor for the appropriate throw helper block, or nullptr if no such -// descriptor exists. +// descriptor exists (only when createIfNeeded is false). // -Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(SpecialCodeKind kind, BasicBlock* fromBlock, bool createIfNeeded) +Compiler::AddCodeDsc* Compiler::fgGetExcptnTarget(SpecialCodeKind kind, BasicBlock* fromBlock, bool createIfNeeded) { assert(fgUseThrowHelperBlocks() || (kind == SCK_FAIL_FAST)); AddCodeDsc* add = nullptr; diff --git a/src/coreclr/jit/stacklevelsetter.cpp b/src/coreclr/jit/stacklevelsetter.cpp index 0555c8c15bc8e5..87bb409001b2af 100644 --- a/src/coreclr/jit/stacklevelsetter.cpp +++ b/src/coreclr/jit/stacklevelsetter.cpp @@ -106,8 +106,9 @@ void StackLevelSetter::ProcessBlocks() { #ifndef TARGET_X86 // Outside x86 we do not need to compute pushed/popped stack slots. - // However, we do optimize throw-helpers and need to process the blocks for - // that, but only when optimizing. + // However, if we are using throw helpers, we also need to process the blocks + // to find where the helpers are needed and create the blocks and calls. + // if (!throwHelperBlocksUsed) { return; @@ -308,7 +309,7 @@ void StackLevelSetter::SetThrowHelperBlocks(GenTree* node, BasicBlock* block) // void StackLevelSetter::SetThrowHelperBlock(SpecialCodeKind kind, BasicBlock* block) { - Compiler::AddCodeDsc* add = comp->fgFindExcptnTarget(kind, block, /* createIfNeeded */ true); + Compiler::AddCodeDsc* add = comp->fgGetExcptnTarget(kind, block, /* createIfNeeded */ true); assert(add != nullptr); // We expect we'll actually need this helper. From fbb3a85e876f466cf7131259a976f59e6d780394 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 30 Jan 2026 12:15:55 -0800 Subject: [PATCH 4/5] Update src/coreclr/jit/flowgraph.cpp Fix range mechanics Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/coreclr/jit/flowgraph.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 980e644e472814..f9665ba8c26d90 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3628,8 +3628,11 @@ void Compiler::fgCreateThrowHelperBlockCode(AddCodeDsc* add) else { LIR::Range range = LIR::SeqTree(this, tree); + GenTree* const firstNode = range.FirstNode(); + GenTree* const lastNode = range.LastNode(); LIR::AsRange(block).InsertAtEnd(std::move(range)); - m_pLowering->LowerRange(block, range); + LIR::ReadOnlyRange blockRange(block, firstNode, lastNode); + m_pLowering->LowerRange(block, blockRange); } } From 90771bf345afb35c3a879f21bdadf8dda2598c4a Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Fri, 30 Jan 2026 12:22:38 -0800 Subject: [PATCH 5/5] fix comment; fix copilot code --- src/coreclr/jit/flowgraph.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index f9665ba8c26d90..1a288a4bed8293 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -3391,12 +3391,13 @@ Compiler::AddCodeDscMap* Compiler::fgGetAddCodeDscMap() // srcBlk - the block that needs an entry; // kind - the kind of exception; // -// Notes: -// You can call this method after throw helpers have been created, -// but it will assert if this entails creation of a new helper. +// Returns: +// newly added AddCodeDsc // Compiler::AddCodeDsc* Compiler::fgCreateAddCodeDsc(BasicBlock* srcBlk, SpecialCodeKind kind) { + // Cannot allow new demands once we have created throw helper blocks + // assert(!fgRngChkThrowAdded); // Fetch block data and designator @@ -3627,11 +3628,11 @@ void Compiler::fgCreateThrowHelperBlockCode(AddCodeDsc* add) } else { - LIR::Range range = LIR::SeqTree(this, tree); + LIR::Range range = LIR::SeqTree(this, tree); GenTree* const firstNode = range.FirstNode(); GenTree* const lastNode = range.LastNode(); LIR::AsRange(block).InsertAtEnd(std::move(range)); - LIR::ReadOnlyRange blockRange(block, firstNode, lastNode); + LIR::ReadOnlyRange blockRange(firstNode, lastNode); m_pLowering->LowerRange(block, blockRange); } }