Skip to content

Commit 8b698a8

Browse files
authored
Vacuum unused values (#1918)
Checks if a value is being dropped higher up, like ``` (drop (block i32 (block i32 (i32.const 1) ) ) ) ``` Handling this forces us to be careful in that pass about whether a value is used, and whether the type matters (for example, we can't replace a unary with its child in all cases, if the return value matters).
1 parent 605e2b7 commit 8b698a8

File tree

3 files changed

+305
-9
lines changed

3 files changed

+305
-9
lines changed

src/passes/Vacuum.cpp

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@
2323
#include <wasm-builder.h>
2424
#include <ir/block-utils.h>
2525
#include <ir/effects.h>
26+
#include <ir/literal-utils.h>
2627
#include <ir/type-updating.h>
28+
#include <ir/utils.h>
2729

2830
namespace wasm {
2931

30-
struct Vacuum : public WalkerPass<PostWalker<Vacuum>> {
32+
struct Vacuum : public WalkerPass<ExpressionStackWalker<Vacuum>> {
3133
bool isFunctionParallel() override { return true; }
3234

3335
Pass* create() override { return new Vacuum; }
@@ -47,11 +49,21 @@ struct Vacuum : public WalkerPass<PostWalker<Vacuum>> {
4749
walk(func->body);
4850
}
4951

50-
// returns nullptr if curr is dead, curr if it must stay as is, or another node if it can be replaced
51-
Expression* optimize(Expression* curr, bool resultUsed) {
52-
// an unreachable node must not be changed
53-
if (curr->type == unreachable) return curr;
52+
// Returns nullptr if curr is dead, curr if it must stay as is, or another node if it can be replaced.
53+
// Takes into account:
54+
// * The result may be used or unused.
55+
// * The type may or may not matter (a drop can drop anything, for example).
56+
Expression* optimize(Expression* curr, bool resultUsed, bool typeMatters) {
57+
auto type = curr->type;
58+
// An unreachable node must not be changed.
59+
if (type == unreachable) return curr;
60+
// We iterate on possible replacements. If a replacement changes the type, stop and go back.
61+
auto* prev = curr;
5462
while (1) {
63+
if (typeMatters && curr->type != type) {
64+
return prev;
65+
}
66+
prev = curr;
5567
switch (curr->_id) {
5668
case Expression::Id::NopId: return nullptr; // never needed
5769

@@ -173,7 +185,22 @@ struct Vacuum : public WalkerPass<PostWalker<Vacuum>> {
173185
size_t size = list.size();
174186
for (size_t z = 0; z < size; z++) {
175187
auto* child = list[z];
176-
auto* optimized = optimize(child, z == size - 1 && isConcreteType(curr->type));
188+
// The last element may be used.
189+
bool used = z == size - 1 &&
190+
isConcreteType(curr->type) &&
191+
ExpressionAnalyzer::isResultUsed(expressionStack, getFunction());
192+
auto* optimized = optimize(child, used, true);
193+
if (!optimized) {
194+
if (isConcreteType(child->type)) {
195+
// We can't just skip a final concrete element, even if it isn't used. Instead,
196+
// replace it with something that's easy to optimize out (for example, code-folding
197+
// can merge out identical zeros at the end of if arms).
198+
optimized = LiteralUtils::makeZero(child->type, *getModule());
199+
} else if (child->type == unreachable) {
200+
// Don't try to optimize out an unreachable child (dce can do that properly).
201+
optimized = child;
202+
}
203+
}
177204
if (!optimized) {
178205
typeUpdater.noteRecursiveRemoval(child);
179206
skip++;
@@ -275,7 +302,7 @@ struct Vacuum : public WalkerPass<PostWalker<Vacuum>> {
275302

276303
void visitDrop(Drop* curr) {
277304
// optimize the dropped value, maybe leaving nothing
278-
curr->value = optimize(curr->value, false);
305+
curr->value = optimize(curr->value, false, false);
279306
if (curr->value == nullptr) {
280307
ExpressionManipulator::nop(curr);
281308
return;
@@ -294,7 +321,7 @@ struct Vacuum : public WalkerPass<PostWalker<Vacuum>> {
294321
// block has an unreachable element in the middle, making the block unreachable
295322
// despite later elements and in particular the last
296323
if (isConcreteType(last->type) && block->type == last->type) {
297-
last = optimize(last, false);
324+
last = optimize(last, false, false);
298325
if (!last) {
299326
// we may be able to remove this, if there are no brs
300327
bool canPop = true;
@@ -343,7 +370,7 @@ struct Vacuum : public WalkerPass<PostWalker<Vacuum>> {
343370
}
344371

345372
void visitFunction(Function* curr) {
346-
auto* optimized = optimize(curr->body, curr->result != none);
373+
auto* optimized = optimize(curr->body, curr->result != none, true);
347374
if (optimized) {
348375
curr->body = optimized;
349376
} else {

test/passes/vacuum.txt

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,138 @@
305305
(nop)
306306
)
307307
)
308+
(module
309+
(type $0 (func (param i64)))
310+
(type $1 (func (param f32 i32) (result i32)))
311+
(func $0 (; 0 ;) (type $0) (param $0 i64)
312+
(nop)
313+
)
314+
(func $1 (; 1 ;) (type $1) (param $0 f32) (param $1 i32) (result i32)
315+
(drop
316+
(block $label$2 (result i64)
317+
(call $0
318+
(br_if $label$2
319+
(i64.const -137438953472)
320+
(i32.const 1)
321+
)
322+
)
323+
(unreachable)
324+
)
325+
)
326+
(unreachable)
327+
)
328+
)
329+
(module
330+
(type $0 (func (param i32) (result i32)))
331+
(type $1 (func (param i32 i32 i32)))
332+
(global $global$1 (mut i32) (i32.const 0))
333+
(export "compress" (func $3))
334+
(func $_deflate (; 0 ;) (type $0) (param $0 i32) (result i32)
335+
(call $_deflate
336+
(local.get $0)
337+
)
338+
)
339+
(func $_deflateInit2_ (; 1 ;) (type $0) (param $0 i32) (result i32)
340+
(call $_deflateInit2_
341+
(local.get $0)
342+
)
343+
)
344+
(func $_deflateEnd (; 2 ;) (type $0) (param $0 i32) (result i32)
345+
(call $_deflateEnd
346+
(local.get $0)
347+
)
348+
)
349+
(func $3 (; 3 ;) (type $1) (param $0 i32) (param $1 i32) (param $2 i32)
350+
(local $3 i32)
351+
(local.set $3
352+
(global.get $global$1)
353+
)
354+
(global.set $global$1
355+
(i32.sub
356+
(global.get $global$1)
357+
(i32.const -64)
358+
)
359+
)
360+
(i32.store
361+
(local.get $3)
362+
(local.get $2)
363+
)
364+
(i32.store offset=4
365+
(local.get $3)
366+
(i32.const 100000)
367+
)
368+
(i32.store offset=12
369+
(local.get $3)
370+
(local.get $0)
371+
)
372+
(i32.store offset=16
373+
(local.get $3)
374+
(i32.load
375+
(local.get $1)
376+
)
377+
)
378+
(i32.store offset=32
379+
(local.get $3)
380+
(i32.const 0)
381+
)
382+
(i32.store offset=36
383+
(local.get $3)
384+
(i32.const 0)
385+
)
386+
(i32.store offset=40
387+
(local.get $3)
388+
(i32.const 0)
389+
)
390+
(if
391+
(call $_deflateInit2_
392+
(local.get $3)
393+
)
394+
(block $block
395+
(global.set $global$1
396+
(local.get $3)
397+
)
398+
(return)
399+
)
400+
)
401+
(drop
402+
(if (result i32)
403+
(i32.eq
404+
(local.tee $0
405+
(call $_deflate
406+
(local.get $3)
407+
)
408+
)
409+
(i32.const 1)
410+
)
411+
(block $block1 (result i32)
412+
(i32.store
413+
(local.get $1)
414+
(i32.load offset=20
415+
(local.get $3)
416+
)
417+
)
418+
(local.set $0
419+
(call $_deflateEnd
420+
(local.get $3)
421+
)
422+
)
423+
(global.set $global$1
424+
(local.get $3)
425+
)
426+
(i32.const 0)
427+
)
428+
(block $block2 (result i32)
429+
(drop
430+
(call $_deflateEnd
431+
(local.get $3)
432+
)
433+
)
434+
(global.set $global$1
435+
(local.get $3)
436+
)
437+
(i32.const 0)
438+
)
439+
)
440+
)
441+
)
442+
)

test/passes/vacuum.wast

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -660,3 +660,137 @@
660660
)
661661
)
662662
)
663+
(module ;; a child with a different type, cannot simply replace the parent with it
664+
(type $0 (func (param i64)))
665+
(type $1 (func (param f32 i32) (result i32)))
666+
(func $0 (; 0 ;) (type $0) (param $0 i64)
667+
(nop)
668+
)
669+
(func $1 (; 1 ;) (type $1) (param $0 f32) (param $1 i32) (result i32)
670+
(drop
671+
(block $label$1 (result i32)
672+
(i32.wrap_i64
673+
(block $label$2 (result i64)
674+
(call $0
675+
(br_if $label$2
676+
(i64.const -137438953472)
677+
(i32.const 1)
678+
)
679+
)
680+
(unreachable)
681+
)
682+
)
683+
)
684+
)
685+
(unreachable)
686+
)
687+
)
688+
(module ;; vacuum away a drop on an if where both arms can be vacuumed
689+
(global $global$1 (mut i32) (i32.const 0))
690+
(func $_deflate (param i32) (result i32)
691+
(call $_deflate (local.get $0))
692+
)
693+
(func $_deflateInit2_ (param i32) (result i32)
694+
(call $_deflateInit2_ (local.get $0))
695+
)
696+
(func $_deflateEnd (param i32) (result i32)
697+
(call $_deflateEnd (local.get $0))
698+
)
699+
(func "compress" (param $0 i32) (param $1 i32) (param $2 i32)
700+
(local $3 i32)
701+
(local.set $3
702+
(global.get $global$1)
703+
)
704+
(global.set $global$1
705+
(i32.sub
706+
(global.get $global$1)
707+
(i32.const -64)
708+
)
709+
)
710+
(i32.store
711+
(local.get $3)
712+
(local.get $2)
713+
)
714+
(i32.store offset=4
715+
(local.get $3)
716+
(i32.const 100000)
717+
)
718+
(i32.store offset=12
719+
(local.get $3)
720+
(local.get $0)
721+
)
722+
(i32.store offset=16
723+
(local.get $3)
724+
(i32.load
725+
(local.get $1)
726+
)
727+
)
728+
(i32.store offset=32
729+
(local.get $3)
730+
(i32.const 0)
731+
)
732+
(i32.store offset=36
733+
(local.get $3)
734+
(i32.const 0)
735+
)
736+
(i32.store offset=40
737+
(local.get $3)
738+
(i32.const 0)
739+
)
740+
(if
741+
(call $_deflateInit2_
742+
(local.get $3)
743+
)
744+
(block
745+
(global.set $global$1
746+
(local.get $3)
747+
)
748+
(return)
749+
)
750+
)
751+
(drop
752+
(if (result i32)
753+
(i32.eq
754+
(local.tee $0
755+
(call $_deflate
756+
(local.get $3)
757+
)
758+
)
759+
(i32.const 1)
760+
)
761+
(block (result i32)
762+
(i32.store
763+
(local.get $1)
764+
(i32.load offset=20
765+
(local.get $3)
766+
)
767+
)
768+
(local.set $0
769+
(call $_deflateEnd
770+
(local.get $3)
771+
)
772+
)
773+
(global.set $global$1
774+
(local.get $3)
775+
)
776+
(local.get $0)
777+
)
778+
(block (result i32)
779+
(drop
780+
(call $_deflateEnd
781+
(local.get $3)
782+
)
783+
)
784+
(global.set $global$1
785+
(local.get $3)
786+
)
787+
(select
788+
(local.get $0)
789+
(i32.const -5)
790+
(local.get $0)
791+
)
792+
)
793+
)
794+
)
795+
)
796+
)

0 commit comments

Comments
 (0)