diff options
Diffstat (limited to 'src/vm/stubgen.h')
-rw-r--r-- | src/vm/stubgen.h | 738 |
1 files changed, 738 insertions, 0 deletions
diff --git a/src/vm/stubgen.h b/src/vm/stubgen.h new file mode 100644 index 0000000000..e6d3f9ec4d --- /dev/null +++ b/src/vm/stubgen.h @@ -0,0 +1,738 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +// File: StubGen.h +// + +// + + +#ifndef __STUBGEN_H__ +#define __STUBGEN_H__ + +#include "stublink.h" + +struct LocalDesc +{ + const static size_t MAX_LOCALDESC_ELEMENTS = 8; + + BYTE ElementType[MAX_LOCALDESC_ELEMENTS]; + size_t cbType; + TypeHandle InternalToken; // only valid with ELEMENT_TYPE_INTERNAL + + // used only for E_T_FNPTR and E_T_ARRAY + PCCOR_SIGNATURE pSig; + union + { + Module* pSigModule; + size_t cbArrayBoundsInfo; + BOOL bIsCopyConstructed; // used for E_T_PTR + }; + + LocalDesc() + { + } + + inline LocalDesc(CorElementType elemType) + { + ElementType[0] = static_cast<BYTE>(elemType); + cbType = 1; + bIsCopyConstructed = FALSE; + } + + inline LocalDesc(TypeHandle thType) + { + ElementType[0] = ELEMENT_TYPE_INTERNAL; + cbType = 1; + InternalToken = thType; + bIsCopyConstructed = FALSE; + } + + inline LocalDesc(MethodTable *pMT) + { + WRAPPER_NO_CONTRACT; + ElementType[0] = ELEMENT_TYPE_INTERNAL; + cbType = 1; + InternalToken = TypeHandle(pMT); + bIsCopyConstructed = FALSE; + } + + void MakeByRef() + { + ChangeType(ELEMENT_TYPE_BYREF); + } + + void MakePinned() + { + ChangeType(ELEMENT_TYPE_PINNED); + } + + // makes the LocalDesc semantically equivalent to ET_TYPE_CMOD_REQD<IsCopyConstructed>/ET_TYPE_CMOD_REQD<NeedsCopyConstructorModifier> + void MakeCopyConstructedPointer() + { + ChangeType(ELEMENT_TYPE_PTR); + bIsCopyConstructed = TRUE; + } + + void ChangeType(CorElementType elemType) + { + LIMITED_METHOD_CONTRACT; + PREFIX_ASSUME((MAX_LOCALDESC_ELEMENTS-1) >= cbType); + + for (size_t i = cbType; i >= 1; i--) + { + ElementType[i] = ElementType[i-1]; + } + + ElementType[0] = static_cast<BYTE>(elemType); + cbType += 1; + } + + bool IsValueClass() + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(cbType == 1); // this only works on 1-element types for now + } + CONTRACTL_END; + + if (ElementType[0] == ELEMENT_TYPE_VALUETYPE) + { + return true; + } + else if ((ElementType[0] == ELEMENT_TYPE_INTERNAL) && + (InternalToken.IsNativeValueType() || + InternalToken.GetMethodTable()->IsValueType())) + { + return true; + } + + return false; + } +}; + +class StubSigBuilder +{ +public: + StubSigBuilder(); + + DWORD Append(LocalDesc* pLoc); + +protected: + CQuickBytes m_qbSigBuffer; + DWORD m_nItems; + BYTE* m_pbSigCursor; + size_t m_cbSig; + + enum Constants { INITIAL_BUFFER_SIZE = 256 }; + + void EnsureEnoughQuickBytes(size_t cbToAppend); +}; + +//--------------------------------------------------------------------------------------- +// +class LocalSigBuilder : protected StubSigBuilder +{ +public: + DWORD NewLocal(LocalDesc * pLoc) + { + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + PRECONDITION(CheckPointer(pLoc)); + } + CONTRACTL_END; + + return Append(pLoc); + } + + DWORD GetSigSize(); + DWORD GetSig(BYTE * pbSig, DWORD cbBuffer); + +}; // class LocalSigBuilder + +//--------------------------------------------------------------------------------------- +// +class FunctionSigBuilder : protected StubSigBuilder +{ +public: + FunctionSigBuilder(); + + DWORD NewArg(LocalDesc * pArg) + { + WRAPPER_NO_CONTRACT; + + return Append(pArg); + } + + DWORD GetNumArgs() + { + LIMITED_METHOD_CONTRACT; + return m_nItems; + } + + void SetCallingConv(CorCallingConvention callingConv) + { + LIMITED_METHOD_CONTRACT; + m_callingConv = callingConv; + } + + CorCallingConvention GetCallingConv() + { + LIMITED_METHOD_CONTRACT; + return m_callingConv; + } + + void SetSig(PCCOR_SIGNATURE pSig, DWORD cSig); + + DWORD GetSigSize(); + DWORD GetSig(BYTE * pbSig, DWORD cbBuffer); + + void SetReturnType(LocalDesc* pLoc); + + CorElementType GetReturnElementType() + { + LIMITED_METHOD_CONTRACT; + + CONSISTENCY_CHECK(m_qbReturnSig.Size() > 0); + return *(CorElementType *)m_qbReturnSig.Ptr(); + } + + PCCOR_SIGNATURE GetReturnSig() + { + LIMITED_METHOD_CONTRACT; + + CONSISTENCY_CHECK(m_qbReturnSig.Size() > 0); + return (PCCOR_SIGNATURE)m_qbReturnSig.Ptr(); + } + +protected: + CorCallingConvention m_callingConv; + CQuickBytes m_qbReturnSig; +}; // class FunctionSigBuilder + +#ifdef _DEBUG +// exercise the resize code +#define TOKEN_LOOKUP_MAP_SIZE (8*sizeof(void*)) +#else // _DEBUG +#define TOKEN_LOOKUP_MAP_SIZE (64*sizeof(void*)) +#endif // _DEBUG + +//--------------------------------------------------------------------------------------- +// +class TokenLookupMap +{ +public: + TokenLookupMap() + { + STANDARD_VM_CONTRACT; + + m_qbEntries.AllocThrows(TOKEN_LOOKUP_MAP_SIZE); + m_nextAvailableRid = 0; + } + + // copy ctor + TokenLookupMap(TokenLookupMap* pSrc) + { + STANDARD_VM_CONTRACT; + + m_nextAvailableRid = pSrc->m_nextAvailableRid; + size_t size = pSrc->m_qbEntries.Size(); + m_qbEntries.AllocThrows(size); + memcpy(m_qbEntries.Ptr(), pSrc->m_qbEntries.Ptr(), size); + } + + TypeHandle LookupTypeDef(mdToken token) + { + WRAPPER_NO_CONTRACT; + return LookupTokenWorker<mdtTypeDef, MethodTable*>(token); + } + MethodDesc* LookupMethodDef(mdToken token) + { + WRAPPER_NO_CONTRACT; + return LookupTokenWorker<mdtMethodDef, MethodDesc*>(token); + } + FieldDesc* LookupFieldDef(mdToken token) + { + WRAPPER_NO_CONTRACT; + return LookupTokenWorker<mdtFieldDef, FieldDesc*>(token); + } + + mdToken GetToken(TypeHandle pMT) + { + WRAPPER_NO_CONTRACT; + return GetTokenWorker<mdtTypeDef, TypeHandle>(pMT); + } + mdToken GetToken(MethodDesc* pMD) + { + WRAPPER_NO_CONTRACT; + return GetTokenWorker<mdtMethodDef, MethodDesc*>(pMD); + } + mdToken GetToken(FieldDesc* pFieldDesc) + { + WRAPPER_NO_CONTRACT; + return GetTokenWorker<mdtFieldDef, FieldDesc*>(pFieldDesc); + } + +protected: + template<mdToken TokenType, typename HandleType> + HandleType LookupTokenWorker(mdToken token) + { + CONTRACTL + { + THROWS; + MODE_ANY; + GC_NOTRIGGER; + PRECONDITION(RidFromToken(token)-1 < m_nextAvailableRid); + PRECONDITION(RidFromToken(token) != 0); + PRECONDITION(TypeFromToken(token) == TokenType); + } + CONTRACTL_END; + + return ((HandleType*)m_qbEntries.Ptr())[RidFromToken(token)-1]; + } + + template<mdToken TokenType, typename HandleType> + mdToken GetTokenWorker(HandleType handle) + { + CONTRACTL + { + THROWS; + MODE_ANY; + GC_NOTRIGGER; + PRECONDITION(handle != NULL); + } + CONTRACTL_END; + + if (m_qbEntries.Size() <= (sizeof(handle) * m_nextAvailableRid)) + { + m_qbEntries.ReSizeThrows(2 * m_qbEntries.Size()); + } + + mdToken token = TokenFromRid(m_nextAvailableRid++, TokenType)+1; + + ((HandleType*)m_qbEntries.Ptr())[RidFromToken(token)-1] = handle; + + return token; + } + + unsigned int m_nextAvailableRid; + CQuickBytesSpecifySize<TOKEN_LOOKUP_MAP_SIZE> m_qbEntries; +}; + +struct ILStubEHClause +{ + enum Kind { kNone, kTypedCatch, kFinally }; + + DWORD kind; + DWORD dwTryBeginOffset; + DWORD cbTryLength; + DWORD dwHandlerBeginOffset; + DWORD cbHandlerLength; + DWORD dwTypeToken; +}; + + +class ILCodeLabel; +class ILCodeStream; +//--------------------------------------------------------------------------------------- +// +class ILStubLinker +{ + friend class ILCodeLabel; + friend class ILCodeStream; + +public: + + ILStubLinker(Module* pModule, const Signature &signature, SigTypeContext *pTypeContext, MethodDesc *pMD, + BOOL fTargetHasThis, BOOL fStubHasThis, BOOL fIsNDirectStub = FALSE); + ~ILStubLinker(); + + void GenerateCode(BYTE* pbBuffer, size_t cbBufferSize); + void ClearCode(); + +protected: + + void DeleteCodeLabels(); + void DeleteCodeStreams(); + + struct ILInstruction + { + UINT16 uInstruction; + INT16 iStackDelta; + UINT_PTR uArg; + }; + + static void PatchInstructionArgument(ILCodeLabel* pLabel, UINT_PTR uNewArg + DEBUG_ARG(UINT16 uExpectedInstruction)); + +#ifdef _DEBUG + bool IsInCodeStreamList(ILCodeStream* pcs); +#endif // _DEBUG + +public: + + void SetHasThis (bool fHasThis); + bool HasThis () { LIMITED_METHOD_CONTRACT; return m_fHasThis; } + + DWORD GetLocalSigSize(); + DWORD GetLocalSig(BYTE * pbLocalSig, DWORD cbBuffer); + + DWORD GetStubTargetMethodSigSize(); + DWORD GetStubTargetMethodSig(BYTE * pbLocalSig, DWORD cbBuffer); + + void SetStubTargetMethodSig(PCCOR_SIGNATURE pSig, DWORD cSig); + + void GetStubTargetReturnType(LocalDesc * pLoc); + void GetStubTargetReturnType(LocalDesc * pLoc, Module * pModule); + + void GetStubArgType(LocalDesc * pLoc); + void GetStubArgType(LocalDesc * pLoc, Module * pModule); + void GetStubReturnType(LocalDesc * pLoc); + void GetStubReturnType(LocalDesc * pLoc, Module * pModule); + CorCallingConvention GetStubTargetCallingConv(); + + + CorElementType GetStubTargetReturnElementType() { WRAPPER_NO_CONTRACT; return m_nativeFnSigBuilder.GetReturnElementType(); } + + static void GetManagedTypeHelper(LocalDesc* pLoc, Module* pModule, PCCOR_SIGNATURE pSig, SigTypeContext *pTypeContext, MethodDesc *pMD); + + BOOL StubHasVoidReturnType(); + + Stub *Link(LoaderHeap *pHeap, UINT *pcbSize /* = NULL*/, BOOL fMC); + + size_t Link(UINT* puMaxStack); + + + TokenLookupMap* GetTokenLookupMap() { LIMITED_METHOD_CONTRACT; return &m_tokenMap; } + + enum CodeStreamType + { + kSetup, + kMarshal, + kDispatch, + kReturnUnmarshal, + kUnmarshal, + kExceptionCleanup, + kCleanup, + kExceptionHandler, + }; + + ILCodeStream* NewCodeStream(CodeStreamType codeStreamType); + + MethodDesc *GetTargetMD() { LIMITED_METHOD_CONTRACT; return m_pMD; } + Signature GetStubSignature() { LIMITED_METHOD_CONTRACT; return m_stubSig; } + + void ClearCodeStreams(); + + void LogILStub(DWORD dwJitFlags, SString *pDumpILStubCode = NULL); +protected: + void LogILStubWorker(ILInstruction* pInstrBuffer, UINT numInstr, size_t* pcbCode, INT* piCurStack, SString *pDumpILStubCode = NULL); + void LogILInstruction(size_t curOffset, bool isLabeled, INT iCurStack, ILInstruction* pInstruction, SString *pDumpILStubCode = NULL); + +private: + ILCodeStream* m_pCodeStreamList; + + TokenLookupMap m_tokenMap; + LocalSigBuilder m_localSigBuilder; + FunctionSigBuilder m_nativeFnSigBuilder; + BYTE m_rgbBuffer[sizeof(COR_ILMETHOD_DECODER)]; + + Signature m_stubSig; // managed sig of stub + SigTypeContext* m_pTypeContext; // type context for m_stubSig + + SigPointer m_managedSigPtr; + void* m_pCode; + Module* m_pStubSigModule; + ILCodeLabel* m_pLabelList; + + bool FirstPassLink(ILInstruction* pInstrBuffer, UINT numInstr, size_t* pcbCode, INT* piCurStack, UINT* puMaxStack); + void SecondPassLink(ILInstruction* pInstrBuffer, UINT numInstr, size_t* pCurCodeOffset); + + BYTE* GenerateCodeWorker(BYTE* pbBuffer, ILInstruction* pInstrBuffer, UINT numInstr, size_t* pcbCode); + + static ILCodeStream* FindLastCodeStream(ILCodeStream* pList); + +protected: + // + // the public entrypoints for these methods are in ILCodeStream + // + ILCodeLabel* NewCodeLabel(); + int GetToken(MethodDesc* pMD); + int GetToken(MethodTable* pMT); + int GetToken(TypeHandle th); + int GetToken(FieldDesc* pFD); + DWORD NewLocal(CorElementType typ = ELEMENT_TYPE_I); + DWORD NewLocal(LocalDesc loc); + + DWORD SetStubTargetArgType(CorElementType typ, bool fConsumeStubArg = true); + DWORD SetStubTargetArgType(LocalDesc* pLoc = NULL, bool fConsumeStubArg = true); // passing pLoc = NULL means "use stub arg type" + void SetStubTargetReturnType(CorElementType typ); + void SetStubTargetReturnType(LocalDesc* pLoc); + void SetStubTargetCallingConv(CorCallingConvention uNativeCallingConv); + + void TransformArgForJIT(LocalDesc *pLoc); + + Module * GetStubSigModule(); + SigTypeContext *GetStubSigTypeContext(); + + BOOL m_StubHasVoidReturnType; + INT m_iTargetStackDelta; + DWORD m_cbCurrentCompressedSigLen; + DWORD m_nLocals; + + bool m_fHasThis; + + // We need this MethodDesc so we can reconstruct the generics + // SigTypeContext info, if needed. + MethodDesc * m_pMD; +}; // class ILStubLinker + + +//--------------------------------------------------------------------------------------- +// +class ILCodeLabel +{ + friend class ILStubLinker; + friend class ILCodeStream; + +public: + ILCodeLabel(); + ~ILCodeLabel(); + + size_t GetCodeOffset(); + +private: + void SetCodeOffset(size_t codeOffset); + + ILCodeLabel* m_pNext; + ILStubLinker* m_pOwningStubLinker; + ILCodeStream* m_pCodeStreamOfLabel; // this is the ILCodeStream that the index is relative to + size_t m_codeOffset; // this is the absolute resolved IL offset after linking + UINT m_idxLabeledInstruction; // this is the index within the instruction buffer of the owning ILCodeStream +}; + +class ILCodeStream +{ + friend class ILStubLinker; + +public: + enum ILInstrEnum + { +#define OPDEF(name,string,pop,push,oprType,opcType,l,s1,s2,ctrl) \ + name, + +#include "opcode.def" +#undef OPDEF + }; + +private: + static ILInstrEnum LowerOpcode(ILInstrEnum instr, ILStubLinker::ILInstruction* pInstr); + +#ifdef _DEBUG + static bool IsSupportedInstruction(ILInstrEnum instr); +#endif // _DEBUG + + static bool IsBranchInstruction(ILInstrEnum instr) + { + LIMITED_METHOD_CONTRACT; + return ((instr >= CEE_BR) && (instr <= CEE_BLT_UN)) || (instr == CEE_LEAVE); + } + + +public: + void EmitADD (); + void EmitADD_OVF (); + void EmitAND (); + void EmitARGLIST (); + void EmitBEQ (ILCodeLabel* pCodeLabel); + void EmitBGE (ILCodeLabel* pCodeLabel); + void EmitBGE_UN(ILCodeLabel* pCodeLabel); + void EmitBGT (ILCodeLabel* pCodeLabel); + void EmitBLE (ILCodeLabel* pCodeLabel); + void EmitBLE_UN (ILCodeLabel* pCodeLabel); + void EmitBLT (ILCodeLabel* pCodeLabel); + void EmitBR (ILCodeLabel* pCodeLabel); + void EmitBREAK (); + void EmitBRFALSE (ILCodeLabel* pCodeLabel); + void EmitBRTRUE (ILCodeLabel* pCodeLabel); + void EmitCALL (int token, int numInArgs, int numRetArgs); + void EmitCALLI (int token, int numInArgs, int numRetArgs); + void EmitCEQ (); + void EmitCGT (); + void EmitCGT_UN (); + void EmitCLT (); + void EmitCLT_UN (); + void EmitCONV_I (); + void EmitCONV_I1 (); + void EmitCONV_I2 (); + void EmitCONV_I4 (); + void EmitCONV_I8 (); + void EmitCONV_U (); + void EmitCONV_U1 (); + void EmitCONV_U2 (); + void EmitCONV_U4 (); + void EmitCONV_U8 (); + void EmitCONV_R4 (); + void EmitCONV_R8 (); + void EmitCONV_OVF_I4(); + void EmitCONV_T (CorElementType type); + void EmitCPBLK (); + void EmitCPOBJ (int token); + void EmitDUP (); + void EmitENDFINALLY (); + void EmitINITBLK (); + void EmitINITOBJ (int token); + void EmitJMP (int token); + void EmitLDARG (unsigned uArgIdx); + void EmitLDARGA (unsigned uArgIdx); + void EmitLDC (DWORD_PTR uConst); + void EmitLDC_R4 (UINT32 uConst); + void EmitLDC_R8 (UINT64 uConst); + void EmitLDELEM_REF (); + void EmitLDFLD (int token); + void EmitLDFLDA (int token); + void EmitLDFTN (int token); + void EmitLDIND_I (); + void EmitLDIND_I1 (); + void EmitLDIND_I2 (); + void EmitLDIND_I4 (); + void EmitLDIND_I8 (); + void EmitLDIND_R4 (); + void EmitLDIND_R8 (); + void EmitLDIND_REF (); + void EmitLDIND_T (LocalDesc* pType); + void EmitLDIND_U1 (); + void EmitLDIND_U2 (); + void EmitLDIND_U4 (); + void EmitLDLEN (); + void EmitLDLOC (DWORD dwLocalNum); + void EmitLDLOCA (DWORD dwLocalNum); + void EmitLDNULL (); + void EmitLDOBJ (int token); + void EmitLDSFLD (int token); + void EmitLDSFLDA (int token); + void EmitLDTOKEN (int token); + void EmitLEAVE (ILCodeLabel* pCodeLabel); + void EmitLOCALLOC (); + void EmitMUL (); + void EmitMUL_OVF (); + void EmitNEWOBJ (int token, int numInArgs); + void EmitNOP (LPCSTR pszNopComment); + void EmitPOP (); + void EmitRET (); + void EmitSHR_UN (); + void EmitSTARG (unsigned uArgIdx); + void EmitSTELEM_REF (); + void EmitSTIND_I (); + void EmitSTIND_I1 (); + void EmitSTIND_I2 (); + void EmitSTIND_I4 (); + void EmitSTIND_I8 (); + void EmitSTIND_R4 (); + void EmitSTIND_R8 (); + void EmitSTIND_REF (); + void EmitSTIND_T (LocalDesc* pType); + void EmitSTFLD (int token); + void EmitSTLOC (DWORD dwLocalNum); + void EmitSTOBJ (int token); + void EmitSTSFLD (int token); + void EmitSUB (); + void EmitTHROW (); + + // Overloads to simplify common usage patterns + void EmitNEWOBJ (BinderMethodID id, int numInArgs); + void EmitCALL (BinderMethodID id, int numInArgs, int numRetArgs); + + void EmitLabel(ILCodeLabel* pLabel); + void EmitLoadThis (); + void EmitLoadNullPtr(); + void EmitArgIteratorCreateAndLoad(); + + ILCodeLabel* NewCodeLabel(); + + void ClearCode(); + + // + // these functions just forward to the owning ILStubLinker + // + + int GetToken(MethodDesc* pMD); + int GetToken(MethodTable* pMT); + int GetToken(TypeHandle th); + int GetToken(FieldDesc* pFD); + + DWORD NewLocal(CorElementType typ = ELEMENT_TYPE_I); + DWORD NewLocal(LocalDesc loc); + DWORD SetStubTargetArgType(CorElementType typ, bool fConsumeStubArg = true); + DWORD SetStubTargetArgType(LocalDesc* pLoc = NULL, bool fConsumeStubArg = true); // passing pLoc = NULL means "use stub arg type" + void SetStubTargetReturnType(CorElementType typ); + void SetStubTargetReturnType(LocalDesc* pLoc); + + + // + // ctors/dtor + // + + ILCodeStream(ILStubLinker* pOwner, ILStubLinker::CodeStreamType codeStreamType) : + m_pNextStream(NULL), + m_pOwner(pOwner), + m_pqbILInstructions(NULL), + m_uCurInstrIdx(0), + m_codeStreamType(codeStreamType) + { + } + + ~ILCodeStream() + { + CONTRACTL + { + MODE_ANY; + NOTHROW; + GC_TRIGGERS; + } + CONTRACTL_END; + + if (NULL != m_pqbILInstructions) + { + delete m_pqbILInstructions; + m_pqbILInstructions = NULL; + } + } + + ILStubLinker::CodeStreamType GetStreamType() { return m_codeStreamType; } + + LPCSTR GetStreamDescription(ILStubLinker::CodeStreamType streamType); + +protected: + + void Emit(ILInstrEnum instr, INT16 iStackDelta, UINT_PTR uArg); + + enum Constants + { + INITIAL_NUM_IL_INSTRUCTIONS = 64, + INITIAL_IL_INSTRUCTION_BUFFER_SIZE = INITIAL_NUM_IL_INSTRUCTIONS * sizeof(ILStubLinker::ILInstruction), + }; + + typedef CQuickBytesSpecifySize<INITIAL_IL_INSTRUCTION_BUFFER_SIZE> ILCodeStreamBuffer; + + ILCodeStream* m_pNextStream; + ILStubLinker* m_pOwner; + ILCodeStreamBuffer* m_pqbILInstructions; + UINT m_uCurInstrIdx; + ILStubLinker::CodeStreamType m_codeStreamType; // Type of the ILCodeStream + +#ifndef _WIN64 + const static UINT32 SPECIAL_VALUE_NAN_64_ON_32 = 0xFFFFFFFF; +#endif // _WIN64 +}; + +#define TOKEN_ILSTUB_TARGET_SIG (TokenFromRid(0xFFFFFF, mdtSignature)) + +#endif // __STUBGEN_H__ |