]>
git.proxmox.com Git - rustc.git/blob - src/binaryen/src/passes/MergeBlocks.cpp
2 * Copyright 2015 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.
18 // Merges blocks to their parents.
20 // We also restructure blocks in order to enable such merging. For
26 // (i32.load (i32.const 100))
31 // can be transformed into
37 // (i32.load (i32.const 100))
43 // after which the internal block can go away, and
44 // the new external block might be mergeable. This is always
45 // worth it if the internal block ends up with 1 item.
46 // For the second operand,
52 // (i32.load (i32.const 200))
56 // The order of operations requires that the first execute
57 // before. We can do the same operation, but only if the
58 // first has no side effects, or the code we are moving out
59 // has no side effects.
60 // If we can do this to both operands, we can generate a
61 // single outside block.
66 #include <wasm-builder.h>
68 #include <ir/effects.h>
72 // Looks for reasons we can't remove the values from breaks to an origin
73 // For example, if there is a switch targeting us, we can't do it - we can't remove the value from other targets
74 struct ProblemFinder
: public ControlFlowWalker
<ProblemFinder
> {
76 bool foundProblem
= false;
77 // count br_ifs, and dropped br_ifs. if they don't match, then a br_if flow value is used, and we can't drop it
79 Index droppedBrIfs
= 0;
80 PassOptions
& passOptions
;
82 ProblemFinder(PassOptions
& passOptions
) : passOptions(passOptions
) {}
84 void visitBreak(Break
* curr
) {
85 if (curr
->name
== origin
) {
86 if (curr
->condition
) {
89 // if the value has side effects, we can't remove it
90 if (EffectAnalyzer(passOptions
, curr
->value
).hasSideEffects()) {
96 void visitDrop(Drop
* curr
) {
97 if (auto* br
= curr
->value
->dynCast
<Break
>()) {
98 if (br
->name
== origin
&& br
->condition
) {
104 void visitSwitch(Switch
* curr
) {
105 if (curr
->default_
== origin
) {
109 for (auto& target
: curr
->targets
) {
110 if (target
== origin
) {
118 assert(brIfs
>= droppedBrIfs
);
119 return foundProblem
|| brIfs
> droppedBrIfs
;
123 // Drops values from breaks to an origin.
124 // While doing so it can create new blocks, so optimize blocks as well.
125 struct BreakValueDropper
: public ControlFlowWalker
<BreakValueDropper
> {
127 PassOptions
& passOptions
;
129 BreakValueDropper(PassOptions
& passOptions
) : passOptions(passOptions
) {}
131 void visitBlock(Block
* curr
);
133 void visitBreak(Break
* curr
) {
134 if (curr
->value
&& curr
->name
== origin
) {
135 Builder
builder(*getModule());
136 auto* value
= curr
->value
;
137 if (value
->type
== unreachable
) {
138 // the break isn't even reached
139 replaceCurrent(value
);
142 curr
->value
= nullptr;
144 replaceCurrent(builder
.makeSequence(builder
.makeDrop(value
), curr
));
148 void visitDrop(Drop
* curr
) {
149 // if we dropped a br_if whose value we removed, then we are now dropping a (block (drop value) (br_if)) with type none, which does not need a drop
150 // likewise, unreachable does not need to be dropped, so we just leave drops of concrete values
151 if (!isConcreteWasmType(curr
->value
->type
)) {
152 replaceCurrent(curr
->value
);
157 static bool hasUnreachableChild(Block
* block
) {
158 for (auto* test
: block
->list
) {
159 if (test
->type
== unreachable
) {
166 // core block optimizer routine
167 static void optimizeBlock(Block
* curr
, Module
* module
, PassOptions
& passOptions
) {
169 bool changed
= false;
172 for (size_t i
= 0; i
< curr
->list
.size(); i
++) {
173 Block
* child
= curr
->list
[i
]->dynCast
<Block
>();
175 // if we have a child that is (drop (block ..)) then we can move the drop into the block, and remove br values. this allows more merging,
176 auto* drop
= curr
->list
[i
]->dynCast
<Drop
>();
178 child
= drop
->value
->dynCast
<Block
>();
180 if (hasUnreachableChild(child
)) {
181 // don't move around unreachable code, as it can change types
182 // dce should have been run anyhow
185 if (child
->name
.is()) {
186 Expression
* expression
= child
;
187 // check if it's ok to remove the value from all breaks to us
188 ProblemFinder
finder(passOptions
);
189 finder
.origin
= child
->name
;
190 finder
.walk(expression
);
191 if (finder
.found()) {
195 BreakValueDropper
fixer(passOptions
);
196 fixer
.origin
= child
->name
;
197 fixer
.setModule(module
);
198 fixer
.walk(expression
);
204 drop
->value
= child
->list
.back();
206 child
->list
.back() = drop
;
208 curr
->list
[i
] = child
;
215 if (!child
) continue;
216 if (child
->name
.is()) continue; // named blocks can have breaks to them (and certainly do, if we ran RemoveUnusedNames and RemoveUnusedBrs)
217 ExpressionList
merged(module
->allocator
);
218 for (size_t j
= 0; j
< i
; j
++) {
219 merged
.push_back(curr
->list
[j
]);
221 for (auto item
: child
->list
) {
222 merged
.push_back(item
);
224 for (size_t j
= i
+ 1; j
< curr
->list
.size(); j
++) {
225 merged
.push_back(curr
->list
[j
]);
227 // if we merged a concrete element in the middle, drop it
228 if (!merged
.empty()) {
229 auto* last
= merged
.back();
230 for (auto*& item
: merged
) {
231 if (item
!= last
&& isConcreteWasmType(item
->type
)) {
232 Builder
builder(*module
);
233 item
= builder
.makeDrop(item
);
237 curr
->list
.swap(merged
);
243 if (changed
) curr
->finalize(curr
->type
);
246 void BreakValueDropper::visitBlock(Block
* curr
) {
247 optimizeBlock(curr
, getModule(), passOptions
);
250 struct MergeBlocks
: public WalkerPass
<PostWalker
<MergeBlocks
>> {
251 bool isFunctionParallel() override
{ return true; }
253 Pass
* create() override
{ return new MergeBlocks
; }
255 void visitBlock(Block
*curr
) {
256 optimizeBlock(curr
, getModule(), getPassOptions());
265 // (..other..children..)
267 // if child is a block, we can move this around to
272 // (..other..children..)
275 // at which point the block is on the outside and potentially mergeable with an outer block
276 Block
* optimize(Expression
* curr
, Expression
*& child
, Block
* outer
= nullptr, Expression
** dependency1
= nullptr, Expression
** dependency2
= nullptr) {
277 if (!child
) return outer
;
278 if ((dependency1
&& *dependency1
) || (dependency2
&& *dependency2
)) {
279 // there are dependencies, things we must be reordered through. make sure no problems there
280 EffectAnalyzer
childEffects(getPassOptions(), child
);
281 if (dependency1
&& *dependency1
&& EffectAnalyzer(getPassOptions(), *dependency1
).invalidates(childEffects
)) return outer
;
282 if (dependency2
&& *dependency2
&& EffectAnalyzer(getPassOptions(), *dependency2
).invalidates(childEffects
)) return outer
;
284 if (auto* block
= child
->dynCast
<Block
>()) {
285 if (!block
->name
.is() && block
->list
.size() >= 2) {
286 // if we move around unreachable code, type changes could occur. avoid that, as
287 // anyhow it means we should have run dce before getting here
288 if (curr
->type
== none
&& hasUnreachableChild(block
)) {
289 // moving the block to the outside would replace a none with an unreachable
292 auto* back
= block
->list
.back();
293 if (back
->type
== unreachable
) {
294 // curr is not reachable, dce could remove it; don't try anything fancy
298 // we are going to replace the block with the final element, so they should
299 // be identically typed
300 if (block
->type
!= back
->type
) {
304 if (outer
== nullptr) {
305 // reuse the block, move it out
306 block
->list
.back() = curr
;
307 // we want the block outside to have the same type as curr had
308 block
->finalize(curr
->type
);
309 replaceCurrent(block
);
312 // append to an existing outer block
313 assert(outer
->list
.back() == curr
);
314 outer
->list
.pop_back();
315 for (Index i
= 0; i
< block
->list
.size() - 1; i
++) {
316 outer
->list
.push_back(block
->list
[i
]);
318 outer
->list
.push_back(curr
);
325 void visitUnary(Unary
* curr
) {
326 optimize(curr
, curr
->value
);
328 void visitSetLocal(SetLocal
* curr
) {
329 optimize(curr
, curr
->value
);
331 void visitLoad(Load
* curr
) {
332 optimize(curr
, curr
->ptr
);
334 void visitReturn(Return
* curr
) {
335 optimize(curr
, curr
->value
);
338 void visitBinary(Binary
* curr
) {
339 optimize(curr
, curr
->right
, optimize(curr
, curr
->left
), &curr
->left
);
341 void visitStore(Store
* curr
) {
342 optimize(curr
, curr
->value
, optimize(curr
, curr
->ptr
), &curr
->ptr
);
344 void visitAtomicRMW(AtomicRMW
* curr
) {
345 optimize(curr
, curr
->value
, optimize(curr
, curr
->ptr
), &curr
->ptr
);
347 void optimizeTernary(Expression
* curr
,
348 Expression
*& first
, Expression
*& second
, Expression
*& third
) {
349 // TODO: for now, just stop when we see any side effect. instead, we could
350 // check effects carefully for reordering
351 Block
* outer
= nullptr;
352 if (EffectAnalyzer(getPassOptions(), first
).hasSideEffects()) return;
353 outer
= optimize(curr
, first
, outer
);
354 if (EffectAnalyzer(getPassOptions(), second
).hasSideEffects()) return;
355 outer
= optimize(curr
, second
, outer
);
356 if (EffectAnalyzer(getPassOptions(), third
).hasSideEffects()) return;
357 optimize(curr
, third
, outer
);
359 void visitAtomicCmpxchg(AtomicCmpxchg
* curr
) {
360 optimizeTernary(curr
, curr
->ptr
, curr
->expected
, curr
->replacement
);
363 void visitSelect(Select
* curr
) {
364 optimizeTernary(curr
, curr
->ifTrue
, curr
->ifFalse
, curr
->condition
);
367 void visitDrop(Drop
* curr
) {
368 optimize(curr
, curr
->value
);
371 void visitBreak(Break
* curr
) {
372 optimize(curr
, curr
->condition
, optimize(curr
, curr
->value
), &curr
->value
);
374 void visitSwitch(Switch
* curr
) {
375 optimize(curr
, curr
->condition
, optimize(curr
, curr
->value
), &curr
->value
);
379 void handleCall(T
* curr
) {
380 Block
* outer
= nullptr;
381 for (Index i
= 0; i
< curr
->operands
.size(); i
++) {
382 if (EffectAnalyzer(getPassOptions(), curr
->operands
[i
]).hasSideEffects()) return;
383 outer
= optimize(curr
, curr
->operands
[i
], outer
);
388 void visitCall(Call
* curr
) {
392 void visitCallImport(CallImport
* curr
) {
396 void visitCallIndirect(CallIndirect
* curr
) {
397 Block
* outer
= nullptr;
398 for (Index i
= 0; i
< curr
->operands
.size(); i
++) {
399 if (EffectAnalyzer(getPassOptions(), curr
->operands
[i
]).hasSideEffects()) return;
400 outer
= optimize(curr
, curr
->operands
[i
], outer
);
402 if (EffectAnalyzer(getPassOptions(), curr
->target
).hasSideEffects()) return;
403 optimize(curr
, curr
->target
, outer
);
407 Pass
*createMergeBlocksPass() {
408 return new MergeBlocks();