1 // Copyright 2020 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #ifndef SRC_READER_SPIRV_CONSTRUCT_H_
16 #define SRC_READER_SPIRV_CONSTRUCT_H_
17
18 #include <memory>
19 #include <sstream>
20 #include <string>
21 #include <vector>
22
23 namespace tint {
24 namespace reader {
25 namespace spirv {
26
27 /// A structured control flow construct, consisting of a set of basic blocks.
28 /// A construct is a span of blocks in the computed block order,
29 /// and will appear contiguously in the WGSL source.
30 ///
31 /// SPIR-V (2.11 Structured Control Flow) defines:
32 /// - loop construct
33 /// - continue construct
34 /// - selection construct
35 /// We also define a "function construct" consisting of all the basic blocks in
36 /// the function.
37 ///
38 /// The first block in a construct (by computed block order) is called a
39 /// "header". For the constructs defined by SPIR-V, the header block is the
40 /// basic block containing the merge instruction. The header for the function
41 /// construct is the entry block of the function.
42 ///
43 /// Given two constructs A and B, we say "A encloses B" if B is a subset of A,
44 /// i.e. if every basic block in B is also in A. Note that a construct encloses
45 /// itself.
46 ///
47 /// In a valid SPIR-V module, constructs will nest, meaning given
48 /// constructs A and B, either A encloses B, or B encloses A, or
49 /// or they are disjoint (have no basic blocks in commont).
50 ///
51 /// A loop in a high level language translates into either:
52 //
53 /// - a single-block loop, where the loop header branches back to itself.
54 /// In this case this single-block loop consists only of the *continue
55 /// construct*. There is no "loop construct" for this case.
56 //
57 /// - a multi-block loop, where the loop back-edge is different from the loop
58 /// header.
59 /// This case has both a non-empty loop construct containing at least the
60 /// loop header, and a non-empty continue construct, containing at least the
61 /// back-edge block.
62 ///
63 /// We care about two kinds of selection constructs:
64 ///
65 /// - if-selection: where the header block ends in OpBranchConditional
66 ///
67 /// - switch-selection: where the header block ends in OpSwitch
68 ///
69 struct Construct {
70 /// Enumeration for the kinds of structured constructs.
71 enum Kind {
72 /// The whole function.
73 kFunction,
74 /// A SPIR-V selection construct, header basic block ending in
75 /// OpBrancConditional.
76 kIfSelection,
77 /// A SPIR-V selection construct, header basic block ending in OpSwitch.
78 kSwitchSelection,
79 /// A SPIR-V loop construct.
80 kLoop,
81 /// A SPIR-V continue construct.
82 kContinue,
83 };
84
85 /// Constructor
86 /// @param the_parent parent construct
87 /// @param the_depth construct nesting depth
88 /// @param the_kind construct kind
89 /// @param the_begin_id block id of the first block in the construct
90 /// @param the_end_id block id of the first block after the construct, or 0
91 /// @param the_begin_pos block order position of the_begin_id
92 /// @param the_end_pos block order position of the_end_id or a too-large value
93 /// @param the_scope_end_pos block position of the first block past the end of
94 /// the WGSL scope
95 Construct(const Construct* the_parent,
96 int the_depth,
97 Kind the_kind,
98 uint32_t the_begin_id,
99 uint32_t the_end_id,
100 uint32_t the_begin_pos,
101 uint32_t the_end_pos,
102 uint32_t the_scope_end_pos);
103
104 /// @param pos a block position
105 /// @returns true if the given block position is inside this construct.
ContainsPosConstruct106 bool ContainsPos(uint32_t pos) const {
107 return begin_pos <= pos && pos < end_pos;
108 }
109 /// Returns true if the given block position is inside the WGSL scope
110 /// corresponding to this construct. A loop construct's WGSL scope encloses
111 /// the associated continue construct. Otherwise the WGSL scope extent is the
112 /// same as the block extent.
113 /// @param pos a block position
114 /// @returns true if the given block position is inside the WGSL scope.
ScopeContainsPosConstruct115 bool ScopeContainsPos(uint32_t pos) const {
116 return begin_pos <= pos && pos < scope_end_pos;
117 }
118
119 /// The nearest enclosing construct other than itself, or nullptr if
120 /// this construct represents the entire function.
121 const Construct* const parent = nullptr;
122 /// The nearest enclosing loop construct, if one exists. Points to `this`
123 /// when this is a loop construct.
124 const Construct* const enclosing_loop = nullptr;
125 /// The nearest enclosing continue construct, if one exists. Points to
126 /// `this` when this is a contnue construct.
127 const Construct* const enclosing_continue = nullptr;
128 /// The nearest enclosing loop construct or continue construct or
129 /// switch-selection construct, if one exists. The signficance is
130 /// that a high level language "break" will branch to the merge block
131 /// of such an enclosing construct. Points to `this` when this is
132 /// a loop construct, a continue construct, or a switch-selection construct.
133 const Construct* const enclosing_loop_or_continue_or_switch = nullptr;
134
135 /// Control flow nesting depth. The entry block is at nesting depth 0.
136 const int depth = 0;
137 /// The construct kind
138 const Kind kind = kFunction;
139 /// The id of the first block in this structure.
140 const uint32_t begin_id = 0;
141 /// 0 for kFunction, or the id of the block immediately after this construct
142 /// in the computed block order.
143 const uint32_t end_id = 0;
144 /// The position of block #begin_id in the computed block order.
145 const uint32_t begin_pos = 0;
146 /// The position of block #end_id in the block order, or the number of
147 /// block order elements if #end_id is 0.
148 const uint32_t end_pos = 0;
149 /// The position of the first block after the WGSL scope corresponding to
150 /// this construct.
151 const uint32_t scope_end_pos = 0;
152 };
153
154 using ConstructList = std::vector<std::unique_ptr<Construct>>;
155
156 /// Converts a construct kind to a string.
157 /// @param kind the construct kind to convert
158 /// @returns the string representation
ToString(Construct::Kind kind)159 inline std::string ToString(Construct::Kind kind) {
160 switch (kind) {
161 case Construct::kFunction:
162 return "Function";
163 case Construct::kIfSelection:
164 return "IfSelection";
165 case Construct::kSwitchSelection:
166 return "SwitchSelection";
167 case Construct::kLoop:
168 return "Loop";
169 case Construct::kContinue:
170 return "Continue";
171 }
172 return "NONE";
173 }
174
175 /// Converts a construct into a short summary string.
176 /// @param c the construct, which can be null
177 /// @returns a short summary string
ToStringBrief(const Construct * c)178 inline std::string ToStringBrief(const Construct* c) {
179 if (c) {
180 std::stringstream ss;
181 ss << ToString(c->kind) << "@" << c->begin_id;
182 return ss.str();
183 }
184 return "null";
185 }
186
187 /// Emits a construct to a stream.
188 /// @param o the stream
189 /// @param c the structured construct
190 /// @returns the stream
191 inline std::ostream& operator<<(std::ostream& o, const Construct& c) {
192 o << "Construct{ " << ToString(c.kind) << " [" << c.begin_pos << ","
193 << c.end_pos << ")"
194 << " begin_id:" << c.begin_id << " end_id:" << c.end_id
195 << " depth:" << c.depth;
196
197 o << " parent:" << ToStringBrief(c.parent);
198
199 if (c.scope_end_pos != c.end_pos) {
200 o << " scope:[" << c.begin_pos << "," << c.scope_end_pos << ")";
201 }
202
203 if (c.enclosing_loop) {
204 o << " in-l:" << ToStringBrief(c.enclosing_loop);
205 }
206
207 if (c.enclosing_continue) {
208 o << " in-c:" << ToStringBrief(c.enclosing_continue);
209 }
210
211 if ((c.enclosing_loop_or_continue_or_switch != c.enclosing_loop) &&
212 (c.enclosing_loop_or_continue_or_switch != c.enclosing_continue)) {
213 o << " in-c-l-s:" << ToStringBrief(c.enclosing_loop_or_continue_or_switch);
214 }
215
216 o << " }";
217 return o;
218 }
219
220 /// Emits a construct to a stream.
221 /// @param o the stream
222 /// @param c the structured construct
223 /// @returns the stream
224 inline std::ostream& operator<<(std::ostream& o,
225 const std::unique_ptr<Construct>& c) {
226 return o << *(c.get());
227 }
228
229 /// Converts a construct to a string.
230 /// @param c the construct
231 /// @returns the string representation
ToString(const Construct & c)232 inline std::string ToString(const Construct& c) {
233 std::stringstream ss;
234 ss << c;
235 return ss.str();
236 }
237
238 /// Converts a construct to a string.
239 /// @param c the construct
240 /// @returns the string representation
ToString(const Construct * c)241 inline std::string ToString(const Construct* c) {
242 return c ? ToString(*c) : ToStringBrief(c);
243 }
244
245 /// Converts a unique pointer to a construct to a string.
246 /// @param c the construct
247 /// @returns the string representation
ToString(const std::unique_ptr<Construct> & c)248 inline std::string ToString(const std::unique_ptr<Construct>& c) {
249 return ToString(*(c.get()));
250 }
251
252 /// Emits a construct list to a stream.
253 /// @param o the stream
254 /// @param cl the construct list
255 /// @returns the stream
256 inline std::ostream& operator<<(std::ostream& o, const ConstructList& cl) {
257 o << "ConstructList{\n";
258 for (const auto& c : cl) {
259 o << " " << c << "\n";
260 }
261 o << "}";
262 return o;
263 }
264
265 /// Converts a construct list to a string.
266 /// @param cl the construct list
267 /// @returns the string representation
ToString(const ConstructList & cl)268 inline std::string ToString(const ConstructList& cl) {
269 std::stringstream ss;
270 ss << cl;
271 return ss.str();
272 }
273
274 } // namespace spirv
275 } // namespace reader
276 } // namespace tint
277
278 #endif // SRC_READER_SPIRV_CONSTRUCT_H_
279