SwitchIRBuilder.cpp 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984
  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(offset);
  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 (isIntConst
  201. && GlobOpt::IsSwitchOptEnabledForIntTypeSpec(m_func->GetTopFunc())
  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 (isStrConst
  208. && GlobOpt::IsSwitchOptEnabled(m_func->GetTopFunc())
  209. && TestAndAddStringCaseConst(JITJavascriptString::FromVar(sym->GetConstAddress(true))))
  210. {
  211. // We've already seen a case statement with the same string const value. No need to emit anything for this.
  212. return;
  213. }
  214. branchInstr = IR::BranchInstr::New(m_eqOp, nullptr, src1Opnd, src2Opnd, m_func);
  215. branchInstr->m_isSwitchBr = true;
  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. // TODO: support switch opt when breaking out of loops
  223. if (!m_func->IsLoopBody() || (targetOffset < m_func->m_workItem->GetLoopHeader()->endOffset && targetOffset >= m_func->m_workItem->GetLoopHeader()->startOffset))
  224. {
  225. if (m_switchIntDynProfile && isIntConst && GlobOpt::IsSwitchOptEnabledForIntTypeSpec(m_func->GetTopFunc()))
  226. {
  227. CaseNode* caseNode = JitAnew(m_tempAlloc, CaseNode, branchInstr, offset, targetOffset, src2Opnd);
  228. m_caseNodes->Add(caseNode);
  229. return;
  230. }
  231. else if (m_switchStrDynProfile && isStrConst && GlobOpt::IsSwitchOptEnabled(m_func->GetTopFunc()))
  232. {
  233. CaseNode* caseNode = JitAnew(m_tempAlloc, CaseNode, branchInstr, offset, targetOffset, src2Opnd);
  234. m_caseNodes->Add(caseNode);
  235. m_seenOnlySingleCharStrCaseNodes = m_seenOnlySingleCharStrCaseNodes && caseNode->GetUpperBoundStringConstLocal()->GetLength() == 1;
  236. return;
  237. }
  238. }
  239. // Otherwise, there are no optimizations to defer, so add the branch for
  240. // this case instruction now
  241. FlushCases(offset);
  242. m_adapter->AddBranchInstr(branchInstr, offset, targetOffset);
  243. }
  244. ///----------------------------------------------------------------------------
  245. ///
  246. /// SwitchIRBuilder::FlushCases
  247. ///
  248. /// Called when a scenario for which optimized switch cases cannot be
  249. /// generated occurs, and generates optimized branches for all cases that
  250. /// have been stored up to this point
  251. ///
  252. ///----------------------------------------------------------------------------
  253. void
  254. SwitchIRBuilder::FlushCases(uint32 targetOffset)
  255. {
  256. if (m_caseNodes->Empty())
  257. {
  258. return;
  259. }
  260. if (m_switchIntDynProfile)
  261. {
  262. BuildCaseBrInstr(targetOffset);
  263. }
  264. else if (m_switchStrDynProfile)
  265. {
  266. BuildMultiBrCaseInstrForStrings(targetOffset);
  267. }
  268. else
  269. {
  270. Assert(false);
  271. }
  272. }
  273. ///----------------------------------------------------------------------------
  274. ///
  275. /// SwitchIRBuilder::RefineCaseNodes
  276. ///
  277. /// Filter IR instructions for case statements that contain no case blocks.
  278. /// Also sets upper bound and lower bound for case instructions that has a
  279. /// consecutive set of cases with just one case block.
  280. ///----------------------------------------------------------------------------
  281. void
  282. SwitchIRBuilder::RefineCaseNodes()
  283. {
  284. m_caseNodes->Sort();
  285. CaseNodeList * tmpCaseNodes = CaseNodeList::New(m_tempAlloc);
  286. for (int currCaseIndex = 1; currCaseIndex < m_caseNodes->Count(); currCaseIndex++)
  287. {
  288. CaseNode * prevCaseNode = m_caseNodes->Item(currCaseIndex - 1);
  289. CaseNode * currCaseNode = m_caseNodes->Item(currCaseIndex);
  290. uint32 prevCaseTargetOffset = prevCaseNode->GetTargetOffset();
  291. uint32 currCaseTargetOffset = currCaseNode->GetTargetOffset();
  292. int prevCaseConstValue = prevCaseNode->GetUpperBoundIntConst();
  293. int currCaseConstValue = currCaseNode->GetUpperBoundIntConst();
  294. /*To handle empty case statements with/without repetition*/
  295. if (prevCaseTargetOffset == currCaseTargetOffset &&
  296. (prevCaseConstValue + 1 == currCaseConstValue || prevCaseConstValue == currCaseConstValue))
  297. {
  298. m_caseNodes->Item(currCaseIndex)->SetLowerBound(prevCaseNode->GetLowerBound());
  299. }
  300. else
  301. {
  302. if (tmpCaseNodes->Count() != 0)
  303. {
  304. int lastTmpCaseConstValue = tmpCaseNodes->Item(tmpCaseNodes->Count() - 1)->GetUpperBoundIntConst();
  305. /*To handle duplicate non empty case statements*/
  306. if (lastTmpCaseConstValue != prevCaseConstValue)
  307. {
  308. tmpCaseNodes->Add(prevCaseNode);
  309. }
  310. }
  311. else
  312. {
  313. tmpCaseNodes->Add(prevCaseNode); //Adding for the first time in tmpCaseNodes
  314. }
  315. }
  316. }
  317. //Adds the last caseNode in the caseNodes list.
  318. tmpCaseNodes->Add(m_caseNodes->Item(m_caseNodes->Count() - 1));
  319. m_caseNodes = tmpCaseNodes;
  320. }
  321. ///--------------------------------------------------------------------------------------
  322. ///
  323. /// SwitchIRBuilder::BuildBinaryTraverseInstr
  324. ///
  325. /// Build IR instructions for case statements in a binary search traversal fashion.
  326. /// defaultLeafBranch: offset of the next instruction to be branched after
  327. /// the set of case instructions under investigation
  328. ///--------------------------------------------------------------------------------------
  329. void
  330. SwitchIRBuilder::BuildBinaryTraverseInstr(int start, int end, uint32 defaultLeafBranch)
  331. {
  332. int mid;
  333. if (start > end)
  334. {
  335. return;
  336. }
  337. if (end - start <= CONFIG_FLAG(MaxLinearIntCaseCount) - 1) // -1 for handling zero index as the base
  338. {
  339. //if only 3 elements, then do linear search on the elements
  340. BuildLinearTraverseInstr(start, end, defaultLeafBranch);
  341. return;
  342. }
  343. mid = start + ((end - start + 1) / 2);
  344. CaseNode* midNode = m_caseNodes->Item(mid);
  345. CaseNode* startNode = m_caseNodes->Item(start);
  346. // if the value that we are switching on is greater than the start case value
  347. // then we branch right to the right half of the binary search
  348. IR::BranchInstr* caseInstr = startNode->GetCaseInstr();
  349. IR::BranchInstr* branchInstr = IR::BranchInstr::New(m_geOp, nullptr, caseInstr->GetSrc1(), midNode->GetLowerBound(), m_func);
  350. branchInstr->m_isSwitchBr = true;
  351. m_adapter->AddBranchInstr(branchInstr, startNode->GetOffset(), midNode->GetOffset(), true);
  352. BuildBinaryTraverseInstr(start, mid - 1, defaultLeafBranch);
  353. BuildBinaryTraverseInstr(mid, end, defaultLeafBranch);
  354. }
  355. ///------------------------------------------------------------------------------------------
  356. ///
  357. /// SwitchIRBuilder::BuildEmptyCasesInstr
  358. ///
  359. /// Build IR instructions for Empty consecutive case statements (with only one case block).
  360. /// defaultLeafBranch: offset of the next instruction to be branched after
  361. /// the set of case instructions under investigation
  362. ///
  363. ///------------------------------------------------------------------------------------------
  364. void
  365. SwitchIRBuilder::BuildEmptyCasesInstr(CaseNode* caseNode, uint32 fallThrOffset)
  366. {
  367. IR::BranchInstr* branchInstr;
  368. IR::Opnd* src1Opnd;
  369. src1Opnd = caseNode->GetCaseInstr()->GetSrc1();
  370. AssertMsg(caseNode->GetLowerBound() != caseNode->GetUpperBound(), "The upper bound and lower bound should not be the same");
  371. //Generate <lb instruction
  372. branchInstr = IR::BranchInstr::New(m_ltOp, nullptr, src1Opnd, caseNode->GetLowerBound(), m_func);
  373. branchInstr->m_isSwitchBr = true;
  374. m_adapter->AddBranchInstr(branchInstr, caseNode->GetOffset(), fallThrOffset, true);
  375. //Generate <=ub instruction
  376. branchInstr = IR::BranchInstr::New(m_leOp, nullptr, src1Opnd, caseNode->GetUpperBound(), m_func);
  377. branchInstr->m_isSwitchBr = true;
  378. m_adapter->AddBranchInstr(branchInstr, caseNode->GetOffset(), caseNode->GetTargetOffset(), true);
  379. BuildBailOnNotInteger();
  380. }
  381. ///----------------------------------------------------------------------------
  382. ///
  383. /// SwitchIRBuilder::BuildLinearTraverseInstr
  384. ///
  385. /// Build IR instr for case statements less than a threshold.
  386. /// defaultLeafBranch: offset of the next instruction to be branched after
  387. /// the set of case instructions under investigation
  388. ///
  389. ///----------------------------------------------------------------------------
  390. void
  391. SwitchIRBuilder::BuildLinearTraverseInstr(int start, int end, uint fallThrOffset)
  392. {
  393. Assert(fallThrOffset);
  394. for (int index = start; index <= end; index++)
  395. {
  396. CaseNode* currCaseNode = m_caseNodes->Item(index);
  397. bool dontBuildEmptyCases = false;
  398. if (currCaseNode->IsUpperBoundIntConst())
  399. {
  400. int lowerBoundCaseConstValue = currCaseNode->GetLowerBoundIntConst();
  401. int upperBoundCaseConstValue = currCaseNode->GetUpperBoundIntConst();
  402. if (lowerBoundCaseConstValue == upperBoundCaseConstValue)
  403. {
  404. dontBuildEmptyCases = true;
  405. }
  406. }
  407. else if (currCaseNode->IsUpperBoundStrConst())
  408. {
  409. dontBuildEmptyCases = true;
  410. }
  411. else
  412. {
  413. AssertMsg(false, "An integer/String CaseNode is required for BuildLinearTraverseInstr");
  414. }
  415. if (dontBuildEmptyCases)
  416. {
  417. // only if the instruction is not part of a cluster of empty consecutive case statements.
  418. m_adapter->AddBranchInstr(currCaseNode->GetCaseInstr(), currCaseNode->GetOffset(), currCaseNode->GetTargetOffset());
  419. }
  420. else
  421. {
  422. BuildEmptyCasesInstr(currCaseNode, fallThrOffset);
  423. }
  424. }
  425. // Adds an unconditional branch instruction at the end
  426. IR::BranchInstr* branchInstr = IR::BranchInstr::New(Js::OpCode::Br, nullptr, m_func);
  427. branchInstr->m_isSwitchBr = true;
  428. m_adapter->AddBranchInstr(branchInstr, Js::Constants::NoByteCodeOffset, fallThrOffset, true);
  429. }
  430. ///----------------------------------------------------------------------------
  431. ///
  432. /// SwitchIRBuilder::ResetCaseNodes
  433. ///
  434. /// Resets SwitchIRBuilder to begin building another switch statement.
  435. ///
  436. ///----------------------------------------------------------------------------
  437. void
  438. SwitchIRBuilder::ResetCaseNodes()
  439. {
  440. m_caseNodes->Clear();
  441. m_seenOnlySingleCharStrCaseNodes = true;
  442. }
  443. ////////////////////////////////////////////////////////////////////////////////////////////
  444. ///
  445. ///SwitchIRBuilder::BuildCaseBrInstr
  446. /// Generates the branch instructions to optimize the switch case execution flow
  447. /// -Sorts, Refines and generates instructions in binary traversal fashion
  448. ////////////////////////////////////////////////////////////////////////////////////////////
  449. void
  450. SwitchIRBuilder::BuildCaseBrInstr(uint32 targetOffset)
  451. {
  452. Assert(m_isAsmJs || m_profiledSwitchInstr);
  453. int start = 0;
  454. int end = m_caseNodes->Count() - 1;
  455. if (m_caseNodes->Count() <= CONFIG_FLAG(MaxLinearIntCaseCount))
  456. {
  457. BuildLinearTraverseInstr(start, end, targetOffset);
  458. ResetCaseNodes();
  459. return;
  460. }
  461. RefineCaseNodes();
  462. BuildOptimizedIntegerCaseInstrs(targetOffset);
  463. ResetCaseNodes(); // clear the list for the next new set of integers - or for a new switch case statement
  464. //optimization is definitely performed when the number of cases is greater than the threshold
  465. if (end - start > CONFIG_FLAG(MaxLinearIntCaseCount) - 1) // -1 for handling zero index as the base
  466. {
  467. BuildBailOnNotInteger();
  468. }
  469. }
  470. ////////////////////////////////////////////////////////////////////////////////////////////
  471. ///
  472. ///SwitchIRBuilder::BuildOptimizedIntegerCaseInstrs
  473. /// Identify chunks of integers cases(consecutive integers)
  474. /// Apply jump table or binary traversal based on the density of the case arms
  475. ///
  476. ////////////////////////////////////////////////////////////////////////////////////////////
  477. void
  478. SwitchIRBuilder::BuildOptimizedIntegerCaseInstrs(uint32 targetOffset)
  479. {
  480. int startjmpTableIndex = 0;
  481. int endjmpTableIndex = 0;
  482. int startBinaryTravIndex = 0;
  483. int endBinaryTravIndex = 0;
  484. IR::MultiBranchInstr * multiBranchInstr = nullptr;
  485. /*
  486. * Algorithm to find chunks of consecutive integers in a given set of case arms(sorted)
  487. * -Start and end indices for jump table and binary tree are maintained.
  488. * -The corresponding start and end indices indicate that they are suitable candidate for their respective category(binaryTree/jumpTable)
  489. * -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
  490. * A Block here refers either to a jump table or to a binary tree.
  491. * -Blocks of BinaryTrav/Jump table are traversed in a linear fashion.
  492. **/
  493. for (int currentIndex = 0; currentIndex < m_caseNodes->Count() - 1; currentIndex++)
  494. {
  495. int nextIndex = currentIndex + 1;
  496. //Check if there is no missing value between subsequent case arms
  497. if (m_caseNodes->Item(currentIndex)->GetUpperBoundIntConst() + 1 != m_caseNodes->Item(nextIndex)->GetUpperBoundIntConst())
  498. {
  499. //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)
  500. Assert(nextIndex == endjmpTableIndex + 1);
  501. int64 speculatedEndJmpCaseValue = m_caseNodes->Item(nextIndex)->GetUpperBoundIntConst();
  502. int64 endJmpCaseValue = m_caseNodes->Item(endjmpTableIndex)->GetUpperBoundIntConst();
  503. int64 startJmpCaseValue = m_caseNodes->Item(startjmpTableIndex)->GetUpperBoundIntConst();
  504. int64 speculatedJmpTableSize = speculatedEndJmpCaseValue - startJmpCaseValue + 1;
  505. int64 jmpTableSize = endJmpCaseValue - startJmpCaseValue + 1;
  506. int numFilledEntries = nextIndex - startjmpTableIndex + 1;
  507. //Checks if the % of filled entries(unique targets from the case arms) in the jump table is within the threshold
  508. if (speculatedJmpTableSize != 0 && ((numFilledEntries)* 100 / speculatedJmpTableSize) < (100 - CONFIG_FLAG(SwitchOptHolesThreshold)))
  509. {
  510. if (jmpTableSize >= CONFIG_FLAG(MinSwitchJumpTableSize))
  511. {
  512. uint32 fallThrOffset = m_caseNodes->Item(endjmpTableIndex)->GetOffset();
  513. TryBuildBinaryTreeOrMultiBrForSwitchInts(multiBranchInstr, fallThrOffset, startjmpTableIndex, endjmpTableIndex, startBinaryTravIndex, targetOffset);
  514. //Reset start/end indices of BinaryTrav to the next index.
  515. startBinaryTravIndex = nextIndex;
  516. endBinaryTravIndex = nextIndex;
  517. }
  518. //Reset start/end indices of the jump table to the next index.
  519. startjmpTableIndex = nextIndex;
  520. endjmpTableIndex = nextIndex;
  521. }
  522. else
  523. {
  524. endjmpTableIndex++;
  525. }
  526. }
  527. else
  528. {
  529. endjmpTableIndex++;
  530. }
  531. }
  532. int64 endJmpCaseValue = m_caseNodes->Item(endjmpTableIndex)->GetUpperBoundIntConst();
  533. int64 startJmpCaseValue = m_caseNodes->Item(startjmpTableIndex)->GetUpperBoundIntConst();
  534. int64 jmpTableSize = endJmpCaseValue - startJmpCaseValue + 1;
  535. if (jmpTableSize < CONFIG_FLAG(MinSwitchJumpTableSize))
  536. {
  537. endBinaryTravIndex = endjmpTableIndex;
  538. BuildBinaryTraverseInstr(startBinaryTravIndex, endBinaryTravIndex, targetOffset);
  539. if (multiBranchInstr)
  540. {
  541. FixUpMultiBrJumpTable(multiBranchInstr, multiBranchInstr->GetNextRealInstr()->GetByteCodeOffset());
  542. multiBranchInstr = nullptr;
  543. }
  544. }
  545. else
  546. {
  547. uint32 fallthrOffset = m_caseNodes->Item(endjmpTableIndex)->GetOffset();
  548. TryBuildBinaryTreeOrMultiBrForSwitchInts(multiBranchInstr, fallthrOffset, startjmpTableIndex, endjmpTableIndex, startBinaryTravIndex, targetOffset);
  549. FixUpMultiBrJumpTable(multiBranchInstr, targetOffset);
  550. }
  551. }
  552. ////////////////////////////////////////////////////////////////////////////////////////////
  553. ///
  554. ///SwitchIRBuilder::TryBuildBinaryTreeOrMultiBrForSwitchInts
  555. /// Builds a range of integer cases into either a binary tree or jump table.
  556. ///
  557. ////////////////////////////////////////////////////////////////////////////////////////////
  558. void
  559. SwitchIRBuilder::TryBuildBinaryTreeOrMultiBrForSwitchInts(IR::MultiBranchInstr * &multiBranchInstr, uint32 fallthrOffset, int startjmpTableIndex, int endjmpTableIndex, int startBinaryTravIndex, uint32 defaultTargetOffset)
  560. {
  561. int endBinaryTravIndex = startjmpTableIndex;
  562. //Try Building Binary tree, if there are available case arms, as indicated by the boundary offsets
  563. if (endBinaryTravIndex != startBinaryTravIndex)
  564. {
  565. endBinaryTravIndex = startjmpTableIndex - 1;
  566. BuildBinaryTraverseInstr(startBinaryTravIndex, endBinaryTravIndex, fallthrOffset);
  567. //Fix up the fallthrOffset for the previous multiBrInstr, if one existed
  568. //Example => Binary tree immediately succeeds a MultiBr Instr
  569. if (multiBranchInstr)
  570. {
  571. FixUpMultiBrJumpTable(multiBranchInstr, multiBranchInstr->GetNextRealInstr()->GetByteCodeOffset());
  572. multiBranchInstr = nullptr;
  573. }
  574. }
  575. //Fix up the fallthrOffset for the previous multiBrInstr, if one existed
  576. //Example -> A multiBr can be followed by another multiBr
  577. if (multiBranchInstr)
  578. {
  579. FixUpMultiBrJumpTable(multiBranchInstr, fallthrOffset);
  580. multiBranchInstr = nullptr;
  581. }
  582. multiBranchInstr = BuildMultiBrCaseInstrForInts(startjmpTableIndex, endjmpTableIndex, defaultTargetOffset);
  583. //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
  584. //AssertMsg(m_lastInstr->GetByteCodeOffset() == fallthrOffset, "The fallthrough offset to the multi branch instruction is wrong");
  585. }
  586. ////////////////////////////////////////////////////////////////////////////////////////////
  587. ///
  588. ///SwitchIRBuilder::FixUpMultiBrJumpTable
  589. /// Creates Reloc Records for the branch instructions that are generated with the MultiBr Instr
  590. /// Also calls FixMultiBrDefaultTarget to fix the target offset in the MultiBr Instr
  591. ////////////////////////////////////////////////////////////////////////////////////////////
  592. void
  593. SwitchIRBuilder::FixUpMultiBrJumpTable(IR::MultiBranchInstr * multiBranchInstr, uint32 targetOffset)
  594. {
  595. multiBranchInstr->FixMultiBrDefaultTarget(targetOffset);
  596. uint32 offset = multiBranchInstr->GetByteCodeOffset();
  597. IR::Instr * subInstr = multiBranchInstr->GetPrevRealInstr();
  598. IR::Instr * upperBoundCheckInstr = subInstr->GetPrevRealInstr();
  599. IR::Instr * lowerBoundCheckInstr = upperBoundCheckInstr->GetPrevRealInstr();
  600. AssertMsg(subInstr->m_opcode == m_subOp, "Missing Offset Calculation instruction");
  601. AssertMsg(upperBoundCheckInstr->IsBranchInstr() && lowerBoundCheckInstr->IsBranchInstr(), "Invalid boundary check instructions");
  602. AssertMsg(upperBoundCheckInstr->m_opcode == m_gtOp && lowerBoundCheckInstr->m_opcode == m_ltOp, "Invalid boundary check instructions");
  603. m_adapter->CreateRelocRecord(upperBoundCheckInstr->AsBranchInstr(), offset, targetOffset, true);
  604. m_adapter->CreateRelocRecord(lowerBoundCheckInstr->AsBranchInstr(), offset, targetOffset, true);
  605. }
  606. ////////////////////////////////////////////////////////////////////////////////////////////
  607. ///
  608. ///SwitchIRBuilder::BuildBailOnNotInteger
  609. /// Creates the necessary bailout for a switch case that expected an integer expression
  610. /// but was not.
  611. ///
  612. ////////////////////////////////////////////////////////////////////////////////////////////
  613. void
  614. SwitchIRBuilder::BuildBailOnNotInteger()
  615. {
  616. if (!m_switchOptBuildBail)
  617. {
  618. return;
  619. }
  620. m_adapter->ConvertToBailOut(m_profiledSwitchInstr, IR::BailOutExpectingInteger);
  621. m_switchOptBuildBail = false; // falsify this to avoid generating extra BailOuts when optimization is done again on the same switch statement
  622. #if ENABLE_DEBUG_CONFIG_OPTIONS
  623. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  624. #endif
  625. PHASE_PRINT_TESTTRACE1(Js::SwitchOptPhase, _u("Func %s, Switch %d:Optimized for Integers\n"),
  626. m_profiledSwitchInstr->m_func->GetDebugNumberSet(debugStringBuffer),
  627. m_profiledSwitchInstr->AsProfiledInstr()->u.profileId);
  628. }
  629. ////////////////////////////////////////////////////////////////////////////////////////////
  630. ///
  631. ///SwitchIRBuilder::BuildBailOnNotString
  632. /// Creates the necessary bailout for a switch case that expected a string expression
  633. /// but was not.
  634. ///
  635. ////////////////////////////////////////////////////////////////////////////////////////////
  636. void
  637. SwitchIRBuilder::BuildBailOnNotString()
  638. {
  639. if (!m_switchOptBuildBail)
  640. {
  641. return;
  642. }
  643. m_adapter->ConvertToBailOut(m_profiledSwitchInstr, IR::BailOutExpectingString);
  644. m_switchOptBuildBail = false; // falsify this to avoid generating extra BailOuts when optimization is done again on the same switch statement
  645. #if ENABLE_DEBUG_CONFIG_OPTIONS
  646. char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
  647. #endif
  648. PHASE_PRINT_TESTTRACE1(Js::SwitchOptPhase, _u("Func %s, Switch %d:Optimized for Strings\n"),
  649. m_profiledSwitchInstr->m_func->GetDebugNumberSet(debugStringBuffer),
  650. m_profiledSwitchInstr->AsProfiledInstr()->u.profileId);
  651. }
  652. ///----------------------------------------------------------------------------
  653. ///
  654. /// SwitchIRBuilder::TestAndAddStringCaseConst
  655. ///
  656. /// Checks if strConstSwitchCases already has the string constant
  657. /// - if yes, then return true
  658. /// - if no, then add the string to the list 'strConstSwitchCases' and return false
  659. ///
  660. ///----------------------------------------------------------------------------
  661. bool
  662. SwitchIRBuilder::TestAndAddStringCaseConst(JITJavascriptString * str)
  663. {
  664. Assert(m_strConstSwitchCases);
  665. if (m_strConstSwitchCases->Contains(str))
  666. {
  667. return true;
  668. }
  669. else
  670. {
  671. m_strConstSwitchCases->Add(str);
  672. return false;
  673. }
  674. }
  675. ///----------------------------------------------------------------------------
  676. ///
  677. /// SwitchIRBuilder::BuildMultiBrCaseInstrForStrings
  678. ///
  679. /// Build Multi Branch IR instr for a set of Case statements(String case arms).
  680. /// - Builds the multibranch target and adds the instruction
  681. ///
  682. ///----------------------------------------------------------------------------
  683. void
  684. SwitchIRBuilder::BuildMultiBrCaseInstrForStrings(uint32 targetOffset)
  685. {
  686. Assert(m_caseNodes && m_caseNodes->Count() && m_profiledSwitchInstr && !m_isAsmJs);
  687. if (m_caseNodes->Count() < CONFIG_FLAG(MaxLinearStringCaseCount))
  688. {
  689. int start = 0;
  690. int end = m_caseNodes->Count() - 1;
  691. BuildLinearTraverseInstr(start, end, targetOffset);
  692. ResetCaseNodes();
  693. return;
  694. }
  695. IR::Opnd * srcOpnd = m_caseNodes->Item(0)->GetCaseInstr()->GetSrc1(); // Src1 is same in all the caseNodes
  696. IR::MultiBranchInstr * multiBranchInstr = IR::MultiBranchInstr::New(Js::OpCode::MultiBr, srcOpnd, m_func);
  697. uint32 lastCaseOffset = m_caseNodes->Item(m_caseNodes->Count() - 1)->GetOffset();
  698. uint caseCount = m_caseNodes->Count();
  699. bool generateDictionary = true;
  700. char16 minChar = USHORT_MAX;
  701. char16 maxChar = 0;
  702. // Either the jump table is within the limit (<= 128) or it is dense (<= 2 * case Count)
  703. uint const maxJumpTableSize = max<uint>(CONFIG_FLAG(MaxSingleCharStrJumpTableSize), CONFIG_FLAG(MaxSingleCharStrJumpTableRatio) * caseCount);
  704. if (this->m_seenOnlySingleCharStrCaseNodes)
  705. {
  706. generateDictionary = false;
  707. for (uint i = 0; i < caseCount; i++)
  708. {
  709. JITJavascriptString * str = m_caseNodes->Item(i)->GetUpperBoundStringConstLocal();
  710. Assert(str->GetLength() == 1);
  711. char16 currChar = str->GetString()[0];
  712. minChar = min(minChar, currChar);
  713. maxChar = max(maxChar, currChar);
  714. if ((uint)(maxChar - minChar) > maxJumpTableSize)
  715. {
  716. generateDictionary = true;
  717. break;
  718. }
  719. }
  720. }
  721. if (generateDictionary)
  722. {
  723. multiBranchInstr->CreateBranchTargetsAndSetDefaultTarget(caseCount, IR::MultiBranchInstr::StrDictionary, targetOffset);
  724. //Adding normal cases to the instruction (except the default case, which we do it later)
  725. for (uint i = 0; i < caseCount; i++)
  726. {
  727. JITJavascriptString * str = m_caseNodes->Item(i)->GetUpperBoundStringConstLocal();
  728. uint32 caseTargetOffset = m_caseNodes->Item(i)->GetTargetOffset();
  729. multiBranchInstr->AddtoDictionary(caseTargetOffset, str, m_caseNodes->Item(i)->GetUpperBoundStrConst());
  730. }
  731. }
  732. else
  733. {
  734. // If we are only going to save 16 entries, just start from 0 so we don't have to subtract
  735. if (minChar < 16)
  736. {
  737. minChar = 0;
  738. }
  739. multiBranchInstr->m_baseCaseValue = minChar;
  740. multiBranchInstr->m_lastCaseValue = maxChar;
  741. uint jumpTableSize = maxChar - minChar + 1;
  742. multiBranchInstr->CreateBranchTargetsAndSetDefaultTarget(jumpTableSize, IR::MultiBranchInstr::SingleCharStrJumpTable, targetOffset);
  743. for (uint i = 0; i < jumpTableSize; i++)
  744. {
  745. // Initialize all the entries to the default target first.
  746. multiBranchInstr->AddtoJumpTable(targetOffset, i);
  747. }
  748. //Adding normal cases to the instruction (except the default case, which we do it later)
  749. for (uint i = 0; i < caseCount; i++)
  750. {
  751. JITJavascriptString * str = m_caseNodes->Item(i)->GetUpperBoundStringConstLocal();
  752. Assert(str->GetLength() == 1);
  753. uint32 caseTargetOffset = m_caseNodes->Item(i)->GetTargetOffset();
  754. multiBranchInstr->AddtoJumpTable(caseTargetOffset, str->GetString()[0] - minChar);
  755. }
  756. }
  757. multiBranchInstr->m_isSwitchBr = true;
  758. m_adapter->CreateRelocRecord(multiBranchInstr, lastCaseOffset, targetOffset);
  759. m_adapter->AddInstr(multiBranchInstr, lastCaseOffset);
  760. BuildBailOnNotString();
  761. ResetCaseNodes();
  762. }
  763. ///----------------------------------------------------------------------------
  764. ///
  765. /// SwitchIRBuilder::BuildMultiBrCaseInstrForInts
  766. ///
  767. /// Build Multi Branch IR instr for a set of Case statements(Integer case arms).
  768. /// - Builds the multibranch target and adds the instruction
  769. /// - Add boundary checks for the jump table and calculates the offset in the jump table
  770. ///
  771. ///----------------------------------------------------------------------------
  772. IR::MultiBranchInstr *
  773. SwitchIRBuilder::BuildMultiBrCaseInstrForInts(uint32 start, uint32 end, uint32 targetOffset)
  774. {
  775. Assert(m_caseNodes && m_caseNodes->Count() && (m_profiledSwitchInstr || m_isAsmJs));
  776. IR::Opnd * srcOpnd = m_caseNodes->Item(start)->GetCaseInstr()->GetSrc1(); // Src1 is same in all the caseNodes
  777. IR::MultiBranchInstr * multiBranchInstr = IR::MultiBranchInstr::New(Js::OpCode::MultiBr, srcOpnd, m_func);
  778. uint32 lastCaseOffset = m_caseNodes->Item(end)->GetOffset();
  779. int32 baseCaseValue = m_caseNodes->Item(start)->GetLowerBoundIntConst();
  780. int32 lastCaseValue = m_caseNodes->Item(end)->GetUpperBoundIntConst();
  781. multiBranchInstr->m_baseCaseValue = baseCaseValue;
  782. multiBranchInstr->m_lastCaseValue = lastCaseValue;
  783. uint32 jmpTableSize = lastCaseValue - baseCaseValue + 1;
  784. multiBranchInstr->CreateBranchTargetsAndSetDefaultTarget(jmpTableSize, IR::MultiBranchInstr::IntJumpTable, targetOffset);
  785. int caseIndex = end;
  786. int lowerBoundCaseConstValue = 0;
  787. int upperBoundCaseConstValue = 0;
  788. uint32 caseTargetOffset = 0;
  789. for (int jmpIndex = jmpTableSize - 1; jmpIndex >= 0; jmpIndex--)
  790. {
  791. if (caseIndex >= 0 && jmpIndex == m_caseNodes->Item(caseIndex)->GetUpperBoundIntConst() - baseCaseValue)
  792. {
  793. lowerBoundCaseConstValue = m_caseNodes->Item(caseIndex)->GetLowerBoundIntConst();
  794. upperBoundCaseConstValue = m_caseNodes->Item(caseIndex)->GetUpperBoundIntConst();
  795. caseTargetOffset = m_caseNodes->Item(caseIndex--)->GetTargetOffset();
  796. multiBranchInstr->AddtoJumpTable(caseTargetOffset, jmpIndex);
  797. }
  798. else
  799. {
  800. if (jmpIndex >= lowerBoundCaseConstValue - baseCaseValue && jmpIndex <= upperBoundCaseConstValue - baseCaseValue)
  801. {
  802. multiBranchInstr->AddtoJumpTable(caseTargetOffset, jmpIndex);
  803. }
  804. else
  805. {
  806. multiBranchInstr->AddtoJumpTable(targetOffset, jmpIndex);
  807. }
  808. }
  809. }
  810. //Insert Boundary checks for the jump table - Reloc records are created later for these instructions (in FixUpMultiBrJumpTable())
  811. IR::BranchInstr* lowerBoundChk = IR::BranchInstr::New(m_ltOp, nullptr, srcOpnd, m_caseNodes->Item(start)->GetLowerBound(), m_func);
  812. lowerBoundChk->m_isSwitchBr = true;
  813. m_adapter->AddInstr(lowerBoundChk, lastCaseOffset);
  814. IR::BranchInstr* upperBoundChk = IR::BranchInstr::New(m_gtOp, nullptr, srcOpnd, m_caseNodes->Item(end)->GetUpperBound(), m_func);
  815. upperBoundChk->m_isSwitchBr = true;
  816. m_adapter->AddInstr(upperBoundChk, lastCaseOffset);
  817. //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)
  818. IR::IntConstOpnd *baseCaseValueOpnd = IR::IntConstOpnd::New(multiBranchInstr->m_baseCaseValue, TyInt32, m_func);
  819. IR::RegOpnd * offset = IR::RegOpnd::New(TyVar, m_func);
  820. IR::Instr * subInstr = IR::Instr::New(m_subOp, offset, multiBranchInstr->GetSrc1(), baseCaseValueOpnd, m_func);
  821. //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.
  822. subInstr->ignoreIntOverflow = true;
  823. m_adapter->AddInstr(subInstr, lastCaseOffset);
  824. //Source of the multi branch instr will now have the calculated offset
  825. multiBranchInstr->UnlinkSrc1();
  826. multiBranchInstr->SetSrc1(offset);
  827. multiBranchInstr->m_isSwitchBr = true;
  828. m_adapter->AddInstr(multiBranchInstr, lastCaseOffset);
  829. m_adapter->CreateRelocRecord(multiBranchInstr, lastCaseOffset, targetOffset);
  830. return multiBranchInstr;
  831. }