2
0

FuncInfo.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  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 "RuntimeByteCodePch.h"
  6. FuncInfo::FuncInfo(
  7. const char16 *name,
  8. ArenaAllocator *alloc,
  9. ByteCodeGenerator *byteCodeGenerator,
  10. Scope *paramScope,
  11. Scope *bodyScope,
  12. ParseNodeFnc *pnode,
  13. Js::ParseableFunctionInfo* byteCodeFunction)
  14. :
  15. inlineCacheCount(0),
  16. rootObjectLoadInlineCacheCount(0),
  17. rootObjectLoadMethodInlineCacheCount(0),
  18. rootObjectStoreInlineCacheCount(0),
  19. isInstInlineCacheCount(0),
  20. referencedPropertyIdCount(0),
  21. currentChildFunction(nullptr),
  22. currentChildScope(nullptr),
  23. capturedSyms(nullptr),
  24. capturedSymMap(nullptr),
  25. nextForInLoopLevel(0),
  26. maxForInLoopLevel(0),
  27. alloc(alloc),
  28. varRegsCount(0),
  29. constRegsCount(InitialConstRegsCount),
  30. inArgsCount(0),
  31. outArgsMaxDepth(0),
  32. outArgsCurrentExpr(0),
  33. innerScopeCount(0),
  34. currentInnerScopeIndex((uint)-1),
  35. #if DBG
  36. outArgsDepth(0),
  37. #endif
  38. name(name),
  39. nullConstantRegister(Js::Constants::NoRegister),
  40. undefinedConstantRegister(Js::Constants::NoRegister),
  41. trueConstantRegister(Js::Constants::NoRegister),
  42. falseConstantRegister(Js::Constants::NoRegister),
  43. thisConstantRegister(Js::Constants::NoRegister),
  44. envRegister(Js::Constants::NoRegister),
  45. frameObjRegister(Js::Constants::NoRegister),
  46. frameSlotsRegister(Js::Constants::NoRegister),
  47. paramSlotsRegister(Js::Constants::NoRegister),
  48. frameDisplayRegister(Js::Constants::NoRegister),
  49. funcObjRegister(Js::Constants::NoRegister),
  50. localClosureReg(Js::Constants::NoRegister),
  51. awaitRegister(Js::Constants::NoRegister),
  52. yieldRegister(Js::Constants::NoRegister),
  53. firstTmpReg(Js::Constants::NoRegister),
  54. curTmpReg(Js::Constants::NoRegister),
  55. argsPlaceHolderSlotCount(0),
  56. canDefer(false),
  57. callsEval(false),
  58. childCallsEval(false),
  59. hasArguments(false),
  60. hasHeapArguments(false),
  61. isTopLevelEventHandler(false),
  62. hasLocalInClosure(false),
  63. hasClosureReference(false),
  64. hasCachedScope(false),
  65. funcExprNameReference(false),
  66. applyEnclosesArgs(false),
  67. escapes(false),
  68. hasLoop(false),
  69. hasEscapedUseNestedFunc(false),
  70. needEnvRegister(false),
  71. isBodyAndParamScopeMerged(true),
  72. #if DBG
  73. isReused(false),
  74. #endif
  75. constantToRegister(alloc, 17),
  76. stringToRegister(alloc, 17),
  77. bigintToRegister(alloc, 17),
  78. doubleConstantToRegister(alloc, 17),
  79. stringTemplateCallsiteRegisterMap(alloc, 17),
  80. paramScope(paramScope),
  81. bodyScope(bodyScope),
  82. funcExprScope(nullptr),
  83. root(pnode),
  84. byteCodeFunction(byteCodeFunction),
  85. targetStatements(alloc),
  86. singleExit(0),
  87. rootObjectLoadInlineCacheMap(nullptr),
  88. rootObjectLoadMethodInlineCacheMap(nullptr),
  89. rootObjectStoreInlineCacheMap(nullptr),
  90. inlineCacheMap(nullptr),
  91. referencedPropertyIdToMapIndex(nullptr),
  92. callSiteToCallApplyCallSiteMap(nullptr),
  93. valueOfStoreCacheIds(),
  94. toStringStoreCacheIds(),
  95. slotProfileIdMap(alloc),
  96. argumentsSymbol(nullptr),
  97. thisSymbol(nullptr),
  98. newTargetSymbol(nullptr),
  99. superSymbol(nullptr),
  100. superConstructorSymbol(nullptr),
  101. importMetaSymbol(nullptr),
  102. nonUserNonTempRegistersToInitialize(alloc)
  103. {
  104. if (bodyScope != nullptr)
  105. {
  106. bodyScope->SetFunc(this);
  107. }
  108. if (paramScope != nullptr)
  109. {
  110. paramScope->SetFunc(this);
  111. }
  112. if (pnode && pnode->NestedFuncEscapes())
  113. {
  114. this->SetHasMaybeEscapedNestedFunc(DebugOnly(_u("Child")));
  115. }
  116. if (byteCodeFunction && !byteCodeFunction->IsDeferred() && byteCodeFunction->CanBeDeferred())
  117. {
  118. // Disable (re-)deferral of this function temporarily. Add it to the list of FuncInfo's to be processed when
  119. // byte code gen is done.
  120. this->canDefer = !!(byteCodeFunction->GetAttributes() & Js::FunctionInfo::Attributes::CanDefer);
  121. byteCodeGenerator->AddFuncInfoToFinalizationSet(this);
  122. byteCodeFunction->SetAttributes((Js::FunctionInfo::Attributes)(byteCodeFunction->GetAttributes() & ~Js::FunctionInfo::Attributes::CanDefer));
  123. }
  124. }
  125. bool FuncInfo::IsGlobalFunction() const
  126. {
  127. return root && root->nop == knopProg;
  128. }
  129. bool FuncInfo::IsDeferred() const
  130. {
  131. return root && root->pnodeBody == nullptr;
  132. }
  133. BOOL FuncInfo::HasSuperReference() const
  134. {
  135. return root->HasSuperReference();
  136. }
  137. BOOL FuncInfo::HasDirectSuper() const
  138. {
  139. return root->HasDirectSuper();
  140. }
  141. BOOL FuncInfo::IsClassMember() const
  142. {
  143. return this->byteCodeFunction->IsClassMethod();
  144. }
  145. BOOL FuncInfo::IsLambda() const
  146. {
  147. return this->byteCodeFunction->IsLambda();
  148. }
  149. BOOL FuncInfo::IsClassConstructor() const
  150. {
  151. return this->byteCodeFunction->IsClassConstructor();
  152. }
  153. BOOL FuncInfo::IsBaseClassConstructor() const
  154. {
  155. return this->byteCodeFunction->IsBaseClassConstructor();
  156. }
  157. BOOL FuncInfo::IsDerivedClassConstructor() const
  158. {
  159. return root->IsDerivedClassConstructor();
  160. }
  161. bool FuncInfo::IsAsyncGenerator() const
  162. {
  163. return (root->IsAsync() && root->IsGenerator());
  164. }
  165. Scope *
  166. FuncInfo::GetGlobalBlockScope() const
  167. {
  168. Assert(this->IsGlobalFunction());
  169. Scope * scope = this->root->pnodeScopes->scope;
  170. Assert(scope == nullptr || scope == this->GetBodyScope() || scope->GetEnclosingScope() == this->GetBodyScope());
  171. return scope;
  172. }
  173. Scope * FuncInfo::GetGlobalEvalBlockScope() const
  174. {
  175. Scope * globalEvalBlockScope = this->GetGlobalBlockScope();
  176. Assert(globalEvalBlockScope->GetEnclosingScope() == this->GetBodyScope());
  177. Assert(globalEvalBlockScope->GetScopeType() == ScopeType_GlobalEvalBlock);
  178. return globalEvalBlockScope;
  179. }
  180. uint FuncInfo::FindOrAddReferencedPropertyId(Js::PropertyId propertyId)
  181. {
  182. Assert(propertyId != Js::Constants::NoProperty);
  183. Assert(referencedPropertyIdToMapIndex != nullptr);
  184. if (propertyId < TotalNumberOfBuiltInProperties)
  185. {
  186. return propertyId;
  187. }
  188. uint index;
  189. if (!referencedPropertyIdToMapIndex->TryGetValue(propertyId, &index))
  190. {
  191. index = this->NewReferencedPropertyId();
  192. referencedPropertyIdToMapIndex->Add(propertyId, index);
  193. }
  194. return index + TotalNumberOfBuiltInProperties;
  195. }
  196. uint FuncInfo::FindOrAddRootObjectInlineCacheId(Js::PropertyId propertyId, bool isLoadMethod, bool isStore)
  197. {
  198. Assert(propertyId != Js::Constants::NoProperty);
  199. Assert(!isLoadMethod || !isStore);
  200. uint cacheId;
  201. RootObjectInlineCacheIdMap * idMap = isStore ? rootObjectStoreInlineCacheMap : isLoadMethod ? rootObjectLoadMethodInlineCacheMap : rootObjectLoadInlineCacheMap;
  202. if (!idMap->TryGetValue(propertyId, &cacheId))
  203. {
  204. cacheId = isStore ? this->NewRootObjectStoreInlineCache() : isLoadMethod ? this->NewRootObjectLoadMethodInlineCache() : this->NewRootObjectLoadInlineCache();
  205. idMap->Add(propertyId, cacheId);
  206. }
  207. return cacheId;
  208. }
  209. #if DBG_DUMP
  210. void FuncInfo::Dump()
  211. {
  212. Output::Print(_u("FuncInfo: CallsEval:%s ChildCallsEval:%s HasArguments:%s HasHeapArguments:%s\n"),
  213. IsTrueOrFalse(this->GetCallsEval()),
  214. IsTrueOrFalse(this->GetChildCallsEval()),
  215. IsTrueOrFalse(this->GetHasArguments()),
  216. IsTrueOrFalse(this->GetHasHeapArguments()));
  217. }
  218. #endif
  219. Js::RegSlot FuncInfo::AcquireLoc(ParseNode *pnode)
  220. {
  221. // Assign a new temp pseudo-register to this expression.
  222. if (pnode->location == Js::Constants::NoRegister)
  223. {
  224. pnode->location = this->AcquireTmpRegister();
  225. }
  226. return pnode->location;
  227. }
  228. Js::RegSlot FuncInfo::AcquireTmpRegister()
  229. {
  230. Assert(this->firstTmpReg != Js::Constants::NoRegister);
  231. // Allocate a new temp pseudo-register, increasing the locals count if necessary.
  232. Assert(this->curTmpReg <= this->varRegsCount && this->curTmpReg >= this->firstTmpReg);
  233. Js::RegSlot tmpReg = this->curTmpReg;
  234. UInt32Math::Inc(this->curTmpReg);
  235. if (this->curTmpReg > this->varRegsCount)
  236. {
  237. this->varRegsCount = this->curTmpReg;
  238. }
  239. return tmpReg;
  240. }
  241. void FuncInfo::ReleaseLoc(ParseNode *pnode)
  242. {
  243. // Release the temp assigned to this expression so it can be re-used.
  244. if (pnode && pnode->location != Js::Constants::NoRegister)
  245. {
  246. this->ReleaseTmpRegister(pnode->location);
  247. }
  248. }
  249. void FuncInfo::ReleaseLoad(ParseNode *pnode)
  250. {
  251. // Release any temp register(s) acquired by an EmitLoad.
  252. switch (pnode->nop)
  253. {
  254. case knopDot:
  255. case knopIndex:
  256. case knopCall:
  257. this->ReleaseReference(pnode);
  258. break;
  259. }
  260. this->ReleaseLoc(pnode);
  261. }
  262. void FuncInfo::ReleaseReference(ParseNode *pnode)
  263. {
  264. // Release any temp(s) assigned to this reference expression so they can be re-used.
  265. switch (pnode->nop)
  266. {
  267. case knopDot:
  268. this->ReleaseLoc(pnode->AsParseNodeBin()->pnode1);
  269. break;
  270. case knopIndex:
  271. this->ReleaseLoc(pnode->AsParseNodeBin()->pnode2);
  272. this->ReleaseLoc(pnode->AsParseNodeBin()->pnode1);
  273. break;
  274. case knopName:
  275. // Do nothing (see EmitReference)
  276. break;
  277. case knopCall:
  278. case knopNew:
  279. // For call/new, we have to release the ArgOut register(s) in reverse order,
  280. // but we have the args in a singly linked list.
  281. // Fortunately, we know that the set we have to release is sequential.
  282. // So find the endpoints of the list and release them in descending order.
  283. if (pnode->AsParseNodeCall()->pnodeArgs)
  284. {
  285. ParseNode *pnodeArg = pnode->AsParseNodeCall()->pnodeArgs;
  286. Js::RegSlot firstArg = Js::Constants::NoRegister;
  287. Js::RegSlot lastArg = Js::Constants::NoRegister;
  288. if (pnodeArg->nop == knopList)
  289. {
  290. do
  291. {
  292. if (this->IsTmpReg(pnodeArg->AsParseNodeBin()->pnode1->location))
  293. {
  294. lastArg = pnodeArg->AsParseNodeBin()->pnode1->location;
  295. if (firstArg == Js::Constants::NoRegister)
  296. {
  297. firstArg = lastArg;
  298. }
  299. }
  300. pnodeArg = pnodeArg->AsParseNodeBin()->pnode2;
  301. }
  302. while (pnodeArg->nop == knopList);
  303. }
  304. if (this->IsTmpReg(pnodeArg->location))
  305. {
  306. lastArg = pnodeArg->location;
  307. if (firstArg == Js::Constants::NoRegister)
  308. {
  309. // Just one: first and last point to the same node.
  310. firstArg = lastArg;
  311. }
  312. }
  313. if (lastArg != Js::Constants::NoRegister)
  314. {
  315. Assert(firstArg != Js::Constants::NoRegister);
  316. Assert(lastArg >= firstArg);
  317. do
  318. {
  319. // Walk down from last to first.
  320. this->ReleaseTmpRegister(lastArg);
  321. } while (lastArg-- > firstArg); // these are unsigned, so (--lastArg >= firstArg) will cause an infinite loop if firstArg is 0 (although that shouldn't happen)
  322. }
  323. }
  324. // Now release the call target.
  325. if (!pnode->AsParseNodeCall()->isSuperCall)
  326. {
  327. switch (pnode->AsParseNodeCall()->pnodeTarget->nop)
  328. {
  329. case knopDot:
  330. case knopIndex:
  331. this->ReleaseReference(pnode->AsParseNodeCall()->pnodeTarget);
  332. this->ReleaseLoc(pnode->AsParseNodeCall()->pnodeTarget);
  333. break;
  334. default:
  335. this->ReleaseLoad(pnode->AsParseNodeCall()->pnodeTarget);
  336. break;
  337. }
  338. }
  339. break;
  340. default:
  341. this->ReleaseLoc(pnode);
  342. break;
  343. }
  344. }
  345. void FuncInfo::ReleaseTmpRegister(Js::RegSlot tmpReg)
  346. {
  347. // Put this reg back on top of the temp stack (if it's a temp).
  348. Assert(tmpReg != Js::Constants::NoRegister);
  349. if (this->IsTmpReg(tmpReg))
  350. {
  351. Assert(tmpReg == this->curTmpReg - 1);
  352. this->curTmpReg--;
  353. }
  354. }
  355. Js::RegSlot FuncInfo::InnerScopeToRegSlot(Scope *scope) const
  356. {
  357. Js::RegSlot reg = FirstInnerScopeReg();
  358. Assert(reg != Js::Constants::NoRegister);
  359. uint32 index = scope->GetInnerScopeIndex();
  360. return reg + index;
  361. }
  362. Js::RegSlot FuncInfo::FirstInnerScopeReg() const
  363. {
  364. // FunctionBody stores this as a mapped reg. Callers of this function want the pre-mapped value.
  365. Js::RegSlot reg = this->GetParsedFunctionBody()->GetFirstInnerScopeRegister();
  366. Assert(reg != Js::Constants::NoRegister);
  367. return reg - this->constRegsCount;
  368. }
  369. void FuncInfo::SetFirstInnerScopeReg(Js::RegSlot reg)
  370. {
  371. // Just forward to the FunctionBody.
  372. this->GetParsedFunctionBody()->MapAndSetFirstInnerScopeRegister(reg);
  373. }
  374. void FuncInfo::AddCapturedSym(Symbol *sym)
  375. {
  376. if (this->capturedSyms == nullptr)
  377. {
  378. this->capturedSyms = Anew(alloc, SymbolTable, alloc);
  379. }
  380. this->capturedSyms->AddNew(sym);
  381. }
  382. void FuncInfo::OnStartVisitFunction(ParseNodeFnc *pnodeFnc)
  383. {
  384. Assert(pnodeFnc->nop == knopFncDecl);
  385. Assert(this->GetCurrentChildFunction() == nullptr);
  386. this->SetCurrentChildFunction(pnodeFnc->funcInfo);
  387. }
  388. void FuncInfo::OnEndVisitFunction(ParseNodeFnc *pnodeFnc)
  389. {
  390. Assert(pnodeFnc->nop == knopFncDecl);
  391. Assert(this->GetCurrentChildFunction() == pnodeFnc->funcInfo);
  392. pnodeFnc->funcInfo->SetCurrentChildScope(nullptr);
  393. this->SetCurrentChildFunction(nullptr);
  394. }
  395. void FuncInfo::OnStartVisitScope(Scope *scope, bool *pisMergedScope)
  396. {
  397. *pisMergedScope = false;
  398. if (scope == nullptr)
  399. {
  400. return;
  401. }
  402. Scope* childScope = this->GetCurrentChildScope();
  403. if (childScope)
  404. {
  405. if (scope->GetScopeType() == ScopeType_Parameter)
  406. {
  407. Assert(childScope->GetEnclosingScope() == scope);
  408. }
  409. else if (childScope->GetScopeType() == ScopeType_Parameter
  410. && childScope->GetFunc()->IsBodyAndParamScopeMerged()
  411. && scope->GetScopeType() == ScopeType_Block)
  412. {
  413. // If param and body are merged then the class declaration in param scope will have body as the parent
  414. *pisMergedScope = true;
  415. Assert(childScope == scope->GetEnclosingScope()->GetEnclosingScope());
  416. }
  417. else
  418. {
  419. Assert(childScope == scope->GetEnclosingScope());
  420. }
  421. }
  422. this->SetCurrentChildScope(scope);
  423. return;
  424. }
  425. void FuncInfo::OnEndVisitScope(Scope *scope, bool isMergedScope)
  426. {
  427. if (scope == nullptr)
  428. {
  429. return;
  430. }
  431. Assert(this->GetCurrentChildScope() == scope || (scope->GetScopeType() == ScopeType_Parameter && this->GetParamScope() == scope));
  432. this->SetCurrentChildScope(isMergedScope ? scope->GetEnclosingScope()->GetEnclosingScope() : scope->GetEnclosingScope());
  433. }
  434. CapturedSymMap *FuncInfo::EnsureCapturedSymMap()
  435. {
  436. if (this->capturedSymMap == nullptr)
  437. {
  438. this->capturedSymMap = Anew(alloc, CapturedSymMap, alloc);
  439. }
  440. return this->capturedSymMap;
  441. }
  442. CallSiteToCallApplyCallSiteMap * FuncInfo::EnsureCallSiteToCallApplyCallSiteMap()
  443. {
  444. if (this->callSiteToCallApplyCallSiteMap == nullptr)
  445. {
  446. this->callSiteToCallApplyCallSiteMap = Anew(alloc, CallSiteToCallApplyCallSiteMap, alloc);
  447. }
  448. return this->callSiteToCallApplyCallSiteMap;
  449. }
  450. void FuncInfo::SetHasMaybeEscapedNestedFunc(DebugOnly(char16 const * reason))
  451. {
  452. if (PHASE_TESTTRACE(Js::StackFuncPhase, this->byteCodeFunction) && !hasEscapedUseNestedFunc)
  453. {
  454. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  455. char16 const * r = _u("");
  456. DebugOnly(r = reason);
  457. Output::Print(_u("HasMaybeEscapedNestedFunc (%s): %s (function %s)\n"),
  458. r,
  459. this->byteCodeFunction->GetDisplayName(),
  460. this->byteCodeFunction->GetDebugNumberSet(debugStringBuffer));
  461. Output::Flush();
  462. }
  463. hasEscapedUseNestedFunc = true;
  464. }
  465. uint FuncInfo::AcquireInnerScopeIndex()
  466. {
  467. uint index = this->currentInnerScopeIndex;
  468. if (index == (uint)-1)
  469. {
  470. index = 0;
  471. }
  472. else
  473. {
  474. index++;
  475. if (index == (uint)-1)
  476. {
  477. Js::Throw::OutOfMemory();
  478. }
  479. }
  480. if (index == this->innerScopeCount)
  481. {
  482. this->innerScopeCount = index + 1;
  483. }
  484. this->currentInnerScopeIndex = index;
  485. return index;
  486. }
  487. void FuncInfo::ReleaseInnerScopeIndex()
  488. {
  489. uint index = this->currentInnerScopeIndex;
  490. Assert(index != (uint)-1);
  491. if (index == 0)
  492. {
  493. index = (uint)-1;
  494. }
  495. else
  496. {
  497. index--;
  498. }
  499. this->currentInnerScopeIndex = index;
  500. }