2
0

InterpreterThunkEmitter.cpp 41 KB

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