SwitchIRBuilder.cpp 38 KB

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