]>
git.proxmox.com Git - rustc.git/blob - src/binaryen/src/ir/type-updating.h
2 * Copyright 2017 WebAssembly Community Group participants
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #ifndef wasm_ir_type_updating_h
18 #define wasm_ir_type_updating_h
20 #include "wasm-traversal.h"
24 // a class that tracks type dependencies between nodes, letting you
25 // update types efficiently when removing and altering code.
26 // altering code can alter types in the following way:
27 // * removing a break can make a block unreachable, if nothing else
29 // * altering the type of a child to unreachable can make the parent
31 struct TypeUpdater
: public ExpressionStackWalker
<TypeUpdater
, UnifiedExpressionVisitor
<TypeUpdater
>> {
34 // track names to their blocks, so that when we remove a break to
35 // a block, we know how to find it if we need to update it
37 Block
* block
= nullptr;
40 std::map
<Name
, BlockInfo
> blockInfos
;
42 // track the parent of each node, as child type changes may lead to
44 std::map
<Expression
*, Expression
*> parents
;
46 void visitExpression(Expression
* curr
) {
47 if (expressionStack
.size() > 1) {
48 parents
[curr
] = expressionStack
[expressionStack
.size() - 2];
50 parents
[curr
] = nullptr; // this is the top level
52 // discover block/break relationships
53 if (auto* block
= curr
->dynCast
<Block
>()) {
54 if (block
->name
.is()) {
55 blockInfos
[block
->name
].block
= block
;
57 } else if (auto* br
= curr
->dynCast
<Break
>()) {
58 // ensure info exists, discoverBreaks can then fill it
60 } else if (auto* sw
= curr
->dynCast
<Switch
>()) {
61 // ensure info exists, discoverBreaks can then fill it
62 for (auto target
: sw
->targets
) {
65 blockInfos
[sw
->default_
];
67 // add a break to the info, for break and switch
68 discoverBreaks(curr
, +1);
73 // Node replacements, additions, removals and type changes should be noted. An
74 // exception is nodes you know will never be looked at again.
76 // note the replacement of one node with another. this should be called
77 // after performing the replacement.
78 // this does *not* look into the node by default. see noteReplacementWithRecursiveRemoval
79 // (we don't support recursive addition because in practice we do not create
80 // new trees in the passes that use this, they just move around children)
81 void noteReplacement(Expression
* from
, Expression
* to
, bool recursivelyRemove
=false) {
82 auto parent
= parents
[from
];
83 if (recursivelyRemove
) {
84 noteRecursiveRemoval(from
);
88 // if we are replacing with a child, i.e. a node that was already present
89 // in the ast, then we just have a type and parent to update
90 if (parents
.find(to
) != parents
.end()) {
92 if (from
->type
!= to
->type
) {
96 noteAddition(to
, parent
, from
);
100 void noteReplacementWithRecursiveRemoval(Expression
* from
, Expression
* to
) {
101 noteReplacement(from
, to
, true);
104 // note the removal of a node
105 void noteRemoval(Expression
* curr
) {
106 noteRemovalOrAddition(curr
, nullptr);
110 // note the removal of a node and all its children
111 void noteRecursiveRemoval(Expression
* curr
) {
112 struct Recurser
: public PostWalker
<Recurser
, UnifiedExpressionVisitor
<Recurser
>> {
115 Recurser(TypeUpdater
& parent
, Expression
* root
) : parent(parent
) {
119 void visitExpression(Expression
* curr
) {
120 parent
.noteRemoval(curr
);
124 Recurser(*this, curr
);
127 void noteAddition(Expression
* curr
, Expression
* parent
, Expression
* previous
= nullptr) {
128 assert(parents
.find(curr
) == parents
.end()); // must not already exist
129 noteRemovalOrAddition(curr
, parent
);
130 // if we didn't replace with the exact same type, propagate types up
131 if (!(previous
&& previous
->type
== curr
->type
)) {
132 propagateTypesUp(curr
);
136 // if parent is nullptr, this is a removal
137 void noteRemovalOrAddition(Expression
* curr
, Expression
* parent
) {
138 parents
[curr
] = parent
;
139 discoverBreaks(curr
, parent
? +1 : -1);
142 // adds (or removes) breaks depending on break/switch contents
143 void discoverBreaks(Expression
* curr
, int change
) {
144 if (auto* br
= curr
->dynCast
<Break
>()) {
145 noteBreakChange(br
->name
, change
, br
->value
);
146 } else if (auto* sw
= curr
->dynCast
<Switch
>()) {
147 applySwitchChanges(sw
, change
);
151 void applySwitchChanges(Switch
* sw
, int change
) {
153 for (auto target
: sw
->targets
) {
154 if (seen
.insert(target
).second
) {
155 noteBreakChange(target
, change
, sw
->value
);
158 if (seen
.insert(sw
->default_
).second
) {
159 noteBreakChange(sw
->default_
, change
, sw
->value
);
163 // note the addition of a node
164 void noteBreakChange(Name name
, int change
, Expression
* value
) {
165 auto iter
= blockInfos
.find(name
);
166 if (iter
== blockInfos
.end()) {
167 return; // we can ignore breaks to loops
169 auto& info
= iter
->second
;
170 info
.numBreaks
+= change
;
171 assert(info
.numBreaks
>= 0);
172 auto* block
= info
.block
;
173 if (block
) { // if to a loop, can ignore
174 if (info
.numBreaks
== 0) {
175 // dropped to 0! the block may now be unreachable. that
176 // requires that it doesn't have a fallthrough
177 makeBlockUnreachableIfNoFallThrough(block
);
178 } else if (change
== 1 && info
.numBreaks
== 1) {
179 // bumped to 1! the block may now be reachable
180 if (block
->type
!= unreachable
) {
181 return; // was already reachable, had a fallthrough
183 changeTypeTo(block
, value
? value
->type
: none
);
188 // alters the type of a node to a new type.
189 // this propagates the type change through all the parents.
190 void changeTypeTo(Expression
* curr
, WasmType newType
) {
191 if (curr
->type
== newType
) return; // nothing to do
192 curr
->type
= newType
;
193 propagateTypesUp(curr
);
196 // given a node that has a new type, or is a new node, update
197 // all the parents accordingly. the existence of the node and
198 // any changes to it already occurred, this just updates the
199 // parents following that. i.e., nothing is done to the
200 // node we start on, it's done.
201 // the one thing we need to do here is propagate unreachability,
202 // no other change is possible
203 void propagateTypesUp(Expression
* curr
) {
204 if (curr
->type
!= unreachable
) return;
207 curr
= parents
[child
];
209 // get ready to apply unreachability to this node
210 if (curr
->type
== unreachable
) {
211 return; // already unreachable, stop here
213 // most nodes become unreachable if a child is unreachable,
214 // but exceptions exist
215 if (auto* block
= curr
->dynCast
<Block
>()) {
216 // if the block has a fallthrough, it can keep its type
217 if (isConcreteWasmType(block
->list
.back()->type
)) {
218 return; // did not turn
220 // if the block has breaks, it can keep its type
221 if (!block
->name
.is() || blockInfos
[block
->name
].numBreaks
== 0) {
222 curr
->type
= unreachable
;
224 return; // did not turn
226 } else if (auto* iff
= curr
->dynCast
<If
>()) {
227 // may not be unreachable if just one side is
229 if (curr
->type
!= unreachable
) {
230 return; // did not turn
233 curr
->type
= unreachable
;
238 // efficiently update the type of a block, given the data we know. this
239 // can remove a concrete type and turn the block unreachable when it is
240 // unreachable, and it does this efficiently, without scanning the full
242 void maybeUpdateTypeToUnreachable(Block
* curr
) {
243 if (!isConcreteWasmType(curr
->type
)) {
244 return; // nothing concrete to change to unreachable
246 if (curr
->name
.is() && blockInfos
[curr
->name
].numBreaks
> 0) {
247 return; // has a break, not unreachable
249 // look for a fallthrough
250 makeBlockUnreachableIfNoFallThrough(curr
);
253 void makeBlockUnreachableIfNoFallThrough(Block
* curr
) {
254 if (curr
->type
== unreachable
) {
255 return; // no change possible
257 if (!curr
->list
.empty() &&
258 isConcreteWasmType(curr
->list
.back()->type
)) {
259 return; // should keep type due to fallthrough, even if has an unreachable child
261 for (auto* child
: curr
->list
) {
262 if (child
->type
== unreachable
) {
263 // no fallthrough, and an unreachable, => this block is now unreachable
264 changeTypeTo(curr
, unreachable
);
270 // efficiently update the type of an if, given the data we know. this
271 // can remove a concrete type and turn the if unreachable when it is
273 void maybeUpdateTypeToUnreachable(If
* curr
) {
274 if (!isConcreteWasmType(curr
->type
)) {
275 return; // nothing concrete to change to unreachable
278 if (curr
->type
== unreachable
) {
279 propagateTypesUp(curr
);
286 #endif // wasm_ir_type_updating_h