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

Code before:
            // Emit the size constant expected by the memory.copy and memory.fill opcodes
            GetEmitter()->emitIns_I(INS_i32_const, EA_4BYTE, blkOp->Size());
            GetEmitter()->emitIns_I(isCopyBlk ? INS_memory_copy : INS_memory_fill, EA_8BYTE, LINEAR_MEMORY_INDEX);
            break;

        default:
            unreached();
    }
}

//------------------------------------------------------------------------
// genCodeForCpObj: Produce code for a GT_STORE_BLK node that represents a cpobj operation.
//
// Arguments:
//    cpObjNode - the node
//
void CodeGen::genCodeForCpObj(GenTreeBlk* cpObjNode)
{
    GenTree*  dstAddr     = cpObjNode->Addr();
    GenTree*  source      = cpObjNode->Data();
    var_types srcAddrType = TYP_BYREF;

    assert(source->isContained());
    if (source->OperIs(GT_IND))
    {
        source = source->gtGetOp1();
        assert(!source->isContained());
        srcAddrType = source->TypeGet();
    }

    noway_assert(source->IsLocal());

    // If the destination is on the stack we don't need the write barrier.
    bool dstOnStack = cpObjNode->IsAddressNotOnHeap(m_compiler);
    // We should have generated a memory.copy for this scenario in lowering.
    assert(!dstOnStack);

#ifdef DEBUG
    assert(!dstAddr->isContained());

    // This GenTree node has data about GC pointers, this means we're dealing
    // with CpObj.
    assert(cpObjNode->GetLayout()->HasGCPtr());
#endif // DEBUG

    genConsumeOperands(cpObjNode);

    regNumber srcReg = GetMultiUseOperandReg(source);
    regNumber dstReg = GetMultiUseOperandReg(dstAddr);

    if (cpObjNode->IsVolatile())

Review comment (performance):
We probably can never get here if we extend `TryLowerBlockStoreAsGcBulkCopyCall` to never bail. So all cpobj will go through the batch helper

Code after:
        case GenTreeBlk::BlkOpKindLoop:
            assert(!isCopyBlk);
            genCodeForInitBlkLoop(blkOp);
            break;

        case GenTreeBlk::BlkOpKindNativeOpcode:
            genConsumeOperands(blkOp);
            // Emit the size constant expected by the memory.copy and memory.fill opcodes
            GetEmitter()->emitIns_I(INS_i32_const, EA_4BYTE, blkOp->Size());
            GetEmitter()->emitIns_I(isCopyBlk ? INS_memory_copy : INS_memory_fill, EA_8BYTE, LINEAR_MEMORY_INDEX);
            break;

        default:
            unreached();
    }

    genUpdateLife(blkOp);
}

//------------------------------------------------------------------------
// genCodeForCpObj: Produce code for a GT_STORE_BLK node that represents a cpobj operation.
//
// Arguments:
//    cpObjNode - the node
//
void CodeGen::genCodeForCpObj(GenTreeBlk* cpObjNode)
{
    GenTree*  dstAddr     = cpObjNode->Addr();
    GenTree*  source      = cpObjNode->Data();
    var_types srcAddrType = TYP_BYREF;
    regNumber dstReg      = GetMultiUseOperandReg(dstAddr);
    unsigned  dstOffset   = 0;
    regNumber srcReg;
    unsigned  srcOffset;

    // Identify the register containing our source base address, either a multi-use
    //  reg representing the operand of a GT_IND, or the frame pointer for LCL_VAR/LCL_FLD.
    if (source->OperIs(GT_IND))
    {
        source = source->gtGetOp1();
        assert(!source->isContained());
        srcAddrType = source->TypeGet();
        srcReg      = GetMultiUseOperandReg(source);
        srcOffset   = 0;

        genEmitNullCheck(srcReg);
    }
    else
    {
        assert(source->OperIs(GT_LCL_FLD, GT_LCL_VAR));
        GenTreeLclVarCommon* lclVar = source->AsLclVarCommon();