SwitchIRBuilder.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
  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 "Backend.h"
  6. ///----------------------------------------------------------------------------
  7. ///
  8. /// IRBuilderSwitchAdapter
  9. ///
  10. /// Implementation for IRBuilderSwitchAdapter, which passes actions generated
  11. /// by a SwitchIRBuilder to an IRBuilder instance
  12. ///----------------------------------------------------------------------------
  13. void
  14. IRBuilderSwitchAdapter::AddBranchInstr(IR::BranchInstr * instr, uint32 offset, uint32 targetOffset, bool clearBackEdge)
  15. {
  16. BranchReloc * reloc = m_builder->AddBranchInstr(instr, offset, targetOffset);
  17. if (clearBackEdge)
  18. {
  19. reloc->SetNotBackEdge();
  20. }
  21. }
  22. void
  23. IRBuilderSwitchAdapter::AddInstr(IR::Instr * instr, uint32 offset)
  24. {
  25. m_builder->AddInstr(instr, offset);
  26. }
  27. void
  28. IRBuilderSwitchAdapter::CreateRelocRecord(IR::BranchInstr * branchInstr, uint32 offset, uint32 targetOffset, bool clearBackEdge)
  29. {
  30. BranchReloc * reloc = m_builder->CreateRelocRecord(
  31. branchInstr,
  32. offset,
  33. targetOffset);
  34. if (clearBackEdge)
  35. {
  36. reloc->SetNotBackEdge();
  37. }
  38. }
  39. void
  40. IRBuilderSwitchAdapter::ConvertToBailOut(IR::Instr * instr, IR::BailOutKind kind)
  41. {
  42. instr = instr->ConvertToBailOutInstr(instr, kind);
  43. Assert(instr->GetByteCodeOffset() < m_builder->m_offsetToInstructionCount);
  44. m_builder->m_offsetToInstruction[instr->GetByteCodeOffset()] = instr;
  45. }
  46. ///----------------------------------------------------------------------------
  47. ///
  48. /// IRBuilderAsmJsSwitchAdapter
  49. ///
  50. /// Implementation for IRBuilderSwitchAdapter, which passes actions generated
  51. /// by a SwitchIRBuilder to an IRBuilder instance
  52. ///----------------------------------------------------------------------------
  53. #ifdef ASMJS_PLAT
  54. void
  55. IRBuilderAsmJsSwitchAdapter::AddBranchInstr(IR::BranchInstr * instr, uint32 offset, uint32 targetOffset, bool clearBackEdge)
  56. {
  57. BranchReloc * reloc = m_builder->AddBranchInstr(instr, offset, targetOffset);
  58. if (clearBackEdge)
  59. {
  60. reloc->SetNotBackEdge();
  61. }
  62. }
  63. void
  64. IRBuilderAsmJsSwitchAdapter::AddInstr(IR::Instr * instr, uint32 offset)
  65. {
  66. m_builder->AddInstr(instr, offset);
  67. }
  68. void
  69. IRBuilderAsmJsSwitchAdapter::CreateRelocRecord(IR::BranchInstr * branchInstr, uint32 offset, uint32 targetOffset, bool clearBackEdge)
  70. {
  71. BranchReloc * reloc = m_builder->CreateRelocRecord(
  72. branchInstr,
  73. offset,
  74. targetOffset);
  75. if (clearBackEdge)
  76. {
  77. reloc->SetNotBackEdge();
  78. }
  79. }
  80. void
  81. IRBuilderAsmJsSwitchAdapter::ConvertToBailOut(IR::Instr * instr, IR::BailOutKind kind)
  82. {
  83. Assert(false);
  84. // ConvertToBailOut should never get called for AsmJs
  85. // switches, since we already know ahead of time that the
  86. // switch expression is Int32
  87. }
  88. #endif
  89. ///----------------------------------------------------------------------------
  90. ///
  91. /// SwitchIRBuilder::Init
  92. ///
  93. /// Initializes the function and temporary allocator for the SwitchIRBuilder
  94. ///----------------------------------------------------------------------------
  95. void
  96. SwitchIRBuilder::Init(Func * func, JitArenaAllocator * tempAlloc, bool isAsmJs)
  97. {
  98. m_func = func;
  99. m_tempAlloc = tempAlloc;
  100. m_isAsmJs = isAsmJs;
  101. // caseNodes is a list of Case instructions
  102. m_caseNodes = CaseNodeList::New(tempAlloc);
  103. m_seenOnlySingleCharStrCaseNodes = true;
  104. m_intConstSwitchCases = JitAnew(tempAlloc, BVSparse<JitArenaAllocator>, tempAlloc);
  105. m_strConstSwitchCases = StrSwitchCaseList::New(tempAlloc);
  106. m_eqOp = isAsmJs ? Js::OpCode::BrEq_I4 : Js::OpCode::BrSrEq_A;
  107. m_ltOp = isAsmJs ? Js::OpCode::BrLt_I4 : Js::OpCode::BrLt_A;
  108. m_leOp = isAsmJs ? Js::OpCode::BrLe_I4 : Js::OpCode::BrLe_A;
  109. m_gtOp = isAsmJs ? Js::OpCode::BrGt_I4 : Js::OpCode::BrGt_A;
  110. m_geOp = isAsmJs ? Js::OpCode::BrGe_I4 : Js::OpCode::BrGe_A;
  111. m_subOp = isAsmJs ? Js::OpCode::Sub_I4 : Js::OpCode::Sub_A;
  112. }
  113. ///----------------------------------------------------------------------------
  114. ///
  115. /// SwitchIRBuilder::BeginSwitch
  116. ///
  117. /// Prepares the SwitchIRBuilder for building a new switch statement
  118. ///----------------------------------------------------------------------------
  119. void
  120. SwitchIRBuilder::BeginSwitch()
  121. {
  122. m_intConstSwitchCases->ClearAll();
  123. m_strConstSwitchCases->Clear();
  124. if (m_isAsmJs)
  125. {
  126. // never build bailout information for asmjs
  127. m_switchOptBuildBail = false;
  128. // asm.js switch is always integer
  129. m_switchIntDynProfile = true;
  130. AssertMsg(!m_switchStrDynProfile, "String profiling should not be enabled for an asm.js switch statement");
  131. }
  132. }
  133. ///----------------------------------------------------------------------------
  134. ///
  135. /// SwitchIRBuilder::EndSwitch
  136. ///
  137. /// Notifies the switch builder the switch being generated has been completed
  138. ///----------------------------------------------------------------------------
  139. void
  140. SwitchIRBuilder::EndSwitch(uint32 offset, uint32 targetOffset)
  141. {
  142. FlushCases(targetOffset);
  143. AssertMsg(m_caseNodes->Count() == 0, "Not all switch case nodes built by end of switch");
  144. // only generate the final unconditional jump at the end of the switch
  145. IR::BranchInstr * branch = IR::BranchInstr::New(Js::OpCode::Br, nullptr, m_func);
  146. m_adapter->AddBranchInstr(branch, offset, targetOffset, true);
  147. m_profiledSwitchInstr = nullptr;
  148. }
  149. ///----------------------------------------------------------------------------
  150. ///
  151. /// SwitchIRBuilder::SetProfiledInstruction
  152. ///
  153. /// Sets the profiled switch instruction for the switch statement that
  154. /// is being built
  155. ///----------------------------------------------------------------------------
  156. void
  157. SwitchIRBuilder::SetProfiledInstruction(IR::Instr * instr, Js::ProfileId profileId)
  158. {
  159. m_profiledSwitchInstr = instr;
  160. m_switchOptBuildBail = true;
  161. //don't optimize if the switch expression is not an Integer (obtained via dynamic profiling data of the BeginSwitch opcode)
  162. bool hasProfile = m_profiledSwitchInstr->IsProfiledInstr() && m_profiledSwitchInstr->m_func->HasProfileInfo();
  163. if (hasProfile)
  164. {
  165. const ValueType valueType(m_profiledSwitchInstr->m_func->GetReadOnlyProfileInfo()->GetSwitchProfileInfo(profileId));
  166. instr->AsProfiledInstr()->u.FldInfo().valueType = valueType;
  167. m_switchIntDynProfile = valueType.IsLikelyTaggedInt();
  168. m_switchStrDynProfile = valueType.IsLikelyString();
  169. if (PHASE_TESTTRACE1(Js::SwitchOptPhase))
  170. {
  171. char valueTypeStr[VALUE_TYPE_MAX_STRING_SIZE];
  172. valueType.ToString(valueTypeStr);
  173. #if ENABLE_DEBUG_CONFIG_OPTIONS
  174. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  175. #endif
  176. PHASE_PRINT_TESTTRACE1(Js::SwitchOptPhase, _u("Func %s, Switch %d: Expression Type : %S\n"),
  177. m_profiledSwitchInstr->m_func->GetDebugNumberSet(debugStringBuffer),
  178. m_profiledSwitchInstr->AsProfiledInstr()->u.profileId, valueTypeStr);
  179. }
  180. }
  181. }
  182. ///----------------------------------------------------------------------------
  183. ///
  184. /// SwitchIRBuilder::OnCase
  185. ///
  186. /// Handles a case instruction, generating the appropriate branches, or
  187. /// storing the case to generate a more optimized branch later
  188. ///
  189. ///----------------------------------------------------------------------------
  190. void
  191. SwitchIRBuilder::OnCase(IR::RegOpnd * src1Opnd, IR::Opnd * src2Opnd, uint32 offset, uint32 targetOffset)
  192. {
  193. IR::BranchInstr * branchInstr;
  194. Assert(src2Opnd->IsIntConstOpnd() || src2Opnd->IsRegOpnd());
  195. // Support only int32 const opnd
  196. Assert(!src2Opnd->IsIntConstOpnd() || src2Opnd->GetType() == TyInt32);
  197. StackSym* sym = src2Opnd->GetStackSym();
  198. const bool isIntConst = src2Opnd->IsIntConstOpnd() || (sym && sym->IsIntConst());
  199. const bool isStrConst = !isIntConst && sym && sym->m_isStrConst;
  200. if (GlobOpt::IsSwitchOptEnabled(m_func->GetTopFunc()) &&
  201. isIntConst &&
  202. m_intConstSwitchCases->TestAndSet(sym ? sym->GetIntConstValue() : src2Opnd->AsIntConstOpnd()->AsInt32()))
  203. {
  204. // We've already seen a case statement with the same int const value. No need to emit anything for this.
  205. return;
  206. }
  207. if (GlobOpt::IsSwitchOptEnabled(m_func->GetTopFunc()) && isStrConst
  208. && TestAndAddStringCaseConst(JITJavascriptString::FromVar(sym->GetConstAddress(true))))
  209. {
  210. // We've already seen a case statement with the same string const value. No need to emit anything for this.
  211. return;
  212. }
  213. branchInstr = IR::BranchInstr::New(m_eqOp, nullptr, src1Opnd, src2Opnd, m_func);
  214. branchInstr->m_isSwitchBr = true;
  215. /*
  216. // Switch optimization
  217. // For Integers - Binary Search or jump table optimization technique is used
  218. // For Strings - Dictionary look up technique is used.
  219. //
  220. // For optimizing, the Load instruction corresponding to the switch instruction is profiled in the interpreter.
  221. // Based on the dynamic profile data, optimization technique is decided.
  222. */
  223. bool deferred = false;
  224. if (GlobOpt::IsSwitchOptEnabled(m_func->GetTopFunc()))
  225. {
  226. if (m_switchIntDynProfile && isIntConst)
  227. {
  228. CaseNode* caseNode = JitAnew(m_tempAlloc, CaseNode, branchInstr, offset, targetOffset, src2Opnd);
  229. m_caseNodes->Add(caseNode);
  230. deferred = true;
  231. }
  232. else if (m_switchStrDynProfile && isStrConst)
  233. {
  234. CaseNode* caseNode = JitAnew(m_tempAlloc, CaseNode, branchInstr, offset, targetOffset, src2Opnd);
  235. m_caseNodes->Add(caseNode);
  236. m_seenOnlySingleCharStrCaseNodes = m_seenOnlySingleCharStrCaseNodes && caseNode->GetUpperBoundStringConstLocal()->GetLength() == 1;
  237. deferred = true;
  238. }
  239. }
  240. if (!deferred)
  241. {
  242. FlushCases(offset);
  243. m_adapter->AddBranchInstr(branchInstr, offset, targetOffset);
  244. }
  245. }
  246. ///----------------------------------------------------------------------------
  247. ///
  248. /// SwitchIRBuilder::FlushCases
  249. ///
  250. /// Called when a scenario for which optimized switch cases cannot be
  251. /// generated occurs, and generates optimized branches for all cases that
  252. /// have been stored up to this point
  253. ///
  254. ///----------------------------------------------------------------------------
  255. void
  256. SwitchIRBuilder::FlushCases(uint32 targetOffset)
  257. {
  258. if (m_caseNodes->Empty())
  259. {
  260. return;
  261. }
  262. if (m_switchIntDynProfile)
  263. {
  264. BuildCaseBrInstr(targetOffset);
  265. }
  266. else if (m_switchStrDynProfile)
  267. {
  268. BuildMultiBrCaseInstrForStrings(targetOffset);
  269. }
  270. else
  271. {
  272. Assert(false);
  273. }
  274. }
  275. ///----------------------------------------------------------------------------
  276. ///
  277. /// SwitchIRBuilder::RefineCaseNodes
  278. ///
  279. /// Filter IR instructions for case statements that contain no case blocks.
  280. /// Also sets upper bound and lower bound for case instructions that has a
  281. /// consecutive set of cases with just one case block.
  282. ///----------------------------------------------------------------------------
  283. void
  284. SwitchIRBuilder::RefineCaseNodes()
  285. {
  286. m_caseNodes->Sort();
  287. CaseNodeList * tmpCaseNodes = CaseNodeList::New(m_tempAlloc);
  288. for (int currCaseIndex = 1; currCaseIndex < m_caseNodes->Count(); currCaseIndex++)
  289. {
  290. CaseNode * prevCaseNode = m_caseNodes->Item(currCaseIndex - 1);
  291. CaseNode * currCaseNode = m_caseNodes->Item(currCaseIndex);
  292. uint32 prevCaseTargetOffset = prevCaseNode->GetTargetOffset();
  293. uint32 currCaseTargetOffset = currCaseNode->GetTargetOffset();
  294. int prevCaseConstValue = prevCaseNode->GetUpperBoundIntConst();
  295. int currCaseConstValue = currCaseNode->GetUpperBoundIntConst();
  296. /*To handle empty case statements with/without repetition*/
  297. if (prevCaseTargetOffset == currCaseTargetOffset &&
  298. (prevCaseConstValue + 1 == currCaseConstValue || prevCaseConstValue == currCaseConstValue))
  299. {
  300. m_caseNodes->Item(currCaseIndex)->SetLowerBound(prevCaseNode->GetLowerBound());
  301. }
  302. else
  303. {
  304. if (tmpCaseNodes->Count() != 0)
  305. {
  306. int lastTmpCaseConstValue = tmpCaseNodes->Item(tmpCaseNodes->Count() - 1)->GetUpperBoundIntConst();
  307. /*To handle duplicate non empty case statements*/
  308. if (lastTmpCaseConstValue != prevCaseConstValue)
  309. {
  310. tmpCaseNodes->Add(prevCaseNode);
  311. }
  312. }
  313. else
  314. {
  315. tmpCaseNodes->Add(prevCaseNode); //Adding for the first time in tmpCaseNodes
  316. }
  317. }
  318. }
  319. //Adds the last caseNode in the caseNodes list.
  320. tmpCaseNodes->Add(m_caseNodes->Item(m_caseNodes->Count() - 1));
  321. m_caseNodes = tmpCaseNodes;
  322. }
  323. ///--------------------------------------------------------------------------------------
  324. ///
  325. /// SwitchIRBuilder::BuildBinaryTraverseInstr
  326. ///
  327. /// Build IR instructions for case statements in a binary search traversal fashion.
  328. /// defaultLeafBranch: offset of the next instruction to be branched after
  329. /// the set of case instructions under investigation
  330. ///--------------------------------------------------------------------------------------
  331. void
  332. SwitchIRBuilder::BuildBinaryTraverseInstr(int start, int end, uint32 defaultLeafBranch)
  333. {
  334. int mid;
  335. if (start > end)
  336. {
  337. return;
  338. }
  339. if (end - start <= CONFIG_FLAG(MaxLinearIntCaseCount) - 1) // -1 for handling zero index as the base
  340. {
  341. //if only 3 elements, then do linear search on the elements
  342. BuildLinearTraverseInstr(start, end, defaultLeafBranch);
  343. return;
  344. }
  345. mid = start + ((end - start + 1) / 2);
  346. CaseNode* midNode = m_caseNodes->Item(mid);
  347. CaseNode* startNode = m_caseNodes->Item(start);
  348. // if the value that we are switching on is greater than the start case value
  349. // then we branch right to the right half of the binary search
  350. IR::BranchInstr* caseInstr = startNode->GetCaseInstr();
  351. IR::BranchInstr* branchInstr = IR::BranchInstr::New(m_geOp, nullptr, caseInstr->GetSrc1(), midNode->GetLowerBound(), m_func);
  352. branchInstr->m_isSwitchBr = true;
  353. m_adapter->AddBranchInstr(branchInstr, startNode->GetOffset(), midNode->GetOffset(), true);
  354. BuildBinaryTraverseInstr(start, mid - 1, defaultLeafBranch);
  355. BuildBinaryTraverseInstr(mid, end, defaultLeafBranch);
  356. }
  357. ///------------------------------------------------------------------------------------------
  358. ///
  359. /// SwitchIRBuilder::BuildEmptyCasesInstr
  360. ///
  361. /// Build IR instructions for Empty consecutive case statements (with only one case block).
  362. /// defaultLeafBranch: offset of the next instruction to be branched after
  363. /// the set of case instructions under investigation
  364. ///
  365. ///------------------------------------------------------------------------------------------
  366. void
  367. SwitchIRBuilder::BuildEmptyCasesInstr(CaseNode* caseNode, uint32 fallThrOffset)
  368. {
  369. IR::BranchInstr* branchInstr;
  370. IR::Opnd* src1Opnd;
  371. src1Opnd = caseNode->GetCaseInstr()->GetSrc1();
  372. AssertMsg(caseNode->GetLowerBound() != caseNode->GetUpperBound(), "The upper bound and lower bound should not be the same");
  373. //Generate <lb instruction
  374. branchInstr = IR::BranchInstr::New(m_ltOp, nullptr, src1Opnd, caseNode->GetLowerBound(), m_func);
  375. branchInstr->m_isSwitchBr = true;
  376. m_adapter->AddBranchInstr(branchInstr, caseNode->GetOffset(), fallThrOffset, true);
  377. //Generate <=ub instruction
  378. branchInstr = IR::BranchInstr::New(m_leOp, nullptr, src1Opnd, caseNode->GetUpperBound(), m_func);
  379. branchInstr->m_isSwitchBr = true;
  380. m_adapter->AddBranchInstr(branchInstr, caseNode->GetOffset(), caseNode->GetTargetOffset(), true);
  381. BuildBailOnNotInteger();
  382. }
  383. ///----------------------------------------------------------------------------
  384. ///
  385. /// SwitchIRBuilder::BuildLinearTraverseInstr
  386. ///
  387. /// Build IR instr for case statements less than a threshold.
  388. /// defaultLeafBranch: offset of the next instruction to be branched after
  389. /// the set of case instructions under investigation
  390. ///
  391. ///----------------------------------------------------------------------------
  392. void
  393. SwitchIRBuilder::BuildLinearTraverseInstr(int start, int end, uint fallThrOffset)
  394. {
  395. Assert(fallThrOffset);
  396. for (int index = start; index <= end; index++)
  397. {
  398. CaseNode* currCaseNode = m_caseNodes->Item(index);
  399. bool dontBuildEmptyCases = false;
  400. if (currCaseNode->IsUpperBoundIntConst())
  401. {
  402. int lowerBoundCaseConstValue = currCaseNode->GetLowerBoundIntConst();
  403. int upperBoundCaseConstValue = currCaseNode->GetUpperBoundIntConst();
  404. if (lowerBoundCaseConstValue == upperBoundCaseConstValue)
  405. {
  406. dontBuildEmptyCases = true;
  407. }
  408. }
  409. else if (currCaseNode->IsUpperBoundStrConst())
  410. {
  411. dontBuildEmptyCases = true;
  412. }
  413. else
  414. {
  415. AssertMsg(false, "An integer/String CaseNode is required for BuildLinearTraverseInstr");
  416. }
  417. if (dontBuildEmptyCases)
  418. {
  419. // only if the instruction is not part of a cluster of empty consecutive case statements.
  420. m_adapter->AddBranchInstr(currCaseNode->GetCaseInstr(), currCaseNode->GetOffset(), currCaseNode->GetTargetOffset());
  421. }
  422. else
  423. {
  424. BuildEmptyCasesInstr(currCaseNode, fallThrOffset);
  425. }
  426. }
  427. // Adds an unconditional branch instruction at the end
  428. IR::BranchInstr* branchInstr = IR::BranchInstr::New(Js::OpCode::Br, nullptr, m_func);
  429. branchInstr->m_isSwitchBr = true;
  430. m_adapter->AddBranchInstr(branchInstr, Js::Constants::NoByteCodeOffset, fallThrOffset, true);
  431. }
  432. ///----------------------------------------------------------------------------
  433. ///
  434. /// SwitchIRBuilder::ResetCaseNodes
  435. ///
  436. /// Resets SwitchIRBuilder to begin building another switch statement.
  437. ///
  438. ///----------------------------------------------------------------------------
  439. void
  440. SwitchIRBuilder::ResetCaseNodes()
  441. {
  442. m_caseNodes->Clear();
  443. m_seenOnlySingleCharStrCaseNodes = true;
  444. }
  445. ////////////////////////////////////////////////////////////////////////////////////////////
  446. ///
  447. ///SwitchIRBuilder::BuildCaseBrInstr
  448. /// Generates the branch instructions to optimize the switch case execution flow
  449. /// -Sorts, Refines and generates instructions in binary traversal fashion
  450. ////////////////////////////////////////////////////////////////////////////////////////////
  451. void
  452. SwitchIRBuilder::BuildCaseBrInstr(uint32 targetOffset)
  453. {
  454. Assert(m_isAsmJs || m_profiledSwitchInstr);
  455. int start = 0;
  456. int end = m_caseNodes->Count() - 1;
  457. if (m_caseNodes->Count() <= CONFIG_FLAG(MaxLinearIntCaseCount))
  458. {
  459. BuildLinearTraverseInstr(start, end, targetOffset);
  460. ResetCaseNodes();
  461. return;
  462. }
  463. RefineCaseNodes();
  464. BuildOptimizedIntegerCaseInstrs(targetOffset);
  465. ResetCaseNodes(); // clear the list for the next new set of integers - or for a new switch case statement
  466. //optimization is definitely performed when the number of cases is greater than the threshold
  467. if (end - start > CONFIG_FLAG(MaxLinearIntCaseCount) - 1) // -1 for handling zero index as the base
  468. {
  469. BuildBailOnNotInteger();
  470. }
  471. }
  472. ////////////////////////////////////////////////////////////////////////////////////////////
  473. ///
  474. ///SwitchIRBuilder::BuildOptimizedIntegerCaseInstrs
  475. /// Identify chunks of integers cases(consecutive integers)
  476. /// Apply jump table or binary traversal based on the density of the case arms
  477. ///
  478. ////////////////////////////////////////////////////////////////////////////////////////////
  479. void
  480. SwitchIRBuilder::BuildOptimizedIntegerCaseInstrs(uint32 targetOffset)
  481. {
  482. int startjmpTableIndex = 0;
  483. int endjmpTableIndex = 0;
  484. int startBinaryTravIndex = 0;
  485. int endBinaryTravIndex = 0;
  486. IR::MultiBranchInstr * multiBranchInstr = nullptr;
  487. /*
  488. * Algorithm to find chunks of consecutive integers in a given set of case arms(sorted)
  489. * -Start and end indices for jump table and binary tree are maintained.
  490. * -The corresponding start and end indices indicate that they are suitable candidate for their respective category(binaryTree/jumpTable)
  491. * -All holes are filled with an offset corresponding to the default fallthrough instruction and each block is filled with an offset corresponding to the start of the next block
  492. * A Block here refers either to a jump table or to a binary tree.
  493. * -Blocks of BinaryTrav/Jump table are traversed in a linear fashion.
  494. **/
  495. for (int currentIndex = 0; currentIndex < m_caseNodes->Count() - 1; currentIndex++)
  496. {
  497. int nextIndex = currentIndex + 1;
  498. //Check if there is no missing value between subsequent case arms
  499. if (m_caseNodes->Item(currentIndex)->GetUpperBoundIntConst() + 1 != m_caseNodes->Item(nextIndex)->GetUpperBoundIntConst())
  500. {
  501. //value of the case nodes are guaranteed to be 32 bits or less than 32bits at this point(if it is more, the Switch Opt will not kick in)
  502. Assert(nextIndex == endjmpTableIndex + 1);
  503. int64 speculatedEndJmpCaseValue = m_caseNodes->Item(nextIndex)->GetUpperBoundIntConst();
  504. int64 endJmpCaseValue = m_caseNodes->Item(endjmpTableIndex)->GetUpperBoundIntConst();
  505. int64 startJmpCaseValue = m_caseNodes->Item(startjmpTableIndex)->GetUpperBoundIntConst();
  506. int64 speculatedJmpTableSize = speculatedEndJmpCaseValue - startJmpCaseValue + 1;
  507. int64 jmpTableSize = endJmpCaseValue - startJmpCaseValue + 1;
  508. int numFilledEntries = nextIndex - startjmpTableIndex + 1;
  509. //Checks if the % of filled entries(unique targets from the case arms) in the jump table is within the threshold
  510. if (speculatedJmpTableSize != 0 && ((numFilledEntries)* 100 / speculatedJmpTableSize) < (100 - CONFIG_FLAG(SwitchOptHolesThreshold)))
  511. {
  512. if (jmpTableSize >= CONFIG_FLAG(MinSwitchJumpTableSize))
  513. {
  514. uint32 fallThrOffset = m_caseNodes->Item(endjmpTableIndex)->GetOffset();
  515. TryBuildBinaryTreeOrMultiBrForSwitchInts(multiBranchInstr, fallThrOffset, startjmpTableIndex, endjmpTableIndex, startBinaryTravIndex, targetOffset);
  516. //Reset start/end indices of BinaryTrav to the next index.
  517. startBinaryTravIndex = nextIndex;
  518. endBinaryTravIndex = nextIndex;
  519. }
  520. //Reset start/end indices of the jump table to the next index.
  521. startjmpTableIndex = nextIndex;
  522. endjmpTableIndex = nextIndex;
  523. }
  524. else
  525. {
  526. endjmpTableIndex++;
  527. }
  528. }
  529. else
  530. {
  531. endjmpTableIndex++;
  532. }
  533. }
  534. int64 endJmpCaseValue = m_caseNodes->Item(endjmpTableIndex)->GetUpperBoundIntConst();
  535. int64 startJmpCaseValue = m_caseNodes->Item(startjmpTableIndex)->GetUpperBoundIntConst();
  536. int64 jmpTableSize = endJmpCaseValue - startJmpCaseValue + 1;
  537. if (jmpTableSize < CONFIG_FLAG(MinSwitchJumpTableSize))
  538. {
  539. endBinaryTravIndex = endjmpTableIndex;
  540. BuildBinaryTraverseInstr(startBinaryTravIndex, endBinaryTravIndex, targetOffset);
  541. if (multiBranchInstr)
  542. {
  543. FixUpMultiBrJumpTable(multiBranchInstr, multiBranchInstr->GetNextRealInstr()->GetByteCodeOffset());
  544. multiBranchInstr = nullptr;
  545. }
  546. }
  547. else
  548. {
  549. uint32 fallthrOffset = m_caseNodes->Item(endjmpTableIndex)->GetOffset();
  550. TryBuildBinaryTreeOrMultiBrForSwitchInts(multiBranchInstr, fallthrOffset, startjmpTableIndex, endjmpTableIndex, startBinaryTravIndex, targetOffset);
  551. FixUpMultiBrJumpTable(multiBranchInstr, targetOffset);
  552. }
  553. }
  554. ////////////////////////////////////////////////////////////////////////////////////////////
  555. ///
  556. ///SwitchIRBuilder::TryBuildBinaryTreeOrMultiBrForSwitchInts
  557. /// Builds a range of integer cases into either a binary tree or jump table.
  558. ///
  559. ////////////////////////////////////////////////////////////////////////////////////////////
  560. void
  561. SwitchIRBuilder::TryBuildBinaryTreeOrMultiBrForSwitchInts(IR::MultiBranchInstr * &multiBranchInstr, uint32 fallthrOffset, int startjmpTableIndex, int endjmpTableIndex, int startBinaryTravIndex, uint32 defaultTargetOffset)
  562. {
  563. int endBinaryTravIndex = startjmpTableIndex;
  564. //Try Building Binary tree, if there are available case arms, as indicated by the boundary offsets
  565. if (endBinaryTravIndex != startBinaryTravIndex)
  566. {
  567. endBinaryTravIndex = startjmpTableIndex - 1;
  568. BuildBinaryTraverseInstr(startBinaryTravIndex, endBinaryTravIndex, fallthrOffset);
  569. //Fix up the fallthrOffset for the previous multiBrInstr, if one existed
  570. //Example => Binary tree immediately succeeds a MultiBr Instr
  571. if (multiBranchInstr)
  572. {
  573. FixUpMultiBrJumpTable(multiBranchInstr, multiBranchInstr->GetNextRealInstr()->GetByteCodeOffset());
  574. multiBranchInstr = nullptr;
  575. }
  576. }
  577. //Fix up the fallthrOffset for the previous multiBrInstr, if one existed
  578. //Example -> A multiBr can be followed by another multiBr
  579. if (multiBranchInstr)
  580. {
  581. FixUpMultiBrJumpTable(multiBranchInstr, fallthrOffset);
  582. multiBranchInstr = nullptr;
  583. }
  584. multiBranchInstr = BuildMultiBrCaseInstrForInts(startjmpTableIndex, endjmpTableIndex, defaultTargetOffset);
  585. //We currently assign the offset of the multiBr Instr same as the offset of the last instruction of the case arm selected for building the jump table
  586. //AssertMsg(m_lastInstr->GetByteCodeOffset() == fallthrOffset, "The fallthrough offset to the multi branch instruction is wrong");
  587. }
  588. ////////////////////////////////////////////////////////////////////////////////////////////
  589. ///
  590. ///SwitchIRBuilder::FixUpMultiBrJumpTable
  591. /// Creates Reloc Records for the branch instructions that are generated with the MultiBr Instr
  592. /// Also calls FixMultiBrDefaultTarget to fix the target offset in the MultiBr Instr
  593. ////////////////////////////////////////////////////////////////////////////////////////////
  594. void
  595. SwitchIRBuilder::FixUpMultiBrJumpTable(IR::MultiBranchInstr * multiBranchInstr, uint32 targetOffset)
  596. {
  597. multiBranchInstr->FixMultiBrDefaultTarget(targetOffset);
  598. uint32 offset = multiBranchInstr->GetByteCodeOffset();
  599. IR::Instr * subInstr = multiBranchInstr->GetPrevRealInstr();
  600. IR::Instr * upperBoundCheckInstr = subInstr->GetPrevRealInstr();
  601. IR::Instr * lowerBoundCheckInstr = upperBoundCheckInstr->GetPrevRealInstr();
  602. AssertMsg(subInstr->m_opcode == m_subOp, "Missing Offset Calculation instruction");
  603. AssertMsg(upperBoundCheckInstr->IsBranchInstr() && lowerBoundCheckInstr->IsBranchInstr(), "Invalid boundary check instructions");
  604. AssertMsg(upperBoundCheckInstr->m_opcode == m_gtOp && lowerBoundCheckInstr->m_opcode == m_ltOp, "Invalid boundary check instructions");
  605. m_adapter->CreateRelocRecord(upperBoundCheckInstr->AsBranchInstr(), offset, targetOffset, true);
  606. m_adapter->CreateRelocRecord(lowerBoundCheckInstr->AsBranchInstr(), offset, targetOffset, true);
  607. }
  608. ////////////////////////////////////////////////////////////////////////////////////////////
  609. ///
  610. ///SwitchIRBuilder::BuildBailOnNotInteger
  611. /// Creates the necessary bailout for a switch case that expected an integer expression
  612. /// but was not.
  613. ///
  614. ////////////////////////////////////////////////////////////////////////////////////////////
  615. void
  616. SwitchIRBuilder::BuildBailOnNotInteger()
  617. {
  618. if (!m_switchOptBuildBail)
  619. {
  620. return;
  621. }
  622. m_adapter->ConvertToBailOut(m_profiledSwitchInstr, IR::BailOutExpectingInteger);
  623. m_switchOptBuildBail = false; // falsify this to avoid generating extra BailOuts when optimization is done again on the same switch statement
  624. #if ENABLE_DEBUG_CONFIG_OPTIONS
  625. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  626. #endif
  627. PHASE_PRINT_TESTTRACE1(Js::SwitchOptPhase, _u("Func %s, Switch %d:Optimized for Integers\n"),
  628. m_profiledSwitchInstr->m_func->GetDebugNumberSet(debugStringBuffer),
  629. m_profiledSwitchInstr->AsProfiledInstr()->u.profileId);
  630. }
  631. ////////////////////////////////////////////////////////////////////////////////////////////
  632. ///
  633. ///SwitchIRBuilder::BuildBailOnNotString
  634. /// Creates the necessary bailout for a switch case that expected a string expression
  635. /// but was not.
  636. ///
  637. ////////////////////////////////////////////////////////////////////////////////////////////
  638. void
  639. SwitchIRBuilder::BuildBailOnNotString()
  640. {
  641. if (!m_switchOptBuildBail)
  642. {
  643. return;
  644. }
  645. m_adapter->ConvertToBailOut(m_profiledSwitchInstr, IR::BailOutExpectingString);
  646. m_switchOptBuildBail = false; // falsify this to avoid generating extra BailOuts when optimization is done again on the same switch statement
  647. #if ENABLE_DEBUG_CONFIG_OPTIONS
  648. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  649. #endif
  650. PHASE_PRINT_TESTTRACE1(Js::SwitchOptPhase, _u("Func %s, Switch %d:Optimized for Strings\n"),
  651. m_profiledSwitchInstr->m_func->GetDebugNumberSet(debugStringBuffer),
  652. m_profiledSwitchInstr->AsProfiledInstr()->u.profileId);
  653. }
  654. ///----------------------------------------------------------------------------
  655. ///
  656. /// SwitchIRBuilder::TestAndAddStringCaseConst
  657. ///
  658. /// Checks if strConstSwitchCases already has the string constant
  659. /// - if yes, then return true
  660. /// - if no, then add the string to the list 'strConstSwitchCases' and return false
  661. ///
  662. ///----------------------------------------------------------------------------
  663. bool
  664. SwitchIRBuilder::TestAndAddStringCaseConst(JITJavascriptString * str)
  665. {
  666. Assert(m_strConstSwitchCases);
  667. if (m_strConstSwitchCases->Contains(str))
  668. {
  669. return true;
  670. }
  671. else
  672. {
  673. m_strConstSwitchCases->Add(str);
  674. return false;
  675. }
  676. }
  677. ///----------------------------------------------------------------------------
  678. ///
  679. /// SwitchIRBuilder::BuildMultiBrCaseInstrForStrings
  680. ///
  681. /// Build Multi Branch IR instr for a set of Case statements(String case arms).
  682. /// - Builds the multibranch target and adds the instruction
  683. ///
  684. ///----------------------------------------------------------------------------
  685. void
  686. SwitchIRBuilder::BuildMultiBrCaseInstrForStrings(uint32 targetOffset)
  687. {
  688. Assert(m_caseNodes && m_caseNodes->Count() && m_profiledSwitchInstr && !m_isAsmJs);
  689. if (m_caseNodes->Count() < CONFIG_FLAG(MaxLinearStringCaseCount))
  690. {
  691. int start = 0;
  692. int end = m_caseNodes->Count() - 1;
  693. BuildLinearTraverseInstr(start, end, targetOffset);
  694. ResetCaseNodes();
  695. return;
  696. }
  697. IR::Opnd * srcOpnd = m_caseNodes->Item(0)->GetCaseInstr()->GetSrc1(); // Src1 is same in all the caseNodes
  698. IR::MultiBranchInstr * multiBranchInstr = IR::MultiBranchInstr::New(Js::OpCode::MultiBr, srcOpnd, m_func);
  699. uint32 lastCaseOffset = m_caseNodes->Item(m_caseNodes->Count() - 1)->GetOffset();
  700. uint caseCount = m_caseNodes->Count();
  701. bool generateDictionary = true;
  702. char16 minChar = USHORT_MAX;
  703. char16 maxChar = 0;
  704. // Either the jump table is within the limit (<= 128) or it is dense (<= 2 * case Count)
  705. uint const maxJumpTableSize = max<uint>(CONFIG_FLAG(MaxSingleCharStrJumpTableSize), CONFIG_FLAG(MaxSingleCharStrJumpTableRatio) * caseCount);
  706. if (this->m_seenOnlySingleCharStrCaseNodes)
  707. {
  708. generateDictionary = false;
  709. for (uint i = 0; i < caseCount; i++)
  710. {
  711. JITJavascriptString * str = m_caseNodes->Item(i)->GetUpperBoundStringConstLocal();
  712. Assert(str->GetLength() == 1);
  713. char16 currChar = str->GetString()[0];
  714. minChar = min(minChar, currChar);
  715. maxChar = max(maxChar, currChar);
  716. if ((uint)(maxChar - minChar) > maxJumpTableSize)
  717. {
  718. generateDictionary = true;
  719. break;
  720. }
  721. }
  722. }
  723. if (generateDictionary)
  724. {
  725. multiBranchInstr->CreateBranchTargetsAndSetDefaultTarget(caseCount, IR::MultiBranchInstr::StrDictionary, targetOffset);
  726. //Adding normal cases to the instruction (except the default case, which we do it later)
  727. for (uint i = 0; i < caseCount; i++)
  728. {
  729. JITJavascriptString * str = m_caseNodes->Item(i)->GetUpperBoundStringConstLocal();
  730. uint32 caseTargetOffset = m_caseNodes->Item(i)->GetTargetOffset();
  731. multiBranchInstr->AddtoDictionary(caseTargetOffset, str, m_caseNodes->Item(i)->GetUpperBoundStrConst());
  732. }
  733. }
  734. else
  735. {
  736. // If we are only going to save 16 entries, just start from 0 so we don't have to subtract
  737. if (minChar < 16)
  738. {
  739. minChar = 0;
  740. }
  741. multiBranchInstr->m_baseCaseValue = minChar;
  742. multiBranchInstr->m_lastCaseValue = maxChar;
  743. uint jumpTableSize = maxChar - minChar + 1;
  744. multiBranchInstr->CreateBranchTargetsAndSetDefaultTarget(jumpTableSize, IR::MultiBranchInstr::SingleCharStrJumpTable, targetOffset);
  745. for (uint i = 0; i < jumpTableSize; i++)
  746. {
  747. // Initialize all the entries to the default target first.
  748. multiBranchInstr->AddtoJumpTable(targetOffset, i);
  749. }
  750. //Adding normal cases to the instruction (except the default case, which we do it later)
  751. for (uint i = 0; i < caseCount; i++)
  752. {
  753. JITJavascriptString * str = m_caseNodes->Item(i)->GetUpperBoundStringConstLocal();
  754. Assert(str->GetLength() == 1);
  755. uint32 caseTargetOffset = m_caseNodes->Item(i)->GetTargetOffset();
  756. multiBranchInstr->AddtoJumpTable(caseTargetOffset, str->GetString()[0] - minChar);
  757. }
  758. }
  759. multiBranchInstr->m_isSwitchBr = true;
  760. m_adapter->CreateRelocRecord(multiBranchInstr, lastCaseOffset, targetOffset);
  761. m_adapter->AddInstr(multiBranchInstr, lastCaseOffset);
  762. BuildBailOnNotString();
  763. ResetCaseNodes();
  764. }
  765. ///----------------------------------------------------------------------------
  766. ///
  767. /// SwitchIRBuilder::BuildMultiBrCaseInstrForInts
  768. ///
  769. /// Build Multi Branch IR instr for a set of Case statements(Integer case arms).
  770. /// - Builds the multibranch target and adds the instruction
  771. /// - Add boundary checks for the jump table and calculates the offset in the jump table
  772. ///
  773. ///----------------------------------------------------------------------------
  774. IR::MultiBranchInstr *
  775. SwitchIRBuilder::BuildMultiBrCaseInstrForInts(uint32 start, uint32 end, uint32 targetOffset)
  776. {
  777. Assert(m_caseNodes && m_caseNodes->Count() && (m_profiledSwitchInstr || m_isAsmJs));
  778. IR::Opnd * srcOpnd = m_caseNodes->Item(start)->GetCaseInstr()->GetSrc1(); // Src1 is same in all the caseNodes
  779. IR::MultiBranchInstr * multiBranchInstr = IR::MultiBranchInstr::New(Js::OpCode::MultiBr, srcOpnd, m_func);
  780. uint32 lastCaseOffset = m_caseNodes->Item(end)->GetOffset();
  781. int32 baseCaseValue = m_caseNodes->Item(start)->GetLowerBoundIntConst();
  782. int32 lastCaseValue = m_caseNodes->Item(end)->GetUpperBoundIntConst();
  783. multiBranchInstr->m_baseCaseValue = baseCaseValue;
  784. multiBranchInstr->m_lastCaseValue = lastCaseValue;
  785. uint32 jmpTableSize = lastCaseValue - baseCaseValue + 1;
  786. multiBranchInstr->CreateBranchTargetsAndSetDefaultTarget(jmpTableSize, IR::MultiBranchInstr::IntJumpTable, targetOffset);
  787. int caseIndex = end;
  788. int lowerBoundCaseConstValue = 0;
  789. int upperBoundCaseConstValue = 0;
  790. uint32 caseTargetOffset = 0;
  791. for (int jmpIndex = jmpTableSize - 1; jmpIndex >= 0; jmpIndex--)
  792. {
  793. if (caseIndex >= 0 && jmpIndex == m_caseNodes->Item(caseIndex)->GetUpperBoundIntConst() - baseCaseValue)
  794. {
  795. lowerBoundCaseConstValue = m_caseNodes->Item(caseIndex)->GetLowerBoundIntConst();
  796. upperBoundCaseConstValue = m_caseNodes->Item(caseIndex)->GetUpperBoundIntConst();
  797. caseTargetOffset = m_caseNodes->Item(caseIndex--)->GetTargetOffset();
  798. multiBranchInstr->AddtoJumpTable(caseTargetOffset, jmpIndex);
  799. }
  800. else
  801. {
  802. if (jmpIndex >= lowerBoundCaseConstValue - baseCaseValue && jmpIndex <= upperBoundCaseConstValue - baseCaseValue)
  803. {
  804. multiBranchInstr->AddtoJumpTable(caseTargetOffset, jmpIndex);
  805. }
  806. else
  807. {
  808. multiBranchInstr->AddtoJumpTable(targetOffset, jmpIndex);
  809. }
  810. }
  811. }
  812. //Insert Boundary checks for the jump table - Reloc records are created later for these instructions (in FixUpMultiBrJumpTable())
  813. IR::BranchInstr* lowerBoundChk = IR::BranchInstr::New(m_ltOp, nullptr, srcOpnd, m_caseNodes->Item(start)->GetLowerBound(), m_func);
  814. lowerBoundChk->m_isSwitchBr = true;
  815. m_adapter->AddInstr(lowerBoundChk, lastCaseOffset);
  816. IR::BranchInstr* upperBoundChk = IR::BranchInstr::New(m_gtOp, nullptr, srcOpnd, m_caseNodes->Item(end)->GetUpperBound(), m_func);
  817. upperBoundChk->m_isSwitchBr = true;
  818. m_adapter->AddInstr(upperBoundChk, lastCaseOffset);
  819. //Calculate the offset inside the jump table using the switch operand value and the lowest case arm value (in the considered set of consecutive integers)
  820. IR::IntConstOpnd *baseCaseValueOpnd = IR::IntConstOpnd::New(multiBranchInstr->m_baseCaseValue, TyInt32, m_func);
  821. IR::RegOpnd * offset = IR::RegOpnd::New(TyVar, m_func);
  822. IR::Instr * subInstr = IR::Instr::New(m_subOp, offset, multiBranchInstr->GetSrc1(), baseCaseValueOpnd, m_func);
  823. //We are sure that the SUB operation will not overflow the int range - It will either bailout or will not optimize if it finds a number that is out of the int range.
  824. subInstr->ignoreIntOverflow = true;
  825. m_adapter->AddInstr(subInstr, lastCaseOffset);
  826. //Source of the multi branch instr will now have the calculated offset
  827. multiBranchInstr->UnlinkSrc1();
  828. multiBranchInstr->SetSrc1(offset);
  829. multiBranchInstr->m_isSwitchBr = true;
  830. m_adapter->AddInstr(multiBranchInstr, lastCaseOffset);
  831. m_adapter->CreateRelocRecord(multiBranchInstr, lastCaseOffset, targetOffset);
  832. return multiBranchInstr;
  833. }