| //===- subzero/src/IceSwitchLowering.cpp - Switch lowering ----------------===// | 
 | // | 
 | //                        The Subzero Code Generator | 
 | // | 
 | // This file is distributed under the University of Illinois Open Source | 
 | // License. See LICENSE.TXT for details. | 
 | // | 
 | //===----------------------------------------------------------------------===// | 
 | /// | 
 | /// \file | 
 | /// \brief Implements platform independent analysis of switch cases to improve | 
 | /// the generated code. | 
 | /// | 
 | //===----------------------------------------------------------------------===// | 
 | #include "IceSwitchLowering.h" | 
 |  | 
 | #include "IceCfgNode.h" | 
 | #include "IceTargetLowering.h" | 
 |  | 
 | #include <algorithm> | 
 |  | 
 | namespace Ice { | 
 |  | 
 | CaseClusterArray CaseCluster::clusterizeSwitch(Cfg *Func, | 
 |                                                const InstSwitch *Instr) { | 
 |   const SizeT NumCases = Instr->getNumCases(); | 
 |   CaseClusterArray CaseClusters; | 
 |   CaseClusters.reserve(NumCases); | 
 |  | 
 |   // Load the cases | 
 |   CaseClusters.reserve(NumCases); | 
 |   for (SizeT I = 0; I < NumCases; ++I) | 
 |     CaseClusters.emplace_back(Instr->getValue(I), Instr->getLabel(I)); | 
 |  | 
 |   // Sort the cases | 
 |   std::sort(CaseClusters.begin(), CaseClusters.end(), | 
 |             [](const CaseCluster &x, const CaseCluster &y) { | 
 |               return x.High < y.Low; | 
 |             }); | 
 |  | 
 |   // Merge adjacent case ranges | 
 |   auto Active = CaseClusters.begin(); | 
 |   std::for_each(Active + 1, CaseClusters.end(), | 
 |                 [&Active](const CaseCluster &x) { | 
 |                   if (!Active->tryAppend(x)) | 
 |                     *(++Active) = x; | 
 |                 }); | 
 |   CaseClusters.erase(Active + 1, CaseClusters.end()); | 
 |  | 
 |   // TODO(ascull): Merge in a cycle i.e. -1(=UINTXX_MAX) to 0. This depends on | 
 |   // the types for correct wrap around behavior. | 
 |  | 
 |   // A small number of cases is more efficient without a jump table | 
 |   if (CaseClusters.size() < Func->getTarget()->getMinJumpTableSize()) | 
 |     return CaseClusters; | 
 |  | 
 |   // Test for a single jump table. This can be done in constant time whereas | 
 |   // finding the best set of jump table would be quadratic, too slow(?). If | 
 |   // jump tables were included in the search tree we'd first have to traverse | 
 |   // to them. Ideally we would have an unbalanced tree which is biased towards | 
 |   // frequently executed code but we can't do this well without profiling data. | 
 |   // So, this single jump table is a good starting point where you can get to | 
 |   // the jump table quickly without figuring out how to unbalance the tree. | 
 |   const uint64_t MaxValue = CaseClusters.back().High; | 
 |   const uint64_t MinValue = CaseClusters.front().Low; | 
 |   // Don't +1 yet to avoid (INT64_MAX-0)+1 overflow | 
 |   const uint64_t Range = MaxValue - MinValue; | 
 |  | 
 |   // Might be too sparse for the jump table | 
 |   if (NumCases * 2 <= Range) | 
 |     return CaseClusters; | 
 |   // Unlikely. Would mean can't store size of jump table. | 
 |   if (Range == UINT64_MAX) | 
 |     return CaseClusters; | 
 |   const uint64_t TotalRange = Range + 1; | 
 |  | 
 |   // Replace everything with a jump table | 
 |   auto *JumpTable = | 
 |       InstJumpTable::create(Func, TotalRange, Instr->getLabelDefault()); | 
 |   for (const CaseCluster &Case : CaseClusters) { | 
 |     // Case.High could be UINT64_MAX which makes the loop awkward. Unwrap the | 
 |     // last iteration to avoid wrap around problems. | 
 |     for (uint64_t I = Case.Low; I < Case.High; ++I) | 
 |       JumpTable->addTarget(I - MinValue, Case.Target); | 
 |     JumpTable->addTarget(Case.High - MinValue, Case.Target); | 
 |     Case.Target->setNeedsAlignment(); | 
 |   } | 
 |   Func->addJumpTable(JumpTable); | 
 |  | 
 |   CaseClusters.clear(); | 
 |   CaseClusters.emplace_back(MinValue, MaxValue, JumpTable); | 
 |  | 
 |   return CaseClusters; | 
 | } | 
 |  | 
 | bool CaseCluster::tryAppend(const CaseCluster &New) { | 
 |   // Can only append ranges with the same target and are adjacent | 
 |   const bool CanAppend = | 
 |       this->Target == New.Target && this->High + 1 == New.Low; | 
 |   if (CanAppend) | 
 |     this->High = New.High; | 
 |   return CanAppend; | 
 | } | 
 |  | 
 | } // end of namespace Ice |