1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
|
// slang-ir-restructure.h
#pragma once
#include "../core/slang-basic.h"
#include "slang-ir-insts.h"
namespace Slang
{
class DiagnosticSink;
struct IRBlock;
struct IRGlobalValueWithCode;
struct IRInst;
struct IRLoop;
/// A structured control-flow region.
///
/// A `Region` is used to layer structured control flow information
/// over an existing IR control flow graph (CFG). Each `Region`
/// represents a sub-graph of the CFG such that control always
/// enters at the start of the region.
///
class Region : public RefObject
{
public:
enum class Flavor
{
Simple,
If,
Break,
Continue,
Loop,
Switch,
};
Flavor getFlavor() { return flavor; }
Region* getParent() { return parent; }
/// Is this region a descendent of `other`?
///
/// For the purpose of this query, a region
/// is a descendent of itself.
bool isDescendentOf(Region* other);
/// Is this region a descendent of `block`?
///
/// This tests is the region is a descendent
/// of any simple region for `block`.
bool isDescendentOf(IRBlock* block);
protected:
Region(Flavor flavor, Region* parent)
: flavor(flavor), parent(parent)
{
}
/// What kind of region is this?
Flavor flavor;
/// The parent region of this region.
Region* parent;
};
/// Base type for regions that have a "next" region.
///
/// While we think of it as a region to execute
/// after this region, the `nextRegion` is actually
/// a *child* region, in that it can see local
/// values that were defined in this parent region
/// (and any other ancestor regions).
class SeqRegion : public Region
{
protected:
SeqRegion(Flavor flavor, Region* parent)
: Region(flavor, parent)
{
}
public:
/// The (child) region to execute after this one.
RefPtr<Region> nextRegion;
};
/// A simple region that encapsulates a basic block.
///
class SimpleRegion : public SeqRegion
{
public:
SimpleRegion(Region* parent, IRBlock* block)
: SeqRegion(Region::Flavor::Simple, parent), block(block)
{
}
/// The basic block for this region.
IRBlock* block = nullptr;
/// The next simple region for the same block
///
/// A single IR basic block may turn into multiple regions,
/// if the restructuring pass has to duplicate it (this
/// currently happens for the continue clause in a `for`
/// loop if it has multiple `continue` sites.
///
SimpleRegion* nextSimpleRegionForSameBlock = nullptr;
};
/// A conditional region, corresponding to an `if`
///
class IfRegion : public SeqRegion
{
public:
IfRegion(Region* parent, IRIfElse* ifElseInst)
: SeqRegion(Region::Flavor::If, parent), ifElseInst(ifElseInst)
{
}
/// The IR `ifElse` instruction
IRIfElse* ifElseInst;
IRInst* getCondition() { return ifElseInst->getCondition(); }
/// The region to execute if the `condition` is `true`
RefPtr<Region> thenRegion;
/// The region to execute if the `condition` is `false`
RefPtr<Region> elseRegion;
};
/// Base type for regions that execution can `break` out of
class BreakableRegion : public SeqRegion
{
protected:
BreakableRegion(Flavor flavor, Region* parent)
: SeqRegion(flavor, parent)
{
}
};
/// A region that expresses a `break` out of nested control flow.
///
class BreakRegion : public Region
{
public:
BreakRegion(Region* parent, BreakableRegion* outerRegion)
: Region(Region::Flavor::Break, parent), outerRegion(outerRegion)
{
}
BreakableRegion* outerRegion;
};
/// A structured loop
class LoopRegion : public BreakableRegion
{
public:
LoopRegion(Region* parent, IRLoop* loopInst)
: BreakableRegion(Region::Flavor::Loop, parent), loopInst(loopInst)
{
}
/// The IR instruction that represents the branch into the loop.
/// We keep this instruction around because it may have decorations
/// that need to influence how we emit this loop.
///
IRLoop* loopInst;
/// The code inside the loop.
///
/// The body region may include `break` or `continue` operations for this loop.
RefPtr<Region> body;
};
/// A region that expresses a `continue` for a structured loop.
///
class ContinueRegion : public Region
{
public:
ContinueRegion(Region* parent, LoopRegion* outerRegion)
: Region(Region::Flavor::Continue, parent), outerRegion(outerRegion)
{
}
LoopRegion* outerRegion;
};
/// A structured `switch` statement.
class SwitchRegion : public BreakableRegion
{
public:
SwitchRegion(Region* parent, IRSwitch* switchInst)
: BreakableRegion(Region::Flavor::Switch, parent), switchInst(switchInst)
{
}
/// The IR `switch` instruction
IRSwitch* switchInst;
IRInst* getCondition() { return switchInst->getCondition(); }
/// A collection of `case`s that share the same code.
class Case : public RefObject
{
public:
/// The various values that should branch to this case.
///
/// It is possible for this list to be empty if this
/// is the `default` case and has no explicit values
/// that map to it.
///
List<IRInst*> values;
/// The region to execute if this case is selected.
RefPtr<Region> body;
};
/// All of the cases for the `switch`.
///
/// This includes any `default` cases.
///
/// As an invariant, a case that "falls through" to another
/// should immediately precede its target in this list.
///
List<RefPtr<Case>> cases;
/// The default case, if any.
///
/// It is valid for this to be `null` if there is no `default` case,
/// in which case the default behavior should be to branch to the region
/// after the `switch`.
///
/// The default case must also be present in `cases`.
Case* defaultCase = nullptr;
};
/// Container for all of the regions in a function.
///
/// A `RegionTree` owns the `Region` objects associated with a function,
/// along with a mapping from basic blocks in the IR function to regions
/// in the tree.
///
class RegionTree : public RefObject
{
public:
/// Type for the mapping from IR blocks to regions.
typedef Dictionary<IRBlock*, SimpleRegion*> MapBlockToRegion;
/// A dictionary to map from IR blocks to regions.
MapBlockToRegion mapBlockToRegion;
/// The root region of the region tree.
RefPtr<Region> rootRegion;
/// The IR function that was used to compute the region tree.
IRGlobalValueWithCode* irCode = nullptr;
};
/// Construct structrured regions to represent the control flow in an IR function.
///
/// The resulting `RegionTree` will encode a structured (statement-like)
/// form for the control flow graph (CFG) of `code`.
/// In cases where our current restructuring approach is not powerful
/// enough to handle something in the input CFG, diagnostic messages
/// will be output to the given `sink`.
///
RefPtr<RegionTree> generateRegionTreeForFunc(IRGlobalValueWithCode* code, DiagnosticSink* sink);
} // namespace Slang
|