InterpreterThunkEmitter.cpp 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  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. #include "Backend.h"
  6. #if ENABLE_NATIVE_CODEGEN
  7. namespace {
  8. // The definitions in this anonymous namespace must be constexpr to allow OACR to conclude that certain operations
  9. // in InterpreterThunkEmitter::EncodeInterpreterThunk are safe. Because constexpr requires that the declaration
  10. // and the definition appear at the same place (i.e., no forward declarations), this means that we either have
  11. // to move all 5 definitions of InterpreterThunk into the header file, or we have to make InterpreterThunkSize
  12. // public. The latter option seems the less objectionable, so that's what I've done here.
  13. #ifdef _M_X64
  14. #ifdef _WIN32
  15. constexpr BYTE FunctionInfoOffset = 23;
  16. constexpr BYTE FunctionProxyOffset = 27;
  17. constexpr BYTE DynamicThunkAddressOffset = 31;
  18. constexpr BYTE CallBlockStartAddrOffset = 41;
  19. constexpr BYTE ThunkSizeOffset = 55;
  20. constexpr BYTE ErrorOffset = 64;
  21. constexpr BYTE ThunkAddressOffset = 81;
  22. constexpr BYTE PrologSize = 80;
  23. constexpr BYTE StackAllocSize = 0x28;
  24. //
  25. // Home the arguments onto the stack and pass a pointer to the base of the stack location to the inner thunk
  26. //
  27. // Calling convention requires that caller should allocate at least 0x20 bytes and the stack be 16 byte aligned.
  28. // Hence, we allocate 0x28 bytes of stack space for the callee to use. The callee uses 8 bytes to push the first
  29. // argument and the rest 0x20 ensures alignment is correct.
  30. //
  31. constexpr BYTE InterpreterThunk[InterpreterThunkEmitter::InterpreterThunkSize] = {
  32. 0x48, 0x89, 0x54, 0x24, 0x10, // mov qword ptr [rsp+10h],rdx
  33. 0x48, 0x89, 0x4C, 0x24, 0x08, // mov qword ptr [rsp+8],rcx
  34. 0x4C, 0x89, 0x44, 0x24, 0x18, // mov qword ptr [rsp+18h],r8
  35. 0x4C, 0x89, 0x4C, 0x24, 0x20, // mov qword ptr [rsp+20h],r9
  36. 0x48, 0x8B, 0x41, 0x00, // mov rax, qword ptr [rcx+FunctionInfoOffset]
  37. 0x48, 0x8B, 0x48, 0x00, // mov rcx, qword ptr [rax+FunctionProxyOffset]
  38. 0x48, 0x8B, 0x51, 0x00, // mov rdx, qword ptr [rcx+DynamicThunkAddressOffset]
  39. // Range Check for Valid call target
  40. 0x48, 0x83, 0xE2, 0xF8, // and rdx, 0xFFFFFFFFFFFFFFF8h ;Force 8 byte alignment
  41. 0x48, 0x8b, 0xca, // mov rcx, rdx
  42. 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, CallBlockStartAddress
  43. 0x48, 0x2b, 0xc8, // sub rcx, rax
  44. 0x48, 0x81, 0xf9, 0x00, 0x00, 0x00, 0x00, // cmp rcx, ThunkSize
  45. 0x76, 0x09, // jbe $safe
  46. 0x48, 0xc7, 0xc1, 0x00, 0x00, 0x00, 0x00, // mov rcx, errorcode
  47. 0xcd, 0x29, // int 29h
  48. // $safe:
  49. 0x48, 0x8D, 0x4C, 0x24, 0x08, // lea rcx, [rsp+8] ;Load the address to stack
  50. 0x48, 0x83, 0xEC, StackAllocSize, // sub rsp,28h
  51. 0x48, 0xB8, 0x00, 0x00, 0x00 ,0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, <thunk>
  52. 0xFF, 0xE2, // jmp rdx
  53. 0xCC, 0xCC, 0xCC, 0xCC, 0xCC // int 3 ;for alignment to size of 8 we are adding this
  54. };
  55. constexpr BYTE Epilog[] = {
  56. 0x48, 0x83, 0xC4, StackAllocSize, // add rsp,28h
  57. 0xC3 // ret
  58. };
  59. #else // Sys V AMD64
  60. constexpr BYTE FunctionInfoOffset = 7;
  61. constexpr BYTE FunctionProxyOffset = 11;
  62. constexpr BYTE DynamicThunkAddressOffset = 15;
  63. constexpr BYTE CallBlockStartAddrOffset = 25;
  64. constexpr BYTE ThunkSizeOffset = 39;
  65. constexpr BYTE ErrorOffset = 48;
  66. constexpr BYTE ThunkAddressOffset = 61;
  67. constexpr BYTE PrologSize = 60;
  68. constexpr BYTE StackAllocSize = 0x0;
  69. constexpr BYTE InterpreterThunk[InterpreterThunkEmitter::InterpreterThunkSize] = {
  70. 0x55, // push rbp // Prolog - setup the stack frame
  71. 0x48, 0x89, 0xe5, // mov rbp, rsp
  72. 0x48, 0x8b, 0x47, 0x00, // mov rax, qword ptr [rdi + FunctionInfoOffset]
  73. 0x48, 0x8B, 0x48, 0x00, // mov rcx, qword ptr [rax+FunctionProxyOffset]
  74. 0x48, 0x8B, 0x51, 0x00, // mov rdx, qword ptr [rcx+DynamicThunkAddressOffset]
  75. // Range Check for Valid call target
  76. 0x48, 0x83, 0xE2, 0xF8, // and rdx, 0xfffffffffffffff8 // Force 8 byte alignment
  77. 0x48, 0x89, 0xd1, // mov rcx, rdx
  78. 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, CallBlockStartAddress
  79. 0x48, 0x29, 0xc1, // sub rcx, rax
  80. 0x48, 0x81, 0xf9, 0x00, 0x00, 0x00, 0x00, // cmp rcx, ThunkSize
  81. 0x76, 0x09, // jbe safe
  82. 0x48, 0xc7, 0xc1, 0x00, 0x00, 0x00, 0x00, // mov rcx, errorcode
  83. 0xcd, 0x29, // int 29h <-- xplat TODO: just to exit
  84. // safe:
  85. 0x48, 0x8d, 0x7c, 0x24, 0x10, // lea rdi, [rsp+0x10]
  86. 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rax, <thunk> // stack already 16-byte aligned
  87. 0xff, 0xe2, // jmp rdx
  88. 0xcc // int 3 // for alignment to size of 8
  89. };
  90. constexpr BYTE Epilog[] = {
  91. 0x5d, // pop rbp
  92. 0xc3 // ret
  93. };
  94. #endif
  95. #elif defined(_M_ARM)
  96. constexpr BYTE ThunkAddressOffset = 8;
  97. constexpr BYTE FunctionInfoOffset = 18;
  98. constexpr BYTE FunctionProxyOffset = 22;
  99. constexpr BYTE DynamicThunkAddressOffset = 26;
  100. constexpr BYTE CallBlockStartAddressInstrOffset = 42;
  101. constexpr BYTE CallThunkSizeInstrOffset = 54;
  102. constexpr BYTE ErrorOffset = 64;
  103. constexpr BYTE InterpreterThunk[InterpreterThunkEmitter::InterpreterThunkSize] = {
  104. 0x0F, 0xB4, // push {r0-r3}
  105. 0x2D, 0xE9, 0x00, 0x48, // push {r11,lr}
  106. 0xEB, 0x46, // mov r11,sp
  107. 0x00, 0x00, 0x00, 0x00, // movw r1,ThunkAddress
  108. 0x00, 0x00, 0x00, 0x00, // movt r1,ThunkAddress
  109. 0xD0, 0xF8, 0x00, 0x20, // ldr.w r2,[r0,#0x00]
  110. 0xD2, 0xF8, 0x00, 0x00, // ldr.w r0,[r2,#0x00]
  111. 0xD0, 0xF8, 0x00, 0x30, // ldr.w r3,[r0,#0x00]
  112. 0x4F, 0xF6, 0xF9, 0x70, // mov r0,#0xFFF9
  113. 0xCF, 0xF6, 0xFF, 0x70, // movt r0,#0xFFFF
  114. 0x03, 0xEA, 0x00, 0x03, // and r3,r3,r0
  115. 0x18, 0x46, // mov r0, r3
  116. 0x00, 0x00, 0x00, 0x00, // movw r12, CallBlockStartAddress
  117. 0x00, 0x00, 0x00, 0x00, // movt r12, CallBlockStartAddress
  118. 0xA0, 0xEB, 0x0C, 0x00, // sub r0, r12
  119. 0x00, 0x00, 0x00, 0x00, // mov r12, ThunkSize
  120. 0x60, 0x45, // cmp r0, r12
  121. 0x02, 0xD9, // bls $safe
  122. 0x4F, 0xF0, 0x00, 0x00, // mov r0, errorcode
  123. 0xFB, 0xDE, // Equivalent to int 0x29
  124. //$safe:
  125. 0x02, 0xA8, // add r0,sp,#8
  126. 0x18, 0x47 // bx r3
  127. };
  128. constexpr BYTE JmpOffset = 2;
  129. constexpr BYTE Call[] = {
  130. 0x88, 0x47, // blx r1
  131. 0x00, 0x00, 0x00, 0x00, // b.w epilog
  132. 0xFE, 0xDE, // int 3 ;Required for alignment
  133. };
  134. constexpr BYTE Epilog[] = {
  135. 0x5D, 0xF8, 0x04, 0xBB, // pop {r11}
  136. 0x5D, 0xF8, 0x14, 0xFB // ldr pc,[sp],#0x14
  137. };
  138. #elif defined(_M_ARM64)
  139. constexpr BYTE FunctionInfoOffset = 24;
  140. constexpr BYTE FunctionProxyOffset = 28;
  141. constexpr BYTE DynamicThunkAddressOffset = 32;
  142. constexpr BYTE ThunkAddressOffset = 36;
  143. //TODO: saravind :Implement Range Check for ARM64
  144. constexpr BYTE InterpreterThunk[InterpreterThunkEmitter::InterpreterThunkSize] = {
  145. 0xFD, 0x7B, 0xBB, 0xA9, //stp fp, lr, [sp, #-80]! ;Prologue
  146. 0xFD, 0x03, 0x00, 0x91, //mov fp, sp ;update frame pointer to the stack pointer
  147. 0xE0, 0x07, 0x01, 0xA9, //stp x0, x1, [sp, #16] ;Prologue again; save all registers
  148. 0xE2, 0x0F, 0x02, 0xA9, //stp x2, x3, [sp, #32]
  149. 0xE4, 0x17, 0x03, 0xA9, //stp x4, x5, [sp, #48]
  150. 0xE6, 0x1F, 0x04, 0xA9, //stp x6, x7, [sp, #64]
  151. 0x02, 0x00, 0x40, 0xF9, //ldr x2, [x0, #0x00] ;offset will be replaced with Offset of FunctionInfo
  152. 0x40, 0x00, 0x40, 0xF9, //ldr x0, [x2, #0x00] ;offset will be replaced with Offset of FunctionProxy
  153. 0x03, 0x00, 0x40, 0xF9, //ldr x3, [x0, #0x00] ;offset will be replaced with offset of DynamicInterpreterThunk
  154. //Following 4 MOV Instrs are to move the 64-bit address of the InterpreterThunk address into register x1.
  155. 0x00, 0x00, 0x00, 0x00, //movz x1, #0x00 ;This is overwritten with the actual thunk address(16 - 0 bits) move
  156. 0x00, 0x00, 0x00, 0x00, //movk x1, #0x00, lsl #16 ;This is overwritten with the actual thunk address(32 - 16 bits) move
  157. 0x00, 0x00, 0x00, 0x00, //movk x1, #0x00, lsl #32 ;This is overwritten with the actual thunk address(48 - 32 bits) move
  158. 0x00, 0x00, 0x00, 0x00, //movk x1, #0x00, lsl #48 ;This is overwritten with the actual thunk address(64 - 48 bits) move
  159. 0xE0, 0x43, 0x00, 0x91, //add x0, sp, #16
  160. 0x60, 0x00, 0x1F, 0xD6, //br x3
  161. 0xCC, 0xCC, 0xCC, 0xCC //int 3 for 8byte alignment
  162. };
  163. constexpr BYTE JmpOffset = 4;
  164. constexpr BYTE Call[] = {
  165. 0x20, 0x00, 0x3f, 0xd6, // blr x1
  166. 0x00, 0x00, 0x00, 0x00 // b epilog
  167. };
  168. constexpr BYTE Epilog[] = {
  169. 0xfd, 0x7b, 0xc5, 0xa8, // ldp fp, lr, [sp], #80
  170. 0xc0, 0x03, 0x5f, 0xd6 // ret
  171. };
  172. #else // x86
  173. constexpr BYTE FunctionInfoOffset = 8;
  174. constexpr BYTE FunctionProxyOffset = 11;
  175. constexpr BYTE DynamicThunkAddressOffset = 14;
  176. constexpr BYTE CallBlockStartAddrOffset = 21;
  177. constexpr BYTE ThunkSizeOffset = 26;
  178. constexpr BYTE ErrorOffset = 33;
  179. constexpr BYTE ThunkAddressOffset = 44;
  180. constexpr BYTE InterpreterThunk[InterpreterThunkEmitter::InterpreterThunkSize] = {
  181. 0x55, // push ebp ;Prolog - setup the stack frame
  182. 0x8B, 0xEC, // mov ebp,esp
  183. 0x8B, 0x45, 0x08, // mov eax, dword ptr [ebp+8]
  184. 0x8B, 0x40, 0x00, // mov eax, dword ptr [eax+FunctionInfoOffset]
  185. 0x8B, 0x40, 0x00, // mov eax, dword ptr [eax+FunctionProxyOffset]
  186. 0x8B, 0x48, 0x00, // mov ecx, dword ptr [eax+DynamicThunkAddressOffset]
  187. // Range Check for Valid call target
  188. 0x83, 0xE1, 0xF8, // and ecx, 0FFFFFFF8h
  189. 0x8b, 0xc1, // mov eax, ecx
  190. 0x2d, 0x00, 0x00, 0x00, 0x00, // sub eax, CallBlockStartAddress
  191. 0x3d, 0x00, 0x00, 0x00, 0x00, // cmp eax, ThunkSize
  192. 0x76, 0x07, // jbe SHORT $safe
  193. 0xb9, 0x00, 0x00, 0x00, 0x00, // mov ecx, errorcode
  194. 0xCD, 0x29, // int 29h
  195. //$safe
  196. 0x8D, 0x45, 0x08, // lea eax, ebp+8
  197. 0x50, // push eax
  198. 0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, <thunk>
  199. 0xFF, 0xE1, // jmp ecx
  200. 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC // int 3 for 8byte alignment
  201. };
  202. constexpr BYTE Epilog[] = {
  203. 0x5D, // pop ebp
  204. 0xC3 // ret
  205. };
  206. #endif
  207. #if defined(_M_X64) || defined(_M_IX86)
  208. constexpr BYTE JmpOffset = 3;
  209. constexpr BYTE Call[] = {
  210. 0xFF, 0xD0, // call rax
  211. 0xE9, 0x00, 0x00, 0x00, 0x00, // jmp [offset]
  212. 0xCC, // int 3 ;for alignment to size of 8 we are adding this
  213. };
  214. #endif
  215. constexpr BYTE HeaderSize = sizeof(InterpreterThunk);
  216. } // anonymous namespace
  217. const BYTE InterpreterThunkEmitter::ThunkSize = sizeof(Call);
  218. InterpreterThunkEmitter::InterpreterThunkEmitter(Js::ScriptContext* context, ArenaAllocator* allocator, CustomHeap::InProcCodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk) :
  219. emitBufferManager(allocator, codePageAllocators, /*scriptContext*/ nullptr, nullptr, _u("Interpreter thunk buffer"), GetCurrentProcess()),
  220. scriptContext(context),
  221. allocator(allocator),
  222. thunkCount(0),
  223. thunkBuffer(nullptr),
  224. isAsmInterpreterThunk(isAsmInterpreterThunk)
  225. {
  226. }
  227. SListBase<ThunkBlock>*
  228. InterpreterThunkEmitter::GetThunkBlocksList()
  229. {
  230. return &thunkBlocks;
  231. }
  232. //
  233. // Returns the next thunk. Batch allocated PageCount pages of thunks and issue them one at a time
  234. //
  235. BYTE* InterpreterThunkEmitter::GetNextThunk(PVOID* ppDynamicInterpreterThunk)
  236. {
  237. Assert(ppDynamicInterpreterThunk);
  238. Assert(*ppDynamicInterpreterThunk == nullptr);
  239. if(thunkCount == 0)
  240. {
  241. if(!this->freeListedThunkBlocks.Empty())
  242. {
  243. return AllocateFromFreeList(ppDynamicInterpreterThunk);
  244. }
  245. if (!NewThunkBlock())
  246. {
  247. #ifdef ASMJS_PLAT
  248. return this->isAsmInterpreterThunk ? (BYTE*)&Js::InterpreterStackFrame::StaticInterpreterAsmThunk : (BYTE*)&Js::InterpreterStackFrame::StaticInterpreterThunk;
  249. #else
  250. Assert(!this->isAsmInterpreterThunk);
  251. return (BYTE*)&Js::InterpreterStackFrame::StaticInterpreterThunk;
  252. #endif
  253. }
  254. }
  255. Assert(this->thunkBuffer != nullptr);
  256. BYTE* thunk = this->thunkBuffer;
  257. #if _M_ARM
  258. thunk = (BYTE*)((DWORD)thunk | 0x01);
  259. #endif
  260. *ppDynamicInterpreterThunk = thunk + HeaderSize + ((--thunkCount) * ThunkSize);
  261. #if _M_ARM
  262. AssertMsg(((uintptr_t)(*ppDynamicInterpreterThunk) & 0x6) == 0, "Not 8 byte aligned?");
  263. #else
  264. AssertMsg(((uintptr_t)(*ppDynamicInterpreterThunk) & 0x7) == 0, "Not 8 byte aligned?");
  265. #endif
  266. return thunk;
  267. }
  268. //
  269. // Interpreter thunks have an entrypoint at the beginning of the page boundary. Each function has a unique thunk return address
  270. // and this function can convert to the unique thunk return address to the beginning of the page which corresponds with the entrypoint
  271. //
  272. void* InterpreterThunkEmitter::ConvertToEntryPoint(PVOID dynamicInterpreterThunk)
  273. {
  274. Assert(dynamicInterpreterThunk != nullptr);
  275. void* entryPoint = (void*)((size_t)dynamicInterpreterThunk & (~((size_t)(BlockSize) - 1)));
  276. #if _M_ARM
  277. entryPoint = (BYTE*)((DWORD)entryPoint | 0x01);
  278. #endif
  279. return entryPoint;
  280. }
  281. bool InterpreterThunkEmitter::NewThunkBlock()
  282. {
  283. if (this->scriptContext->GetConfig()->IsNoDynamicThunks())
  284. {
  285. return false;
  286. }
  287. #ifdef ENABLE_OOP_NATIVE_CODEGEN
  288. if (CONFIG_FLAG(ForceStaticInterpreterThunk))
  289. {
  290. return false;
  291. }
  292. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  293. {
  294. return NewOOPJITThunkBlock();
  295. }
  296. #endif
  297. Assert(this->thunkCount == 0);
  298. BYTE* buffer;
  299. EmitBufferAllocation<VirtualAllocWrapper, PreReservedVirtualAllocWrapper> * allocation = emitBufferManager.AllocateBuffer(BlockSize, &buffer);
  300. if (allocation == nullptr)
  301. {
  302. Js::Throw::OutOfMemory();
  303. }
  304. if (!emitBufferManager.ProtectBufferWithExecuteReadWriteForInterpreter(allocation))
  305. {
  306. Js::Throw::OutOfMemory();
  307. }
  308. #if PDATA_ENABLED
  309. PRUNTIME_FUNCTION pdataStart = nullptr;
  310. intptr_t epilogEnd = 0;
  311. #endif
  312. DWORD count = this->thunkCount;
  313. FillBuffer(
  314. this->scriptContext->GetThreadContext(),
  315. this->isAsmInterpreterThunk,
  316. (intptr_t)buffer,
  317. BlockSize,
  318. buffer,
  319. #if PDATA_ENABLED
  320. &pdataStart,
  321. &epilogEnd,
  322. #endif
  323. &count
  324. );
  325. if (!emitBufferManager.CommitBufferForInterpreter(allocation, buffer, BlockSize))
  326. {
  327. Js::Throw::OutOfMemory();
  328. }
  329. // Call to set VALID flag for CFG check
  330. BYTE* callTarget = buffer;
  331. #ifdef _M_ARM
  332. // We want to allow the actual callable value, so thumb-tag the address
  333. callTarget = (BYTE*)((uintptr_t)buffer | 0x1);
  334. #endif
  335. ThreadContext::GetContextForCurrentThread()->SetValidCallTargetForCFG(callTarget);
  336. // Update object state only at the end when everything has succeeded - and no exceptions can be thrown.
  337. auto block = this->thunkBlocks.PrependNode(allocator, buffer, count);
  338. #if PDATA_ENABLED
  339. void* pdataTable;
  340. PDataManager::RegisterPdata((PRUNTIME_FUNCTION)pdataStart, (ULONG_PTR)buffer, (ULONG_PTR)epilogEnd, &pdataTable);
  341. block->SetPdata(pdataTable);
  342. #else
  343. Unused(block);
  344. #endif
  345. this->thunkBuffer = buffer;
  346. this->thunkCount = count;
  347. return true;
  348. }
  349. #ifdef ENABLE_OOP_NATIVE_CODEGEN
  350. bool InterpreterThunkEmitter::NewOOPJITThunkBlock()
  351. {
  352. PSCRIPTCONTEXT_HANDLE remoteScriptContext = this->scriptContext->GetRemoteScriptAddr();
  353. if (!JITManager::GetJITManager()->IsConnected())
  354. {
  355. return false;
  356. }
  357. InterpreterThunkInputIDL thunkInput;
  358. thunkInput.asmJsThunk = this->isAsmInterpreterThunk;
  359. InterpreterThunkOutputIDL thunkOutput;
  360. HRESULT hr = JITManager::GetJITManager()->NewInterpreterThunkBlock(remoteScriptContext, &thunkInput, &thunkOutput);
  361. if (!JITManager::HandleServerCallResult(hr, RemoteCallType::ThunkCreation))
  362. {
  363. return false;
  364. }
  365. BYTE* buffer = (BYTE*)thunkOutput.mappedBaseAddr;
  366. if (!CONFIG_FLAG(OOPCFGRegistration))
  367. {
  368. BYTE* callTarget = buffer;
  369. #ifdef _M_ARM
  370. // Need to register the thumb-tagged call target for CFG
  371. callTarget = (BYTE*)((uintptr_t)callTarget | 0x1);
  372. #endif
  373. this->scriptContext->GetThreadContext()->SetValidCallTargetForCFG(callTarget);
  374. }
  375. // Update object state only at the end when everything has succeeded - and no exceptions can be thrown.
  376. auto block = this->thunkBlocks.PrependNode(allocator, buffer, thunkOutput.thunkCount);
  377. #if PDATA_ENABLED
  378. void* pdataTable;
  379. PDataManager::RegisterPdata((PRUNTIME_FUNCTION)thunkOutput.pdataTableStart, (ULONG_PTR)thunkOutput.mappedBaseAddr, (ULONG_PTR)thunkOutput.epilogEndAddr, &pdataTable);
  380. block->SetPdata(pdataTable);
  381. #else
  382. Unused(block);
  383. #endif
  384. this->thunkBuffer = (BYTE*)thunkOutput.mappedBaseAddr;
  385. this->thunkCount = thunkOutput.thunkCount;
  386. return true;
  387. }
  388. #endif
  389. /* static */
  390. void InterpreterThunkEmitter::FillBuffer(
  391. _In_ ThreadContextInfo * threadContext,
  392. _In_ bool asmJsThunk,
  393. _In_ intptr_t finalAddr,
  394. _In_ size_t bufferSize,
  395. _Out_writes_bytes_all_(BlockSize) BYTE* buffer,
  396. #if PDATA_ENABLED
  397. _Out_ PRUNTIME_FUNCTION * pdataTableStart,
  398. _Out_ intptr_t * epilogEndAddr,
  399. #endif
  400. _Out_ DWORD * thunkCount
  401. )
  402. {
  403. #ifdef _M_X64
  404. PrologEncoder prologEncoder;
  405. prologEncoder.EncodeSmallProlog(PrologSize, StackAllocSize);
  406. DWORD pdataSize = prologEncoder.SizeOfPData();
  407. #elif defined(_M_ARM32_OR_ARM64)
  408. DWORD pdataSize = sizeof(RUNTIME_FUNCTION);
  409. #else
  410. DWORD pdataSize = 0;
  411. #endif
  412. DWORD bytesRemaining = BlockSize;
  413. DWORD bytesWritten = 0;
  414. DWORD thunks = 0;
  415. DWORD epilogSize = sizeof(Epilog);
  416. const BYTE *epilog = Epilog;
  417. const BYTE *header = InterpreterThunk;
  418. intptr_t interpreterThunk;
  419. // the static interpreter thunk invoked by the dynamic emitted thunk
  420. #ifdef ASMJS_PLAT
  421. if (asmJsThunk)
  422. {
  423. interpreterThunk = ShiftAddr(threadContext, &Js::InterpreterStackFrame::InterpreterAsmThunk);
  424. }
  425. else
  426. #endif
  427. {
  428. interpreterThunk = ShiftAddr(threadContext, &Js::InterpreterStackFrame::InterpreterThunk);
  429. }
  430. BYTE * currentBuffer = buffer;
  431. // Ensure there is space for PDATA at the end
  432. BYTE* pdataStart = currentBuffer + (BlockSize - Math::Align(pdataSize, EMIT_BUFFER_ALIGNMENT));
  433. BYTE* epilogStart = pdataStart - Math::Align(epilogSize, EMIT_BUFFER_ALIGNMENT);
  434. // Ensure there is space for PDATA at the end
  435. intptr_t finalPdataStart = finalAddr + (BlockSize - Math::Align(pdataSize, EMIT_BUFFER_ALIGNMENT));
  436. intptr_t finalEpilogStart = finalPdataStart - Math::Align(epilogSize, EMIT_BUFFER_ALIGNMENT);
  437. // Copy the thunk buffer and modify it.
  438. js_memcpy_s(currentBuffer, bytesRemaining, header, HeaderSize);
  439. EncodeInterpreterThunk(currentBuffer, finalAddr, finalEpilogStart, epilogSize, interpreterThunk);
  440. currentBuffer += HeaderSize;
  441. bytesRemaining -= HeaderSize;
  442. // Copy call buffer
  443. DWORD callSize = sizeof(Call);
  444. while (currentBuffer < epilogStart - callSize)
  445. {
  446. js_memcpy_s(currentBuffer, bytesRemaining, Call, callSize);
  447. #if _M_ARM
  448. int offset = (epilogStart - (currentBuffer + JmpOffset));
  449. Assert(offset >= 0);
  450. DWORD encodedOffset = EncoderMD::BranchOffset_T2_24(offset);
  451. DWORD encodedBranch = /*opcode=*/ 0x9000F000 | encodedOffset;
  452. Emit(currentBuffer, JmpOffset, encodedBranch);
  453. #elif _M_ARM64
  454. int64 offset = (epilogStart - (currentBuffer + JmpOffset));
  455. Assert(offset >= 0);
  456. DWORD encodedOffset = EncoderMD::BranchOffset_26(offset);
  457. DWORD encodedBranch = /*opcode=*/ 0x14000000 | encodedOffset;
  458. Emit(currentBuffer, JmpOffset, encodedBranch);
  459. #else
  460. // jump requires an offset from the end of the jump instruction.
  461. int offset = (int)(epilogStart - (currentBuffer + JmpOffset + sizeof(int)));
  462. Assert(offset >= 0);
  463. Emit(currentBuffer, JmpOffset, offset);
  464. #endif
  465. currentBuffer += callSize;
  466. bytesRemaining -= callSize;
  467. thunks++;
  468. }
  469. // Fill any gap till start of epilog
  470. bytesWritten = FillDebugBreak(currentBuffer, (DWORD)(epilogStart - currentBuffer));
  471. bytesRemaining -= bytesWritten;
  472. currentBuffer += bytesWritten;
  473. // Copy epilog
  474. bytesWritten = CopyWithAlignment(currentBuffer, bytesRemaining, epilog, epilogSize, EMIT_BUFFER_ALIGNMENT);
  475. currentBuffer += bytesWritten;
  476. bytesRemaining -= bytesWritten;
  477. // Generate and register PDATA
  478. #if PDATA_ENABLED
  479. BYTE* epilogEnd = epilogStart + epilogSize;
  480. DWORD functionSize = (DWORD)(epilogEnd - buffer);
  481. Assert(pdataStart == currentBuffer);
  482. #ifdef _M_X64
  483. Assert(bytesRemaining >= pdataSize);
  484. BYTE* pdata = prologEncoder.Finalize(buffer, functionSize, pdataStart);
  485. bytesWritten = CopyWithAlignment(pdataStart, bytesRemaining, pdata, pdataSize, EMIT_BUFFER_ALIGNMENT);
  486. #elif defined(_M_ARM32_OR_ARM64)
  487. RUNTIME_FUNCTION pdata;
  488. GeneratePdata(buffer, functionSize, &pdata);
  489. bytesWritten = CopyWithAlignment(pdataStart, bytesRemaining, (const BYTE*)&pdata, pdataSize, EMIT_BUFFER_ALIGNMENT);
  490. #endif
  491. *pdataTableStart = (PRUNTIME_FUNCTION)finalPdataStart;
  492. *epilogEndAddr = finalEpilogStart;
  493. #endif
  494. *thunkCount = thunks;
  495. }
  496. #if _M_ARM
  497. void InterpreterThunkEmitter::EncodeInterpreterThunk(
  498. __in_bcount(InterpreterThunkSize) BYTE* thunkBuffer,
  499. __in const intptr_t thunkBufferStartAddress,
  500. __in const intptr_t epilogStart,
  501. __in const DWORD epilogSize,
  502. __in const intptr_t interpreterThunk)
  503. {
  504. // Encode MOVW
  505. DWORD lowerThunkBits = (uint32)interpreterThunk & 0x0000FFFF;
  506. DWORD movW = EncodeMove(/*Opcode*/ 0x0000F240, /*register*/1, lowerThunkBits);
  507. Emit(thunkBuffer,ThunkAddressOffset, movW);
  508. // Encode MOVT
  509. DWORD higherThunkBits = ((uint32)interpreterThunk & 0xFFFF0000) >> 16;
  510. DWORD movT = EncodeMove(/*Opcode*/ 0x0000F2C0, /*register*/1, higherThunkBits);
  511. Emit(thunkBuffer, ThunkAddressOffset + sizeof(movW), movT);
  512. // Encode LDR - Load of function Body
  513. thunkBuffer[FunctionInfoOffset] = Js::JavascriptFunction::GetOffsetOfFunctionInfo();
  514. thunkBuffer[FunctionProxyOffset] = Js::FunctionInfo::GetOffsetOfFunctionProxy();
  515. // Encode LDR - Load of interpreter thunk number
  516. thunkBuffer[DynamicThunkAddressOffset] = Js::FunctionBody::GetOffsetOfDynamicInterpreterThunk();
  517. // Encode MOVW R12, CallBlockStartAddress
  518. uintptr_t callBlockStartAddress = (uintptr_t)thunkBufferStartAddress + HeaderSize;
  519. uint totalThunkSize = (uint)(epilogStart - callBlockStartAddress);
  520. DWORD lowerCallBlockStartAddress = callBlockStartAddress & 0x0000FFFF;
  521. DWORD movWblockStart = EncodeMove(/*Opcode*/ 0x0000F240, /*register*/12, lowerCallBlockStartAddress);
  522. Emit(thunkBuffer,CallBlockStartAddressInstrOffset, movWblockStart);
  523. // Encode MOVT R12, CallBlockStartAddress
  524. DWORD higherCallBlockStartAddress = (callBlockStartAddress & 0xFFFF0000) >> 16;
  525. DWORD movTblockStart = EncodeMove(/*Opcode*/ 0x0000F2C0, /*register*/12, higherCallBlockStartAddress);
  526. Emit(thunkBuffer, CallBlockStartAddressInstrOffset + sizeof(movWblockStart), movTblockStart);
  527. //Encode MOV R12, CallBlockSize
  528. DWORD movBlockSize = EncodeMove(/*Opcode*/ 0x0000F240, /*register*/12, (DWORD)totalThunkSize);
  529. Emit(thunkBuffer, CallThunkSizeInstrOffset, movBlockSize);
  530. Emit(thunkBuffer, ErrorOffset, (BYTE) FAST_FAIL_INVALID_ARG);
  531. }
  532. DWORD InterpreterThunkEmitter::EncodeMove(DWORD opCode, int reg, DWORD imm16)
  533. {
  534. DWORD encodedMove = reg << 24;
  535. #if _M_ARM
  536. DWORD encodedImm = 0;
  537. EncoderMD::EncodeImmediate16(imm16, &encodedImm);
  538. encodedMove |= encodedImm;
  539. #elif _M_ARM64
  540. // ToDo (SaAgarwa) - From Aaron change. Validate for ARM64
  541. encodedMove |= (imm16 & 0xFFFF) << 5;
  542. #endif
  543. AssertMsg((encodedMove & opCode) == 0, "Any bits getting overwritten?");
  544. encodedMove |= opCode;
  545. return encodedMove;
  546. }
  547. void InterpreterThunkEmitter::GeneratePdata(_In_ const BYTE* entryPoint, _In_ const DWORD functionSize, _Out_ RUNTIME_FUNCTION* function)
  548. {
  549. function->BeginAddress = 0x1; // Since our base address is the start of the function - this is offset from the base address
  550. function->Flag = 1; // Packed unwind data is used
  551. function->FunctionLength = functionSize / 2;
  552. function->Ret = 0; // Return via Pop
  553. function->H = 1; // Homes parameters
  554. function->Reg = 7; // No saved registers - R11 is the frame pointer - not considered here
  555. function->R = 1; // No registers are being saved.
  556. function->L = 1; // Save/restore LR register
  557. function->C = 1; // Frame pointer chain in R11 established
  558. function->StackAdjust = 0; // Stack allocation for the function
  559. }
  560. #elif _M_ARM64
  561. void InterpreterThunkEmitter::EncodeInterpreterThunk(
  562. __in_bcount(InterpreterThunkSize) BYTE* thunkBuffer,
  563. __in const intptr_t thunkBufferStartAddress,
  564. __in const intptr_t epilogStart,
  565. __in const DWORD epilogSize,
  566. __in const intptr_t interpreterThunk)
  567. {
  568. int addrOffset = ThunkAddressOffset;
  569. // Following 4 MOV Instrs are to move the 64-bit address of the InterpreterThunk address into register x1.
  570. // Encode MOVZ (movz x1, #<interpreterThunk 16-0 bits>)
  571. DWORD lowerThunkBits = (uint64)interpreterThunk & 0x0000FFFF;
  572. DWORD movZ = EncodeMove(/*Opcode*/ 0xD2800000, /*register x1*/1, lowerThunkBits); // no shift; hw = 00
  573. Emit(thunkBuffer,addrOffset, movZ);
  574. static_assert(sizeof(movZ) == 4, "movZ has to be 32-bit encoded");
  575. addrOffset+= sizeof(movZ);
  576. // Encode MOVK (movk x1, #<interpreterThunk 32-16 bits>, lsl #16)
  577. DWORD higherThunkBits = ((uint64)interpreterThunk & 0xFFFF0000) >> 16;
  578. DWORD movK = EncodeMove(/*Opcode*/ 0xF2A00000, /*register x1*/1, higherThunkBits); // left shift 16 bits; hw = 01
  579. Emit(thunkBuffer, addrOffset, movK);
  580. static_assert(sizeof(movK) == 4, "movK has to be 32-bit encoded");
  581. addrOffset+= sizeof(movK);
  582. // Encode MOVK (movk x1, #<interpreterThunk 48-32 bits>, lsl #16)
  583. higherThunkBits = ((uint64)interpreterThunk & 0xFFFF00000000) >> 32;
  584. movK = EncodeMove(/*Opcode*/ 0xF2C00000, /*register x1*/1, higherThunkBits); // left shift 32 bits; hw = 02
  585. Emit(thunkBuffer, addrOffset, movK);
  586. addrOffset += sizeof(movK);
  587. // Encode MOVK (movk x1, #<interpreterThunk 64-48 bits>, lsl #16)
  588. higherThunkBits = ((uint64)interpreterThunk & 0xFFFF000000000000) >> 48;
  589. movK = EncodeMove(/*Opcode*/ 0xF2E00000, /*register x1*/1, higherThunkBits); // left shift 48 bits; hw = 03
  590. Emit(thunkBuffer, addrOffset, movK);
  591. // Encode LDR - Load of function Body
  592. ULONG offsetOfFunctionInfo = Js::JavascriptFunction::GetOffsetOfFunctionInfo();
  593. AssertMsg(offsetOfFunctionInfo % 8 == 0, "Immediate offset for LDR must be 8 byte aligned");
  594. AssertMsg(offsetOfFunctionInfo < 0x8000, "Immediate offset for LDR must be less than 0x8000");
  595. *(PULONG)&thunkBuffer[FunctionInfoOffset] |= (offsetOfFunctionInfo / 8) << 10;
  596. ULONG offsetOfFunctionProxy = Js::FunctionInfo::GetOffsetOfFunctionProxy();
  597. AssertMsg(offsetOfFunctionProxy % 8 == 0, "Immediate offset for LDR must be 8 byte aligned");
  598. AssertMsg(offsetOfFunctionProxy < 0x8000, "Immediate offset for LDR must be less than 0x8000");
  599. *(PULONG)&thunkBuffer[FunctionProxyOffset] |= (offsetOfFunctionProxy / 8) << 10;
  600. // Encode LDR - Load of interpreter thunk number
  601. ULONG offsetOfDynamicInterpreterThunk = Js::FunctionBody::GetOffsetOfDynamicInterpreterThunk();
  602. AssertMsg(offsetOfDynamicInterpreterThunk % 8 == 0, "Immediate offset for LDR must be 8 byte aligned");
  603. AssertMsg(offsetOfDynamicInterpreterThunk < 0x8000, "Immediate offset for LDR must be less than 0x8000");
  604. *(PULONG)&thunkBuffer[DynamicThunkAddressOffset] |= (offsetOfDynamicInterpreterThunk / 8) << 10;
  605. }
  606. DWORD InterpreterThunkEmitter::EncodeMove(DWORD opCode, int reg, DWORD imm16)
  607. {
  608. DWORD encodedMove = reg << 0;
  609. #if _M_ARM
  610. DWORD encodedImm = 0;
  611. EncoderMD::EncodeImmediate16(imm16, &encodedImm);
  612. encodedMove |= encodedImm;
  613. #elif _M_ARM64
  614. // ToDo (SaAgarwa) - From Aaron change. Validate for ARM64
  615. encodedMove |= (imm16 & 0xFFFF) << 5;
  616. #endif
  617. AssertMsg((encodedMove & opCode) == 0, "Any bits getting overwritten?");
  618. encodedMove |= opCode;
  619. return encodedMove;
  620. }
  621. void InterpreterThunkEmitter::GeneratePdata(_In_ const BYTE* entryPoint, _In_ const DWORD functionSize, _Out_ RUNTIME_FUNCTION* function)
  622. {
  623. function->BeginAddress = 0x0; // Since our base address is the start of the function - this is offset from the base address
  624. function->Flag = 1; // Packed unwind data is used
  625. function->FunctionLength = functionSize / 4;
  626. function->RegF = 0; // number of non-volatile FP registers (d8-d15) saved in the canonical stack location
  627. function->RegI = 0; // number of non-volatile INT registers (r19-r28) saved in the canonical stack location
  628. function->H = 1; // Homes parameters
  629. // (indicating whether the function "homes" the integer parameter registers (r0-r7) by storing them at the very start of the function)
  630. function->CR = 3; // chained function, a store/load pair instruction is used in prolog/epilog <r29,lr>
  631. function->FrameSize = 5; // the number of bytes of stack that is allocated for this function divided by 16
  632. }
  633. #else
  634. void InterpreterThunkEmitter::EncodeInterpreterThunk(
  635. __in_bcount(InterpreterThunkSize) BYTE* thunkBuffer,
  636. __in const intptr_t thunkBufferStartAddress,
  637. __in const intptr_t epilogStart,
  638. __in const DWORD epilogSize,
  639. __in const intptr_t interpreterThunk)
  640. {
  641. Emit(thunkBuffer, ThunkAddressOffset, (uintptr_t)interpreterThunk);
  642. thunkBuffer[DynamicThunkAddressOffset] = Js::FunctionBody::GetOffsetOfDynamicInterpreterThunk();
  643. thunkBuffer[FunctionInfoOffset] = Js::JavascriptFunction::GetOffsetOfFunctionInfo();
  644. thunkBuffer[FunctionProxyOffset] = Js::FunctionInfo::GetOffsetOfFunctionProxy();
  645. Emit(thunkBuffer, CallBlockStartAddrOffset, (uintptr_t) thunkBufferStartAddress + HeaderSize);
  646. uint totalThunkSize = (uint)(epilogStart - (thunkBufferStartAddress + HeaderSize));
  647. Emit(thunkBuffer, ThunkSizeOffset, totalThunkSize);
  648. Emit(thunkBuffer, ErrorOffset, (BYTE) FAST_FAIL_INVALID_ARG);
  649. }
  650. #endif
  651. /*static*/
  652. DWORD InterpreterThunkEmitter::FillDebugBreak(_Out_writes_bytes_all_(count) BYTE* dest, _In_ DWORD count)
  653. {
  654. #if defined(_M_ARM)
  655. Assert(count % 2 == 0);
  656. #elif defined(_M_ARM64)
  657. Assert(count % 4 == 0);
  658. #endif
  659. CustomHeap::FillDebugBreak(dest, count);
  660. return count;
  661. }
  662. /*static*/
  663. DWORD InterpreterThunkEmitter::CopyWithAlignment(
  664. _Out_writes_bytes_all_(sizeInBytes) BYTE* dest,
  665. _In_ const DWORD sizeInBytes,
  666. _In_reads_bytes_(srcSize) const BYTE* src,
  667. _In_ const DWORD srcSize,
  668. _In_ const DWORD alignment)
  669. {
  670. js_memcpy_s(dest, sizeInBytes, src, srcSize);
  671. dest += srcSize;
  672. DWORD alignPad = Math::Align(srcSize, alignment) - srcSize;
  673. Assert(alignPad <= (sizeInBytes - srcSize));
  674. if(alignPad > 0 && alignPad <= (sizeInBytes - srcSize))
  675. {
  676. FillDebugBreak(dest, alignPad);
  677. return srcSize + alignPad;
  678. }
  679. return srcSize;
  680. }
  681. #if DBG
  682. bool
  683. InterpreterThunkEmitter::IsInHeap(void* address)
  684. {
  685. #ifdef ENABLE_OOP_NATIVE_CODEGEN
  686. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  687. {
  688. PSCRIPTCONTEXT_HANDLE remoteScript = this->scriptContext->GetRemoteScriptAddr(false);
  689. if (!remoteScript || !JITManager::GetJITManager()->IsConnected())
  690. {
  691. // this method is used in asserts to validate whether an entry point is valid
  692. // in case JIT process crashed, let's just say true to keep asserts from firing
  693. return true;
  694. }
  695. boolean result;
  696. HRESULT hr = JITManager::GetJITManager()->IsInterpreterThunkAddr(remoteScript, (intptr_t)address, this->isAsmInterpreterThunk, &result);
  697. if (!JITManager::HandleServerCallResult(hr, RemoteCallType::HeapQuery))
  698. {
  699. return true;
  700. }
  701. return result != FALSE;
  702. }
  703. else
  704. #endif
  705. {
  706. return emitBufferManager.IsInHeap(address);
  707. }
  708. }
  709. #endif
  710. // We only decommit at close because there might still be some
  711. // code running here.
  712. // The destructor of emitBufferManager will cause the eventual release.
  713. void InterpreterThunkEmitter::Close()
  714. {
  715. #if PDATA_ENABLED
  716. auto unregisterPdata = ([&] (const ThunkBlock& block)
  717. {
  718. PDataManager::UnregisterPdata((PRUNTIME_FUNCTION) block.GetPdata());
  719. });
  720. thunkBlocks.Iterate(unregisterPdata);
  721. freeListedThunkBlocks.Iterate(unregisterPdata);
  722. #endif
  723. this->thunkBlocks.Clear(allocator);
  724. this->freeListedThunkBlocks.Clear(allocator);
  725. #ifdef ENABLE_OOP_NATIVE_CODEGEN
  726. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  727. {
  728. PSCRIPTCONTEXT_HANDLE remoteScript = this->scriptContext->GetRemoteScriptAddr(false);
  729. if (remoteScript && JITManager::GetJITManager()->IsConnected())
  730. {
  731. JITManager::GetJITManager()->DecommitInterpreterBufferManager(remoteScript, this->isAsmInterpreterThunk);
  732. }
  733. }
  734. else
  735. #endif
  736. {
  737. emitBufferManager.Decommit();
  738. }
  739. this->thunkBuffer = nullptr;
  740. this->thunkCount = 0;
  741. }
  742. void InterpreterThunkEmitter::Release(BYTE* thunkAddress, bool addtoFreeList)
  743. {
  744. if(!addtoFreeList)
  745. {
  746. return;
  747. }
  748. auto predicate = ([=] (const ThunkBlock& block)
  749. {
  750. return block.Contains(thunkAddress);
  751. });
  752. ThunkBlock* block = freeListedThunkBlocks.Find(predicate);
  753. if(!block)
  754. {
  755. block = thunkBlocks.MoveTo(&freeListedThunkBlocks, predicate);
  756. }
  757. // if EnsureFreeList fails in an OOM scenario - we just leak the thunks
  758. if(block && block->EnsureFreeList(allocator))
  759. {
  760. block->Release(thunkAddress);
  761. }
  762. }
  763. BYTE* InterpreterThunkEmitter::AllocateFromFreeList(PVOID* ppDynamicInterpreterThunk )
  764. {
  765. ThunkBlock& block = this->freeListedThunkBlocks.Head();
  766. BYTE* thunk = block.AllocateFromFreeList();
  767. #if _M_ARM
  768. thunk = (BYTE*)((DWORD)thunk | 0x01);
  769. #endif
  770. if(block.IsFreeListEmpty())
  771. {
  772. this->freeListedThunkBlocks.MoveHeadTo(&this->thunkBlocks);
  773. }
  774. *ppDynamicInterpreterThunk = thunk;
  775. BYTE* entryPoint = block.GetStart();
  776. #if _M_ARM
  777. entryPoint = (BYTE*)((DWORD)entryPoint | 0x01);
  778. #endif
  779. return entryPoint;
  780. }
  781. bool ThunkBlock::Contains(BYTE* address) const
  782. {
  783. bool contains = address >= start && address < (start + InterpreterThunkEmitter::BlockSize);
  784. return contains;
  785. }
  786. void ThunkBlock::Release(BYTE* address)
  787. {
  788. Assert(Contains(address));
  789. Assert(this->freeList);
  790. BVIndex index = FromThunkAddress(address);
  791. this->freeList->Set(index);
  792. }
  793. BYTE* ThunkBlock::AllocateFromFreeList()
  794. {
  795. Assert(this->freeList);
  796. BVIndex index = this->freeList->GetNextBit(0);
  797. BYTE* address = ToThunkAddress(index);
  798. this->freeList->Clear(index);
  799. return address;
  800. }
  801. BVIndex ThunkBlock::FromThunkAddress(BYTE* address)
  802. {
  803. uint index = ((uint)(address - start) - HeaderSize) / InterpreterThunkEmitter::ThunkSize;
  804. Assert(index < this->thunkCount);
  805. return index;
  806. }
  807. BYTE* ThunkBlock::ToThunkAddress(BVIndex index)
  808. {
  809. Assert(index < this->thunkCount);
  810. BYTE* address = start + HeaderSize + InterpreterThunkEmitter::ThunkSize * index;
  811. return address;
  812. }
  813. bool ThunkBlock::EnsureFreeList(ArenaAllocator* allocator)
  814. {
  815. if(!this->freeList)
  816. {
  817. this->freeList = BVFixed::NewNoThrow(this->thunkCount, allocator);
  818. }
  819. return this->freeList != nullptr;
  820. }
  821. bool ThunkBlock::IsFreeListEmpty() const
  822. {
  823. Assert(this->freeList);
  824. return this->freeList->IsAllClear();
  825. }
  826. #endif // ENABLE_NATIVE_CODEGEN