Repository: dotnet/runtime
File: src/coreclr/jit/flowgraph.cpp (C++)

Code before:

    noway_assert(helper != CORINFO_HELP_UNDEF);

    // Add the appropriate helper call.
    //
    GenTreeCall* tree = gtNewHelperCallNode(helper, TYP_VOID);

    // 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).
    //
    tree = fgMorphArgs(tree);

    // Store the tree in the new basic block.
    //
    if (fgNodeThreading != NodeThreading::LIR)
    {
        fgInsertStmtAtEnd(block, fgNewStmtFromTree(tree));
    }
    else
    {
        LIR::Range range = LIR::SeqTree(this, tree);
        LIR::AsRange(block).InsertAtEnd(std::move(range));
        m_pLowering->LowerRange(block, range);
    }
}

//------------------------------------------------------------------------
// 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 (only when createIfNeeded is false).
//
Compiler::AddCodeDsc* Compiler::fgGetExcptnTarget(SpecialCodeKind kind, BasicBlock* fromBlock, bool createIfNeeded)
{
    assert(fgUseThrowHelperBlocks() || (kind == SCK_FAIL_FAST));
    AddCodeDsc*          add = nullptr;
    AddCodeDscMap* const map = fgGetAddCodeDscMap();
    AddCodeDscKey        key(kind, fromBlock, this);
    map->Lookup(key, &add);

    if (add == nullptr)
    {

Review comment (bug):
In fgCreateThrowHelperBlockCode, the LIR::Range is moved into the block via InsertAtEnd(std::move(range)) and then the moved-from `range` is passed to LowerRange. In DEBUG builds, the move constructor clears the source range, so LowerRange will see an empty range and won’t lower the inserted call, which can break later LIR/regalloc invariants. Capture first/last nodes before moving (or build a separate ReadOnlyRange after insertion) and pass that to LowerRange instead of the moved-from range.
```suggestion
        GenTree* const firstNode = range.FirstNode();
        GenTree* const lastNode  = range.LastNode();
        LIR::AsRange(block).InsertAtEnd(std::move(range));
        LIR::ReadOnlyRange blockRange(block, firstNode, lastNode);
        m_pLowering->LowerRange(block, blockRange);
```

Code after:
    }

    noway_assert(helper != CORINFO_HELP_UNDEF);

    // Add the appropriate helper call.
    //
    GenTreeCall* tree = gtNewHelperCallNode(helper, TYP_VOID);

    // 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).
    //
    tree = fgMorphArgs(tree);

    // Store the tree in the new basic block.
    //
    if (fgNodeThreading != NodeThreading::LIR)
    {
        fgInsertStmtAtEnd(block, fgNewStmtFromTree(tree));
    }
    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));
        LIR::ReadOnlyRange blockRange(firstNode, lastNode);
        m_pLowering->LowerRange(block, blockRange);
    }
}

//------------------------------------------------------------------------
// 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.
//    When createIfNeeded is false, this can return an AddCodeDsc where acdDstBlk is nullptr.
//    If createIfNeeded is true, acdDstBlk will be non-null.
//
Compiler::AddCodeDsc* Compiler::fgGetExcptnTarget(SpecialCodeKind kind, BasicBlock* fromBlock, bool createIfNeeded)
{
    assert(fgUseThrowHelperBlocks() || (kind == SCK_FAIL_FAST));
    AddCodeDsc*          add = nullptr;
    AddCodeDscMap* const map = fgGetAddCodeDscMap();