FuncInfo.cpp 17 KB

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