FuncInfo.cpp 18 KB

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