| //===- 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 |