Skip to content

Commit c692533

Browse files
authored
fix dce bug with not updating the parent when turning a node unreachable (#1198)
1 parent 23d015b commit c692533

File tree

4 files changed

+138
-23
lines changed

4 files changed

+138
-23
lines changed

src/ast/type-updating.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,16 @@ struct TypeUpdater : public ExpressionStackWalker<TypeUpdater, UnifiedExpression
7575

7676
// note the replacement of one node with another. this should be called
7777
// after performing the replacement.
78-
// this does *not* look into the node. you should do so yourself if necessary
79-
void noteReplacement(Expression* from, Expression* to) {
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) {
8082
auto parent = parents[from];
81-
noteRemoval(from);
83+
if (recursivelyRemove) {
84+
noteRecursiveRemoval(from);
85+
} else {
86+
noteRemoval(from);
87+
}
8288
// if we are replacing with a child, i.e. a node that was already present
8389
// in the ast, then we just have a type and parent to update
8490
if (parents.find(to) != parents.end()) {
@@ -91,6 +97,10 @@ struct TypeUpdater : public ExpressionStackWalker<TypeUpdater, UnifiedExpression
9197
}
9298
}
9399

100+
void noteReplacementWithRecursiveRemoval(Expression* from, Expression* to) {
101+
noteReplacement(from, to, true);
102+
}
103+
94104
// note the removal of a node
95105
void noteRemoval(Expression* curr) {
96106
noteRemovalOrAddition(curr, nullptr);

src/passes/DeadCodeElimination.cpp

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
4848

4949
Expression* replaceCurrent(Expression* expression) {
5050
auto* old = getCurrent();
51+
if (old == expression) return expression;
5152
WalkerPass<PostWalker<DeadCodeElimination>>::replaceCurrent(expression);
5253
// also update the type updater
5354
typeUpdater.noteReplacement(old, expression);
@@ -219,14 +220,17 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
219220
}
220221

221222
static void scan(DeadCodeElimination* self, Expression** currp) {
223+
auto* curr = *currp;
222224
if (!self->reachable) {
223-
// convert to an unreachable. do this without UB, even though we have no destructors on AST nodes
225+
// convert to an unreachable safely
224226
#define DELEGATE(CLASS_TO_VISIT) { \
225-
self->typeUpdater.noteRecursiveRemoval(*currp); \
226-
ExpressionManipulator::convert<CLASS_TO_VISIT, Unreachable>(static_cast<CLASS_TO_VISIT*>(*currp)); \
227+
auto* parent = self->typeUpdater.parents[curr]; \
228+
self->typeUpdater.noteRecursiveRemoval(curr); \
229+
ExpressionManipulator::convert<CLASS_TO_VISIT, Unreachable>(static_cast<CLASS_TO_VISIT*>(curr)); \
230+
self->typeUpdater.noteAddition(curr, parent); \
227231
break; \
228232
}
229-
switch ((*currp)->_id) {
233+
switch (curr->_id) {
230234
case Expression::Id::BlockId: DELEGATE(Block);
231235
case Expression::Id::IfId: DELEGATE(If);
232236
case Expression::Id::LoopId: DELEGATE(Loop);
@@ -256,7 +260,6 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
256260
#undef DELEGATE
257261
return;
258262
}
259-
auto* curr =* currp;
260263
if (curr->is<If>()) {
261264
self->pushTask(DeadCodeElimination::doVisitIf, currp);
262265
if (curr->cast<If>()->ifFalse) {
@@ -328,18 +331,18 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
328331
for (size_t i = 0; i < list.size(); ++i) {
329332
auto* elem = list[i];
330333
if (isUnreachable(elem)) {
331-
auto* replacement = elem;
332-
if (i > 0) {
333-
auto* block = getModule()->allocator.alloc<Block>();
334-
for (size_t j = 0; j < i; ++j) {
335-
block->list.push_back(drop(list[j]));
336-
}
337-
block->list.push_back(list[i]);
338-
block->finalize(type);
339-
replacement = block;
340-
}
341-
replaceCurrent(replacement);
342-
return;
334+
auto* replacement = elem;
335+
if (i > 0) {
336+
auto* block = getModule()->allocator.alloc<Block>();
337+
for (size_t j = 0; j < i; ++j) {
338+
block->list.push_back(drop(list[j]));
339+
}
340+
block->list.push_back(list[i]);
341+
block->finalize(type);
342+
replacement = block;
343+
}
344+
replaceCurrent(replacement);
345+
return;
343346
}
344347
}
345348
}
@@ -349,7 +352,7 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
349352
}
350353

351354
void visitLoad(Load* curr) {
352-
blockifyReachableOperands({ curr->ptr}, curr->type);
355+
blockifyReachableOperands({ curr->ptr }, curr->type);
353356
}
354357

355358
void visitStore(Store* curr) {
@@ -369,11 +372,11 @@ struct DeadCodeElimination : public WalkerPass<PostWalker<DeadCodeElimination>>
369372
}
370373

371374
void visitBinary(Binary* curr) {
372-
blockifyReachableOperands({ curr->left, curr->right}, curr->type);
375+
blockifyReachableOperands({ curr->left, curr->right }, curr->type);
373376
}
374377

375378
void visitSelect(Select* curr) {
376-
blockifyReachableOperands({ curr->ifTrue, curr->ifFalse, curr->condition}, curr->type);
379+
blockifyReachableOperands({ curr->ifTrue, curr->ifFalse, curr->condition }, curr->type);
377380
}
378381

379382
void visitDrop(Drop* curr) {

test/passes/dce.txt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
(type $2 (func (result i32)))
55
(type $3 (func (param i32) (result i32)))
66
(type $4 (func (param i64 i64) (result i64)))
7+
(type $5 (func (param f32 i64)))
8+
(type $6 (func (param f32 i64) (result i32)))
79
(global $x (mut i32) (i32.const 0))
810
(table 1 1 anyfunc)
911
(elem (i32.const 0) $call-me)
@@ -441,4 +443,48 @@
441443
(unreachable)
442444
)
443445
)
446+
(func $replace-with-unreachable-affects-parent (type $5) (param $var$0 f32) (param $var$1 i64)
447+
(block $top
448+
(drop
449+
(f32.load offset=4
450+
(block (result i32)
451+
(drop
452+
(i64.const 0)
453+
)
454+
(if
455+
(block $block (result i32)
456+
(call $replace-with-unreachable-affects-parent
457+
(f32.const 1)
458+
(i64.const -15917430362925035)
459+
)
460+
(i32.const 1)
461+
)
462+
(unreachable)
463+
(unreachable)
464+
)
465+
)
466+
)
467+
)
468+
(unreachable)
469+
)
470+
)
471+
(func $replace-block-changes-later-when-if-goes (type $1)
472+
(block $top
473+
(set_global $global$0
474+
(i32.const 0)
475+
)
476+
(block $inner
477+
(drop
478+
(call $helper
479+
(f32.const 1)
480+
(i64.const -15917430362925035)
481+
)
482+
)
483+
(unreachable)
484+
)
485+
)
486+
)
487+
(func $helper (type $6) (param $var$0 f32) (param $var$1 i64) (result i32)
488+
(i32.const 0)
489+
)
444490
)

test/passes/dce.wast

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,4 +651,60 @@
651651
)
652652
)
653653
)
654+
(func $replace-with-unreachable-affects-parent (param $var$0 f32) (param $var$1 i64)
655+
(block $top
656+
(drop
657+
(f32.load offset=4
658+
(i64.ne
659+
(i64.const 0)
660+
(if (result i64)
661+
(block (result i32)
662+
(call $replace-with-unreachable-affects-parent
663+
(f32.const 1)
664+
(i64.const -15917430362925035)
665+
)
666+
(i32.const 1)
667+
)
668+
(unreachable)
669+
(unreachable)
670+
)
671+
)
672+
)
673+
)
674+
(nop) ;; this is not reachable due to the above code, so we replace it with unreachable. type should go to parent
675+
)
676+
)
677+
(func $replace-block-changes-later-when-if-goes
678+
(block $top ;; and so should this
679+
(set_global $global$0
680+
(i32.const 0)
681+
)
682+
(drop
683+
(f32.load offset=4
684+
(i64.ne
685+
(block $inner (result i64) ;; this becomes unreachable
686+
(drop
687+
(call $helper
688+
(f32.const 1)
689+
(i64.const -15917430362925035)
690+
)
691+
)
692+
(unreachable)
693+
)
694+
(i64.const 0)
695+
)
696+
)
697+
)
698+
(if
699+
(i32.load16_s offset=22 align=1
700+
(i32.const 0)
701+
)
702+
(br $top) ;; this keeps the block none after the inner block gets unreachable. but it will vanish into unreachable itself
703+
(unreachable)
704+
)
705+
)
706+
)
707+
(func $helper (param $var$0 f32) (param $var$1 i64) (result i32)
708+
(i32.const 0)
709+
)
654710
)

0 commit comments

Comments
 (0)