FuncInfo.cpp 16 KB

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