FuncInfo.cpp 15 KB

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