blob: 37e67c12c005651223b84d13b1738b9e2b149a9f [file] [log] [blame]
Andrew Scull87f80c12015-07-20 10:19:16 -07001//===- subzero/src/IceSwitchLowering.cpp - Switch lowering -----------------==//
2//
3// The Subzero Code Generator
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// This file implements platform independent analysis of switch cases to
12/// improve the generated code.
13///
14//===----------------------------------------------------------------------===//
15#include "IceSwitchLowering.h"
16
17#include "IceTargetLowering.h"
18
19#include <algorithm>
20
21namespace Ice {
22
23CaseClusterArray CaseCluster::clusterizeSwitch(Cfg *Func,
24 const InstSwitch *Inst) {
25 CaseClusterArray CaseClusters;
26
27 // Load the cases
28 SizeT NumCases = Inst->getNumCases();
29 CaseClusters.reserve(NumCases);
30 for (SizeT I = 0; I < NumCases; ++I)
31 CaseClusters.emplace_back(Inst->getValue(I), Inst->getLabel(I));
32
33 // Sort the cases
34 std::sort(CaseClusters.begin(), CaseClusters.end(),
35 [](const CaseCluster &x, const CaseCluster &y) {
36 return x.High < y.Low;
37 });
38
39 // Merge adjacent case ranges
40 auto Active = CaseClusters.begin();
41 std::for_each(Active + 1, CaseClusters.end(),
42 [&Active](const CaseCluster &x) {
43 if (!Active->tryAppend(x))
44 *(++Active) = x;
45 });
46 CaseClusters.erase(Active + 1, CaseClusters.end());
47
48 // TODO(ascull): Merge in a cycle i.e. -1(=UINTXX_MAX) to 0. This depends on
49 // the types for correct wrap around behavior.
50
51 // A small number of cases is more efficient without a jump table
52 if (CaseClusters.size() < Func->getTarget()->getMinJumpTableSize())
53 return CaseClusters;
54
55 // Test for a single jump table. This can be done in constant time whereas
56 // finding the best set of jump table would be quadratic, too slow(?). If
57 // jump tables were included in the search tree we'd first have to traverse to
58 // them. Ideally we would have an unbalanced tree which is biased towards
59 // frequently executed code but we can't do this well without profiling data.
60 // So, this single jump table is a good starting point where you can get to
61 // the jump table quickly without figuring out how to unbalance the tree.
62 uint64_t MaxValue = CaseClusters.back().High;
63 uint64_t MinValue = CaseClusters.front().Low;
64 // Don't +1 yet to avoid (INT64_MAX-0)+1 overflow
65 uint64_t TotalRange = MaxValue - MinValue;
66
67 // Might be too sparse for the jump table
68 if (NumCases * 2 <= TotalRange)
69 return CaseClusters;
70 // Unlikely. Would mean can't store size of jump table.
71 if (TotalRange == UINT64_MAX)
72 return CaseClusters;
73 ++TotalRange;
74
75 // Replace everything with a jump table
76 InstJumpTable *JumpTable =
77 InstJumpTable::create(Func, TotalRange, Inst->getLabelDefault());
Andrew Scull016c56d2015-07-23 14:31:12 -070078 for (const CaseCluster &Case : CaseClusters) {
79 // Case.High could be UINT64_MAX which makes the loop awkward. Unwrap the
80 // last iteration to avoid wrap around problems.
81 for (uint64_t I = Case.Low; I < Case.High; ++I)
Andrew Scull87f80c12015-07-20 10:19:16 -070082 JumpTable->addTarget(I - MinValue, Case.Label);
Andrew Scull016c56d2015-07-23 14:31:12 -070083 JumpTable->addTarget(Case.High - MinValue, Case.Label);
84 }
Andrew Scull87f80c12015-07-20 10:19:16 -070085
86 CaseClusters.clear();
87 CaseClusters.emplace_back(MinValue, MaxValue, JumpTable);
88
89 return CaseClusters;
90}
91
92bool CaseCluster::tryAppend(const CaseCluster &New) {
93 // Can only append ranges with the same target and are adjacent
94 bool CanAppend = this->Label == New.Label && this->High + 1 == New.Low;
95 if (CanAppend)
96 this->High = New.High;
97 return CanAppend;
98}
99
100} // end of namespace Ice