InterpreterThunkEmitter.h 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. #ifdef ENABLE_NATIVE_CODEGEN
  7. class ThunkBlock
  8. {
  9. private:
  10. #if PDATA_ENABLED
  11. void* registeredPdataTable;
  12. #endif
  13. BYTE* start;
  14. BVFixed* freeList;
  15. DWORD thunkCount;
  16. public:
  17. ThunkBlock(BYTE* start, DWORD thunkCount) :
  18. start(start),
  19. thunkCount(thunkCount),
  20. freeList(NULL)
  21. #if PDATA_ENABLED
  22. , registeredPdataTable(NULL)
  23. #endif
  24. {}
  25. bool Contains(BYTE* address) const;
  26. void Release(BYTE* address);
  27. bool EnsureFreeList(ArenaAllocator* allocator);
  28. BYTE* AllocateFromFreeList();
  29. bool IsFreeListEmpty() const;
  30. BYTE* GetStart() const { return start; }
  31. #if PDATA_ENABLED
  32. void* GetPdata() const { return registeredPdataTable; }
  33. void SetPdata(void* pdata) { Assert(!this->registeredPdataTable); this->registeredPdataTable = pdata; }
  34. #endif
  35. private:
  36. BVIndex FromThunkAddress(BYTE* address);
  37. BYTE* ToThunkAddress(BVIndex index);
  38. };
  39. //
  40. // Emits interpreter thunks such that each javascript function in the interpreter gets a unique return address on call.
  41. // This unique address can be used to identify the function when a stackwalk is
  42. // performed using ETW.
  43. //
  44. // On every call, we generate 1 thunk (size ~ 1 page) that is implemented as a jump table.
  45. // The shape of the thunk is as follows:
  46. // 1. Function prolog
  47. // 2. Determine the thunk number from the JavascriptFunction object passed as first argument.
  48. // 3. Calculate the address of the correct calling point and jump to it.
  49. // 4. Make the call and jump to the epilog.
  50. // 5. Function epilog.
  51. //
  52. class InterpreterThunkEmitter
  53. {
  54. private:
  55. /* ------- instance methods --------*/
  56. InProcEmitBufferManager emitBufferManager;
  57. SListBase<ThunkBlock> thunkBlocks;
  58. SListBase<ThunkBlock> freeListedThunkBlocks;
  59. bool isAsmInterpreterThunk; // To emit address of InterpreterAsmThunk or InterpreterThunk
  60. BYTE* thunkBuffer;
  61. ArenaAllocator* allocator;
  62. Js::ScriptContext * scriptContext;
  63. DWORD thunkCount; // Count of thunks available in the current thunk block
  64. /* -------static constants ----------*/
  65. // Interpreter thunk buffer includes function prolog, setting up of arguments, jumping to the appropriate calling point.
  66. static const BYTE ThunkAddressOffset;
  67. static const BYTE FunctionInfoOffset;
  68. static const BYTE FunctionProxyOffset;
  69. static const BYTE DynamicThunkAddressOffset;
  70. static const BYTE CallBlockStartAddrOffset;
  71. static const BYTE ThunkSizeOffset;
  72. static const BYTE ErrorOffset;
  73. #if defined(_M_ARM)
  74. static const BYTE CallBlockStartAddressInstrOffset;
  75. static const BYTE CallThunkSizeInstrOffset;
  76. #endif
  77. #ifdef _M_X64
  78. #ifdef _WIN32
  79. #define INTERPRETER_THUNK_SIZE 96
  80. #else // Sys V AMD64
  81. #define INTERPRETER_THUNK_SIZE 72
  82. #endif
  83. #elif defined(_M_ARM)
  84. #define INTERPRETER_THUNK_SIZE 72
  85. #elif defined(_M_ARM64)
  86. #define INTERPRETER_THUNK_SIZE 60
  87. #else
  88. #define INTERPRETER_THUNK_SIZE 56
  89. #endif
  90. static const BYTE InterpreterThunk[INTERPRETER_THUNK_SIZE];
  91. // Call buffer includes a call to the inner interpreter thunk and eventual jump to the epilog
  92. static const BYTE JmpOffset;
  93. static const BYTE Call[];
  94. static const BYTE Epilog[];
  95. static const BYTE PageCount = 1;
  96. #if defined(_M_X64)
  97. static const BYTE PrologSize;
  98. static const BYTE StackAllocSize;
  99. #endif
  100. /* ------private helpers -----------*/
  101. bool NewThunkBlock();
  102. #ifdef ENABLE_OOP_NATIVE_CODEGEN
  103. bool NewOOPJITThunkBlock();
  104. #endif
  105. static void EncodeInterpreterThunk(
  106. __in_bcount(thunkSize) BYTE* thunkBuffer,
  107. __in const intptr_t thunkBufferStartAddress,
  108. __in const DWORD thunkSize,
  109. __in const intptr_t epilogStart,
  110. __in const DWORD epilogSize,
  111. __in const intptr_t interpreterThunk);
  112. #if defined(_M_ARM32_OR_ARM64)
  113. static DWORD EncodeMove(DWORD opCode, int reg, DWORD imm16);
  114. static void GeneratePdata(_In_ const BYTE* entryPoint, _In_ const DWORD functionSize, _Out_ RUNTIME_FUNCTION* function);
  115. #endif
  116. /*-------static helpers ---------*/
  117. inline static DWORD FillDebugBreak(_Out_writes_bytes_all_(count) BYTE* dest, _In_ DWORD count);
  118. inline static DWORD CopyWithAlignment(_Out_writes_bytes_all_(sizeInBytes) BYTE* dest, _In_ const DWORD sizeInBytes, _In_reads_bytes_(srcSize) const BYTE* src, _In_ const DWORD srcSize, _In_ const DWORD alignment);
  119. template<class T>
  120. inline static void Emit(__in_bcount(sizeof(T) + offset) BYTE* dest, __in const DWORD offset, __in const T value)
  121. {
  122. AssertMsg(*(T*) (dest + offset) == 0, "Overwriting an already existing opcode?");
  123. *(T*)(dest + offset) = value;
  124. };
  125. BYTE* AllocateFromFreeList(PVOID* ppDynamicInterpreterThunk);
  126. static const BYTE _HeaderSize;
  127. public:
  128. static const BYTE HeaderSize();
  129. static const BYTE ThunkSize;
  130. static const uint BlockSize= AutoSystemInfo::PageSize * PageCount;
  131. static void* ConvertToEntryPoint(PVOID dynamicInterpreterThunk);
  132. InterpreterThunkEmitter(Js::ScriptContext * context, ArenaAllocator* allocator, CustomHeap::InProcCodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk = false);
  133. BYTE* GetNextThunk(PVOID* ppDynamicInterpreterThunk);
  134. SListBase<ThunkBlock>* GetThunkBlocksList();
  135. void Close();
  136. void Release(BYTE* thunkAddress, bool addtoFreeList);
  137. // Returns true if the argument falls within the range managed by this buffer.
  138. #if DBG
  139. bool IsInHeap(void* address);
  140. #endif
  141. const InProcEmitBufferManager* GetEmitBufferManager() const
  142. {
  143. return &emitBufferManager;
  144. }
  145. static void FillBuffer(
  146. _In_ ThreadContextInfo * context,
  147. _In_ bool asmJsThunk,
  148. _In_ intptr_t finalAddr,
  149. _In_ size_t bufferSize,
  150. _Out_writes_bytes_all_(BlockSize) BYTE* buffer,
  151. #if PDATA_ENABLED
  152. _Out_ PRUNTIME_FUNCTION * pdataTableStart,
  153. _Out_ intptr_t * epilogEndAddr,
  154. #endif
  155. _Out_ DWORD * thunkCount
  156. );
  157. };
  158. #endif