HRESULT Export()

in ProfilingAPI/ReJITEnterLeaveHooks/ILRewriter.cpp [460:677]


    HRESULT Export()
    {
        // One instruction produces 2 + sizeof(native int) bytes in the worst case which can be 10 bytes for 64-bit.
        // For simplification we just use 10 here.
        unsigned maxSize = m_nInstrs * 10;

        m_pOutputBuffer = new BYTE[maxSize];
        IfNullRet(m_pOutputBuffer);

    again:
        BYTE * pIL = m_pOutputBuffer;

        bool fBranch = false;
        unsigned offset = 0;

        // Go over all instructions and produce code for them
        for (ILInstr * pInstr = m_IL.m_pNext; pInstr != &m_IL; pInstr = pInstr->m_pNext)
        {
            assert(offset < maxSize);
            pInstr->m_offset = offset;

            unsigned opcode = pInstr->m_opcode;
            if (opcode < CEE_COUNT)
            {
                // CEE_PREFIX1 refers not to instruction prefixes (like tail.), but to
                // the lead byte of multi-byte opcodes. For now, the only lead byte
                // supported is CEE_PREFIX1 = 0xFE.
                if (opcode >= 0x100)
                    m_pOutputBuffer[offset++] = CEE_PREFIX1;

                // This appears to depend on an implicit conversion from
                // unsigned opcode down to BYTE, to deliberately lose data and have
                // opcode >= 0x100 wrap around to 0.
                m_pOutputBuffer[offset++] = (opcode & 0xFF);
            }

            BYTE flags = s_OpCodeFlags[pInstr->m_opcode];
            switch (flags)
            {
            case 0:
                break;
            case 1:
                *(UNALIGNED INT8 *)&(pIL[offset]) = pInstr->m_Arg8;
                break;
            case 2:
                *(UNALIGNED INT16 *)&(pIL[offset]) = pInstr->m_Arg16;
                break;
            case 4:
                *(UNALIGNED INT32 *)&(pIL[offset]) = pInstr->m_Arg32;
                break;
            case 8:
                *(UNALIGNED INT64 *)&(pIL[offset]) = pInstr->m_Arg64;
                break;
            case 1 | OPCODEFLAGS_BranchTarget:
                fBranch = true;
                break;
            case 4 | OPCODEFLAGS_BranchTarget:
                fBranch = true;
                break;
            case 0 | OPCODEFLAGS_Switch:
                *(UNALIGNED INT32 *)&(pIL[offset]) = pInstr->m_Arg32;
                offset += sizeof(INT32);
                break;
            default:
                assert(false);
                break;
            }
            offset += (flags & OPCODEFLAGS_SizeMask);
        }
        m_IL.m_offset = offset;

        if (fBranch)
        {
            bool fTryAgain = false;
            unsigned switchBase = 0;

            // Go over all control flow instructions and resolve the targets
            for (ILInstr * pInstr = m_IL.m_pNext; pInstr != &m_IL; pInstr = pInstr->m_pNext)
            {
                unsigned opcode = pInstr->m_opcode;

                if (pInstr->m_opcode == CEE_SWITCH)
                {
                    switchBase = pInstr->m_offset + 1 + sizeof(INT32) * (pInstr->m_Arg32 + 1);
                    continue;
                }
                if (opcode == CEE_SWITCH_ARG)
                {
                    // Switch args are special
                    *(UNALIGNED INT32 *)&(pIL[pInstr->m_offset]) = pInstr->m_pTarget->m_offset - switchBase;
                    continue;
                }

                BYTE flags = s_OpCodeFlags[pInstr->m_opcode];

                if (flags & OPCODEFLAGS_BranchTarget)
                {
                    int delta = pInstr->m_pTarget->m_offset - pInstr->m_pNext->m_offset;

                    switch (flags)
                    {
                    case 1 | OPCODEFLAGS_BranchTarget:
                        // Check if delta is too big to fit into an INT8.
                        // 
                        // (see #pragma at top of file)
                        if ((INT8)delta != delta)
                        {
                            if (opcode == CEE_LEAVE_S)
                            {
                                pInstr->m_opcode = CEE_LEAVE;
                            }
                            else
                            {
                                assert(opcode >= CEE_BR_S && opcode <= CEE_BLT_UN_S);
                                pInstr->m_opcode = opcode - CEE_BR_S + CEE_BR;
                                assert(pInstr->m_opcode >= CEE_BR && pInstr->m_opcode <= CEE_BLT_UN);
                            }
                            fTryAgain = true;
                            continue;
                        }
                        *(UNALIGNED INT8 *)&(pIL[pInstr->m_pNext->m_offset - sizeof(INT8)]) = delta;
                        break;
                    case 4 | OPCODEFLAGS_BranchTarget:
                        *(UNALIGNED INT32 *)&(pIL[pInstr->m_pNext->m_offset - sizeof(INT32)]) = delta;
                        break;
                    default:
                        assert(false);
                        break;
                    }
                }
            }

            // Do the whole thing again if we changed the size of some branch targets
            if (fTryAgain)
                goto again;
        }

        unsigned codeSize = offset;
        unsigned totalSize;
        LPBYTE pBody = NULL;
        if (m_fGenerateTinyHeader)
        {
            // Make sure we can fit in a tiny header
            if (codeSize >= 64)
                return E_FAIL;

            totalSize = sizeof(IMAGE_COR_ILMETHOD_TINY) + codeSize;
            pBody = AllocateILMemory(totalSize);
            IfNullRet(pBody);

            BYTE * pCurrent = pBody;

            // Here's the tiny header
            *pCurrent = (BYTE)(CorILMethod_TinyFormat | (codeSize << 2));
            pCurrent += sizeof(IMAGE_COR_ILMETHOD_TINY);

            // And the body
            CopyMemory(pCurrent, m_pOutputBuffer, codeSize);
        }
        else
        {
            // Use FAT header

            unsigned alignedCodeSize = (offset + 3) & ~3;

            totalSize = sizeof(IMAGE_COR_ILMETHOD_FAT) + alignedCodeSize +
                (m_nEH ? (sizeof(IMAGE_COR_ILMETHOD_SECT_FAT) + sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT) * m_nEH) : 0);

            pBody = AllocateILMemory(totalSize);
            IfNullRet(pBody);

            BYTE * pCurrent = pBody;

            IMAGE_COR_ILMETHOD_FAT *pHeader = (IMAGE_COR_ILMETHOD_FAT *)pCurrent;
            pHeader->Flags = m_flags | (m_nEH ? CorILMethod_MoreSects : 0) | CorILMethod_FatFormat;
            pHeader->Size = sizeof(IMAGE_COR_ILMETHOD_FAT) / sizeof(DWORD);
            pHeader->MaxStack = m_maxStack;
            pHeader->CodeSize = offset;
            pHeader->LocalVarSigTok = m_tkLocalVarSig;

            pCurrent = (BYTE*)(pHeader + 1);

            CopyMemory(pCurrent, m_pOutputBuffer, codeSize);
            pCurrent += alignedCodeSize;

            if (m_nEH != 0)
            {
                IMAGE_COR_ILMETHOD_SECT_FAT *pEH = (IMAGE_COR_ILMETHOD_SECT_FAT *)pCurrent;
                pEH->Kind = CorILMethod_Sect_EHTable | CorILMethod_Sect_FatFormat;
                pEH->DataSize = (unsigned)(sizeof(IMAGE_COR_ILMETHOD_SECT_FAT) + sizeof(IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT) * m_nEH);

                pCurrent = (BYTE*)(pEH + 1);

                for (unsigned iEH = 0; iEH < m_nEH; iEH++)
                {
                    EHClause *pSrc = &(m_pEH[iEH]);
                    IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT * pDst = (IMAGE_COR_ILMETHOD_SECT_EH_CLAUSE_FAT *)pCurrent;

                    pDst->Flags = pSrc->m_Flags;
                    pDst->TryOffset = pSrc->m_pTryBegin->m_offset;
                    pDst->TryLength = pSrc->m_pTryEnd->m_offset - pSrc->m_pTryBegin->m_offset;
                    pDst->HandlerOffset = pSrc->m_pHandlerBegin->m_offset;
                    pDst->HandlerLength = pSrc->m_pHandlerEnd->m_pNext->m_offset - pSrc->m_pHandlerBegin->m_offset;
                    if ((pSrc->m_Flags & COR_ILEXCEPTION_CLAUSE_FILTER) == 0)
                        pDst->ClassToken = pSrc->m_ClassToken;
                    else
                        pDst->FilterOffset = pSrc->m_pFilter->m_offset;

                    pCurrent = (BYTE*)(pDst + 1);
                }
            }
        }

        IfFailRet(SetILFunctionBody(totalSize, pBody));
        DeallocateILMemory(pBody);

        return S_OK;
    }