NativeEntryPointData.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  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. #include "NativeEntryPointData.h"
  8. #include "JitTransferData.h"
  9. using namespace Js;
  10. NativeEntryPointData::NativeEntryPointData() :
  11. runtimeTypeRefs(nullptr), propertyGuardCount(0), propertyGuardWeakRefs(nullptr), polymorphicInlineCacheInfo(nullptr),
  12. equivalentTypeCacheCount(0), equivalentTypeCaches(nullptr), registeredEquivalentTypeCacheRef(nullptr),
  13. sharedPropertyGuards(nullptr), constructorCaches(nullptr), weakFuncRefSet(nullptr), jitTransferData(nullptr),
  14. nativeThrowSpanSequence(nullptr), codeSize(0), nativeAddress(nullptr), thunkAddress(nullptr), validationCookie(nullptr)
  15. #if PDATA_ENABLED
  16. , xdataInfo(nullptr)
  17. #endif
  18. #if DBG_DUMP | defined(VTUNE_PROFILING)
  19. , nativeOffsetMaps(&HeapAllocator::Instance)
  20. #endif
  21. {
  22. }
  23. JitTransferData*
  24. NativeEntryPointData::EnsureJitTransferData(Recycler* recycler)
  25. {
  26. if (this->jitTransferData == nullptr)
  27. {
  28. this->jitTransferData = RecyclerNew(recycler, JitTransferData);
  29. }
  30. return this->jitTransferData;
  31. }
  32. void
  33. NativeEntryPointData::FreeJitTransferData()
  34. {
  35. JitTransferData * data = this->jitTransferData;
  36. if (data != nullptr)
  37. {
  38. this->jitTransferData = nullptr;
  39. data->Cleanup();
  40. }
  41. }
  42. void
  43. NativeEntryPointData::RecordNativeCode(Js::JavascriptMethod thunkAddress, Js::JavascriptMethod nativeAddress, ptrdiff_t codeSize, void * validationCookie)
  44. {
  45. Assert(this->thunkAddress == nullptr);
  46. Assert(this->nativeAddress == nullptr);
  47. Assert(this->codeSize == 0);
  48. Assert(this->validationCookie == nullptr);
  49. this->nativeAddress = nativeAddress;
  50. this->thunkAddress = thunkAddress;
  51. this->codeSize = codeSize;
  52. this->validationCookie = validationCookie;
  53. }
  54. void
  55. NativeEntryPointData::FreeNativeCode(ScriptContext * scriptContext, bool isShutdown)
  56. {
  57. #if PDATA_ENABLED
  58. this->CleanupXDataInfo();
  59. #endif
  60. // if scriptContext is shutting down, no need to free that native code
  61. if (!isShutdown && this->nativeAddress != nullptr)
  62. {
  63. // In the debugger case, we might call cleanup after the native code gen that
  64. // allocated this entry point has already shutdown. In that case, the validation
  65. // check below should fail and we should not try to free this entry point
  66. // since it's already been freed
  67. Assert(this->validationCookie != nullptr);
  68. if (this->validationCookie == scriptContext->GetNativeCodeGenerator())
  69. {
  70. FreeNativeCodeGenAllocation(scriptContext, this->nativeAddress, this->thunkAddress);
  71. }
  72. }
  73. #ifdef PERF_COUNTERS
  74. PERF_COUNTER_SUB(Code, TotalNativeCodeSize, codeSize);
  75. PERF_COUNTER_SUB(Code, FunctionNativeCodeSize,codeSize);
  76. PERF_COUNTER_SUB(Code, DynamicNativeCodeSize, codeSize);
  77. #endif
  78. this->nativeAddress = nullptr;
  79. this->thunkAddress = nullptr;
  80. this->codeSize = 0;
  81. }
  82. void
  83. NativeEntryPointData::SetTJNativeAddress(Js::JavascriptMethod nativeAddress, void * validationCookie)
  84. {
  85. Assert(this->nativeAddress == nullptr);
  86. Assert(this->validationCookie == nullptr);
  87. this->nativeAddress = nativeAddress;
  88. this->validationCookie = validationCookie;
  89. }
  90. void
  91. NativeEntryPointData::SetTJCodeSize(ptrdiff_t codeSize)
  92. {
  93. Assert(this->codeSize == 0);
  94. this->codeSize = codeSize;
  95. }
  96. void
  97. NativeEntryPointData::AddWeakFuncRef(RecyclerWeakReference<FunctionBody> *weakFuncRef, Recycler *recycler)
  98. {
  99. NativeEntryPointData::WeakFuncRefSet * weakFuncRefSet = this->weakFuncRefSet;
  100. if (weakFuncRefSet == nullptr)
  101. {
  102. weakFuncRefSet = RecyclerNew(recycler, WeakFuncRefSet, recycler);
  103. this->weakFuncRefSet = weakFuncRefSet;
  104. }
  105. weakFuncRefSet->AddNew(weakFuncRef);
  106. }
  107. EntryPointPolymorphicInlineCacheInfo *
  108. NativeEntryPointData::EnsurePolymorphicInlineCacheInfo(Recycler * recycler, FunctionBody * functionBody)
  109. {
  110. if (!polymorphicInlineCacheInfo)
  111. {
  112. polymorphicInlineCacheInfo = RecyclerNew(recycler, EntryPointPolymorphicInlineCacheInfo, functionBody);
  113. }
  114. return polymorphicInlineCacheInfo;
  115. }
  116. void
  117. NativeEntryPointData::RegisterConstructorCache(Js::ConstructorCache* constructorCache, Recycler* recycler)
  118. {
  119. Assert(constructorCache != nullptr);
  120. if (!this->constructorCaches)
  121. {
  122. this->constructorCaches = RecyclerNew(recycler, ConstructorCacheList, recycler);
  123. }
  124. this->constructorCaches->Prepend(constructorCache);
  125. }
  126. void
  127. NativeEntryPointData::PinTypeRefs(Recycler * recycler, size_t jitPinnedTypeRefCount, void ** jitPinnedTypeRefs)
  128. {
  129. this->runtimeTypeRefs = RecyclerNewArray(recycler, Field(void*), jitPinnedTypeRefCount + 1);
  130. // Can't use memcpy here because we need write barrier triggers.
  131. // TODO: optimize this by using Memory::CopyArray instead.
  132. for (size_t i = 0; i < jitPinnedTypeRefCount; i++)
  133. {
  134. this->runtimeTypeRefs[i] = jitPinnedTypeRefs[i];
  135. }
  136. this->runtimeTypeRefs[jitPinnedTypeRefCount] = nullptr;
  137. }
  138. PropertyGuard* NativeEntryPointData::RegisterSharedPropertyGuard(Js::PropertyId propertyId, ScriptContext* scriptContext)
  139. {
  140. if (this->sharedPropertyGuards == nullptr)
  141. {
  142. Recycler* recycler = scriptContext->GetRecycler();
  143. this->sharedPropertyGuards = RecyclerNew(recycler, SharedPropertyGuardDictionary, recycler);
  144. }
  145. PropertyGuard* guard = nullptr;
  146. if (!this->sharedPropertyGuards->TryGetValue(propertyId, &guard))
  147. {
  148. ThreadContext* threadContext = scriptContext->GetThreadContext();
  149. guard = threadContext->RegisterSharedPropertyGuard(propertyId);
  150. this->sharedPropertyGuards->Add(propertyId, guard);
  151. }
  152. return guard;
  153. }
  154. Js::PropertyId* NativeEntryPointData::GetSharedPropertyGuards(Recycler * recycler, _Out_ unsigned int& count)
  155. {
  156. Js::PropertyId* sharedPropertyGuards = nullptr;
  157. unsigned int guardCount = 0;
  158. if (this->sharedPropertyGuards != nullptr)
  159. {
  160. const unsigned int sharedPropertyGuardsCount = (unsigned int)this->sharedPropertyGuards->Count();
  161. Js::PropertyId* guards = RecyclerNewArray(recycler, Js::PropertyId, sharedPropertyGuardsCount);
  162. auto sharedGuardIter = this->sharedPropertyGuards->GetIterator();
  163. while (sharedGuardIter.IsValid())
  164. {
  165. AnalysisAssert(guardCount < sharedPropertyGuardsCount);
  166. guards[guardCount] = sharedGuardIter.CurrentKey();
  167. sharedGuardIter.MoveNext();
  168. ++guardCount;
  169. }
  170. AnalysisAssert(guardCount == sharedPropertyGuardsCount);
  171. sharedPropertyGuards = guards;
  172. }
  173. count = guardCount;
  174. return sharedPropertyGuards;
  175. }
  176. bool NativeEntryPointData::TryGetSharedPropertyGuard(Js::PropertyId propertyId, Js::PropertyGuard*& guard)
  177. {
  178. return this->sharedPropertyGuards != nullptr ? this->sharedPropertyGuards->TryGetValue(propertyId, &guard) : false;
  179. }
  180. EquivalentTypeCache *
  181. NativeEntryPointData::EnsureEquivalentTypeCache(int guardCount, ScriptContext * scriptContext, EntryPointInfo * entryPointInfo)
  182. {
  183. Assert(this->equivalentTypeCacheCount == 0);
  184. Assert(this->equivalentTypeCaches == nullptr);
  185. // Create an array of equivalent type caches on the entry point info to ensure they are kept
  186. // alive for the lifetime of the entry point.
  187. // No need to zero-initialize, since we will populate all data slots.
  188. // We used to let the recycler scan the types in the cache, but we no longer do. See
  189. // ThreadContext::ClearEquivalentTypeCaches for an explanation.
  190. this->equivalentTypeCaches = RecyclerNewArrayLeafZ(scriptContext->GetRecycler(), EquivalentTypeCache, guardCount);
  191. this->equivalentTypeCacheCount = guardCount;
  192. this->RegisterEquivalentTypeCaches(scriptContext, entryPointInfo);
  193. return this->equivalentTypeCaches;
  194. }
  195. bool
  196. NativeEntryPointData::ClearEquivalentTypeCaches(Recycler * recycler)
  197. {
  198. Assert(this->equivalentTypeCaches != nullptr);
  199. Assert(this->equivalentTypeCacheCount > 0);
  200. bool isAnyCacheLive = false;
  201. for (EquivalentTypeCache *cache = this->equivalentTypeCaches;
  202. cache < this->equivalentTypeCaches + this->equivalentTypeCacheCount;
  203. cache++)
  204. {
  205. bool isCacheLive = cache->ClearUnusedTypes(recycler);
  206. if (isCacheLive)
  207. {
  208. isAnyCacheLive = true;
  209. }
  210. }
  211. if (!isAnyCacheLive)
  212. {
  213. // The caller must take care of unregistering this entry point. We may be in the middle of
  214. // walking the list of registered entry points.
  215. this->equivalentTypeCaches = nullptr;
  216. this->equivalentTypeCacheCount = 0;
  217. this->registeredEquivalentTypeCacheRef = nullptr;
  218. }
  219. return isAnyCacheLive;
  220. }
  221. Field(FakePropertyGuardWeakReference*) *
  222. NativeEntryPointData::EnsurePropertyGuardWeakRefs(int guardCount, Recycler * recycler)
  223. {
  224. Assert(this->propertyGuardCount == 0);
  225. Assert(this->propertyGuardWeakRefs == nullptr);
  226. this->propertyGuardWeakRefs = RecyclerNewArrayZ(recycler, Field(FakePropertyGuardWeakReference*), guardCount);
  227. this->propertyGuardCount = guardCount;
  228. return this->propertyGuardWeakRefs;
  229. }
  230. void
  231. NativeEntryPointData::RegisterEquivalentTypeCaches(ScriptContext * scriptContext, EntryPointInfo * entryPointInfo)
  232. {
  233. Assert(this->registeredEquivalentTypeCacheRef == nullptr);
  234. this->registeredEquivalentTypeCacheRef = scriptContext->GetThreadContext()->RegisterEquivalentTypeCacheEntryPoint(entryPointInfo);
  235. }
  236. void
  237. NativeEntryPointData::UnregisterEquivalentTypeCaches(ScriptContext * scriptContext)
  238. {
  239. EntryPointInfo ** registeredEquivalentTypeCacheRef = this->registeredEquivalentTypeCacheRef;
  240. if (registeredEquivalentTypeCacheRef != nullptr)
  241. {
  242. if (scriptContext != nullptr)
  243. {
  244. scriptContext->GetThreadContext()->UnregisterEquivalentTypeCacheEntryPoint(
  245. registeredEquivalentTypeCacheRef);
  246. }
  247. this->registeredEquivalentTypeCacheRef = nullptr;
  248. }
  249. }
  250. void
  251. NativeEntryPointData::FreePropertyGuards()
  252. {
  253. // While typePropertyGuardWeakRefs are allocated via NativeCodeData::Allocator and will be automatically freed to the heap,
  254. // we must zero out the fake weak references so that property guard invalidation doesn't access freed memory.
  255. if (this->propertyGuardWeakRefs != nullptr)
  256. {
  257. for (int i = 0; i < this->propertyGuardCount; i++)
  258. {
  259. if (this->propertyGuardWeakRefs[i] != nullptr)
  260. {
  261. this->propertyGuardWeakRefs[i]->Zero();
  262. }
  263. }
  264. this->propertyGuardCount = 0;
  265. this->propertyGuardWeakRefs = nullptr;
  266. }
  267. }
  268. void
  269. NativeEntryPointData::ClearTypeRefsAndGuards(ScriptContext * scriptContext)
  270. {
  271. this->runtimeTypeRefs = nullptr;
  272. this->FreePropertyGuards();
  273. this->equivalentTypeCacheCount = 0;
  274. this->equivalentTypeCaches = nullptr;
  275. this->UnregisterEquivalentTypeCaches(scriptContext);
  276. }
  277. #if PDATA_ENABLED
  278. void
  279. NativeEntryPointData::CleanupXDataInfo()
  280. {
  281. if (this->xdataInfo != nullptr)
  282. {
  283. #ifdef _WIN32
  284. if (this->xdataInfo->functionTable
  285. && !DelayDeletingFunctionTable::AddEntry(this->xdataInfo->functionTable))
  286. {
  287. DelayDeletingFunctionTable::DeleteFunctionTable(this->xdataInfo->functionTable);
  288. }
  289. #endif
  290. XDataAllocator::Unregister(this->xdataInfo);
  291. #if defined(_M_ARM)
  292. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  293. #endif
  294. {
  295. HeapDelete(this->xdataInfo);
  296. }
  297. this->xdataInfo = nullptr;
  298. }
  299. }
  300. #endif //PDATA_ENABLED
  301. void
  302. NativeEntryPointData::Cleanup(ScriptContext * scriptContext, bool isShutdown, bool reset)
  303. {
  304. this->FreeJitTransferData();
  305. this->FreeNativeCode(scriptContext, isShutdown);
  306. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  307. {
  308. ((OOPNativeEntryPointData *)this)->OnCleanup();
  309. }
  310. else
  311. {
  312. ((InProcNativeEntryPointData *)this)->OnCleanup();
  313. }
  314. this->ClearTypeRefsAndGuards(scriptContext);
  315. if (this->sharedPropertyGuards != nullptr)
  316. {
  317. // Reset can be called on the background thread, don't clear it explicitly
  318. if (!reset)
  319. {
  320. sharedPropertyGuards->Clear();
  321. }
  322. sharedPropertyGuards = nullptr;
  323. }
  324. if (this->constructorCaches != nullptr)
  325. {
  326. // Reset can be called on the background thread, don't clear it explicitly
  327. if (!reset)
  328. {
  329. this->constructorCaches->Clear();
  330. }
  331. this->constructorCaches = nullptr;
  332. }
  333. this->polymorphicInlineCacheInfo = nullptr;
  334. this->weakFuncRefSet = nullptr;
  335. if (this->nativeThrowSpanSequence)
  336. {
  337. HeapDelete(this->nativeThrowSpanSequence);
  338. this->nativeThrowSpanSequence = nullptr;
  339. }
  340. #if DBG_DUMP | defined(VTUNE_PROFILING)
  341. this->nativeOffsetMaps.Reset();
  342. #endif
  343. }
  344. InProcNativeEntryPointData::InProcNativeEntryPointData() :
  345. nativeCodeData(nullptr), inlineeFrameMap(nullptr), bailoutRecordMap(nullptr)
  346. #if !FLOATVAR
  347. , numberChunks(nullptr)
  348. #endif
  349. {
  350. Assert(!JITManager::GetJITManager()->IsOOPJITEnabled());
  351. }
  352. void
  353. InProcNativeEntryPointData::SetNativeCodeData(NativeCodeData * data)
  354. {
  355. Assert(!JITManager::GetJITManager()->IsOOPJITEnabled());
  356. Assert(this->nativeCodeData == nullptr);
  357. this->nativeCodeData = data;
  358. }
  359. InlineeFrameMap *
  360. InProcNativeEntryPointData::GetInlineeFrameMap()
  361. {
  362. Assert(!JITManager::GetJITManager()->IsOOPJITEnabled());
  363. return inlineeFrameMap;
  364. }
  365. void
  366. InProcNativeEntryPointData::RecordInlineeFrameMap(JsUtil::List<NativeOffsetInlineeFramePair, ArenaAllocator>* tempInlineeFrameMap)
  367. {
  368. Assert(!JITManager::GetJITManager()->IsOOPJITEnabled());
  369. Assert(this->inlineeFrameMap == nullptr);
  370. if (tempInlineeFrameMap->Count() > 0)
  371. {
  372. this->inlineeFrameMap = HeapNew(InlineeFrameMap, &HeapAllocator::Instance);
  373. this->inlineeFrameMap->Copy(tempInlineeFrameMap);
  374. }
  375. }
  376. BailOutRecordMap *
  377. InProcNativeEntryPointData::GetBailOutRecordMap()
  378. {
  379. Assert(!JITManager::GetJITManager()->IsOOPJITEnabled());
  380. return this->bailoutRecordMap;
  381. }
  382. void
  383. InProcNativeEntryPointData::RecordBailOutMap(JsUtil::List<LazyBailOutRecord, ArenaAllocator>* bailoutMap)
  384. {
  385. Assert(!JITManager::GetJITManager()->IsOOPJITEnabled());
  386. Assert(this->bailoutRecordMap == nullptr);
  387. this->bailoutRecordMap = HeapNew(BailOutRecordMap, &HeapAllocator::Instance);
  388. this->bailoutRecordMap->Copy(bailoutMap);
  389. }
  390. void
  391. InProcNativeEntryPointData::OnCleanup()
  392. {
  393. Assert(!JITManager::GetJITManager()->IsOOPJITEnabled());
  394. if (this->nativeCodeData)
  395. {
  396. DeleteNativeCodeData(this->nativeCodeData);
  397. this->nativeCodeData = nullptr;
  398. }
  399. if (this->inlineeFrameMap)
  400. {
  401. HeapDelete(this->inlineeFrameMap);
  402. this->inlineeFrameMap = nullptr;
  403. }
  404. if (this->bailoutRecordMap)
  405. {
  406. HeapDelete(this->bailoutRecordMap);
  407. this->bailoutRecordMap = nullptr;
  408. }
  409. #if !FLOATVAR
  410. this->numberChunks = nullptr;
  411. #endif
  412. }
  413. OOPNativeEntryPointData::OOPNativeEntryPointData() :
  414. nativeDataBuffer(nullptr)
  415. #if !FLOATVAR
  416. , numberArray(nullptr), numberPageSegments(nullptr)
  417. #endif
  418. {
  419. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  420. }
  421. char*
  422. OOPNativeEntryPointData::GetNativeDataBuffer()
  423. {
  424. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  425. return nativeDataBuffer;
  426. }
  427. char**
  428. OOPNativeEntryPointData::GetNativeDataBufferRef()
  429. {
  430. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  431. return &nativeDataBuffer;
  432. }
  433. void
  434. OOPNativeEntryPointData::SetNativeDataBuffer(char * buffer)
  435. {
  436. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  437. Assert(this->nativeDataBuffer == nullptr);
  438. this->nativeDataBuffer = buffer;
  439. }
  440. uint32
  441. OOPNativeEntryPointData::GetOffsetOfNativeDataBuffer()
  442. {
  443. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  444. return offsetof(OOPNativeEntryPointData, nativeDataBuffer);
  445. }
  446. uint
  447. OOPNativeEntryPointData::GetInlineeFrameOffsetArrayOffset()
  448. {
  449. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  450. return this->inlineeFrameOffsetArrayOffset;
  451. }
  452. uint
  453. OOPNativeEntryPointData::GetInlineeFrameOffsetArrayCount()
  454. {
  455. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  456. return this->inlineeFrameOffsetArrayCount;
  457. }
  458. void
  459. OOPNativeEntryPointData::RecordInlineeFrameOffsetsInfo(unsigned int offsetsArrayOffset, unsigned int offsetsArrayCount)
  460. {
  461. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  462. this->inlineeFrameOffsetArrayOffset = offsetsArrayOffset;
  463. this->inlineeFrameOffsetArrayCount = offsetsArrayCount;
  464. }
  465. #if !FLOATVAR
  466. void
  467. OOPNativeEntryPointData::ProcessNumberPageSegments(ScriptContext * scriptContext)
  468. {
  469. if (this->numberPageSegments)
  470. {
  471. this->numberArray = scriptContext->GetThreadContext()
  472. ->GetXProcNumberPageSegmentManager()->RegisterSegments(this->numberPageSegments);
  473. this->numberPageSegments = nullptr;
  474. }
  475. }
  476. #endif
  477. void
  478. OOPNativeEntryPointData::OnCleanup()
  479. {
  480. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  481. if (this->nativeDataBuffer)
  482. {
  483. DeleteNativeDataBuffer(this->nativeDataBuffer);
  484. this->nativeDataBuffer = nullptr;
  485. }
  486. #if !FLOATVAR
  487. this->numberArray = nullptr;
  488. #endif
  489. }
  490. void
  491. OOPNativeEntryPointData::DeleteNativeDataBuffer(char * nativeDataBuffer)
  492. {
  493. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  494. NativeDataBuffer* buffer = (NativeDataBuffer*)(nativeDataBuffer - offsetof(NativeDataBuffer, data));
  495. midl_user_free(buffer);
  496. }
  497. #endif