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

Code before:
//------------------------------------------------------------------------------
// ShouldClone: check if this cloning looks profitable
//
// Arguments:
//   info        -- info about a cloning opportunity
//
// Returns:
//   true if cloning looks profitable
//
bool ObjectAllocator::ShouldClone(CloneInfo* info)
{
    // For now, use the same cloning size limit we use for loop cloning
    //
    int const      sizeConfig  = JitConfig.JitCloneLoopsSizeLimit();
    unsigned const sizeLimit   = (sizeConfig >= 0) ? (unsigned)sizeConfig : UINT_MAX;
    unsigned       size        = 0;
    bool           shouldClone = true;
    auto           countNode   = [&size](GenTree* tree) -> unsigned {
        size++;
        return 1;
    };

    for (BasicBlock* const block : *info->m_blocksToClone)
    {
        // Note this overstates the size a bit since we'll resolve GDVs
        // in the clone and the original...
        //
        unsigned const slack = sizeLimit - size;
        if (block->ComplexityExceeds(comp, slack, countNode))
        {
            JITDUMP("Rejecting");
            JITDUMPEXEC(DumpIndex(info->m_pseudoIndex));
            JITDUMP(" cloning: exceeds size limit %u\n", sizeLimit);
            return false;
        }
    }

    // TODO: some kind of profile check...
    //
    JITDUMP("Accepting");
    JITDUMPEXEC(DumpIndex(info->m_pseudoIndex));
    JITDUMP(" cloning: size %u does not exceed size limit %u\n", size, sizeLimit);
    return true;
}

//------------------------------------------------------------------------------
// CanClone: check that cloning can remove all escaping references and
//   is a reasonble thing to do
//
// Arguments:
//   info -- [in, out] info about the cloning opportunity

Review comment (security):
The Notes section is misleading. The function identifies blocks that should NOT be stack allocated (because they were cloned and violate single-assignment assumptions). The note suggests these blocks are "interesting" for proving non-escape, but the actual usage prevents stack allocation for these blocks. Consider revising to: "Such blocks cannot be stack allocated as they violate the single assignment assumption used by escape analysis."
```suggestion
//   Such blocks cannot be stack allocated as they violate the single assignment assumption used by escape analysis.
```

Code after:
        JITDUMP("Cloned blocks for");
        JITDUMPEXEC(DumpIndex(info->m_pseudoIndex));
        JITDUMP(" overlap with those for");
        JITDUMPEXEC(DumpIndex(c->m_pseudoIndex));
        JITDUMP(" unable to clone\n");

        overlaps = true;
        break;
    }

    return overlaps;
}

//------------------------------------------------------------------------------
// BlockIsCloneOrWasCloned: check if this block is a clone or was cloned as
//    part of an conditional escape optimization
//
// Arguments:
//   block -- block in question
//
// Returns:
//   true if block was cloned
//
// Notes:
//   Such blocks are interesting when they contain ALLCOBJs that we can prove
//   won't escape.
//
bool ObjectAllocator::BlockIsCloneOrWasCloned(BasicBlock* block)
{
    if (block->bbID >= m_initialMaxBlockID)
    {
        JITDUMP("Block " FMT_BB " was cloned as part of conditional escape processing\n", block->bbNum);
        return true;
    }

    BitVecTraits traits(m_initialMaxBlockID, comp);

    for (CloneInfo* const c : CloneMap::ValueIteration(&m_CloneMap))
    {
        // Ignore regions that we did not clone
        //
        if (!c->m_willClone)
        {
            continue;
        }

        // We don't actually clone the alloc blocks, despite them being
        // in the block set for the clone.
        //
        if (block == c->m_allocBlock)
        {