Skip to content

Commit f0d858b

Browse files
authored
fix simplify-locals bug where we create a br_if value, which is dangerous if we are moving code out of the br_if's condition - the value executes before (#1213)
1 parent 69408e6 commit f0d858b

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

src/passes/SimplifyLocals.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <pass.h>
4848
#include <ast/count.h>
4949
#include <ast/effects.h>
50+
#include <ast/find_all.h>
5051
#include <ast/manipulation.h>
5152

5253
namespace wasm {
@@ -327,6 +328,51 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
327328
}
328329
}
329330
if (!found) return;
331+
// If one of our brs is a br_if, then we will give it a value. since
332+
// the value executes before the condition, it is dangerous if we are
333+
// moving code out of the condition,
334+
// (br_if
335+
// (block
336+
// ..use $x..
337+
// (set_local $x ..)
338+
// )
339+
// )
340+
// =>
341+
// (br_if
342+
// (tee_local $x ..) ;; this now affects the use!
343+
// (block
344+
// ..use $x..
345+
// )
346+
// )
347+
// so we must check for that.
348+
for (size_t j = 0; j < breaks.size(); j++) {
349+
// move break set_local's value to the break
350+
auto* breakSetLocalPointer = breaks[j].sinkables.at(sharedIndex).item;
351+
auto* brp = breaks[j].brp;
352+
auto* br = (*brp)->cast<Break>();
353+
auto* set = (*breakSetLocalPointer)->cast<SetLocal>();
354+
if (br->condition) {
355+
// TODO: optimize
356+
FindAll<SetLocal> findAll(br->condition);
357+
for (auto* otherSet : findAll.list) {
358+
if (otherSet == set) {
359+
// the set is indeed in the condition, so we can't just move it
360+
// but maybe there are no effects? see if, ignoring the set
361+
// itself, there is any risk
362+
Nop nop;
363+
*breakSetLocalPointer = &nop;
364+
EffectAnalyzer condition(getPassOptions(), br->condition);
365+
EffectAnalyzer value(getPassOptions(), set);
366+
*breakSetLocalPointer = set;
367+
if (condition.invalidates(value)) {
368+
// indeed, we can't do this, stop
369+
return;
370+
}
371+
break; // we found set in the list, can stop now
372+
}
373+
}
374+
}
375+
}
330376
// Great, this local is set in them all, we can optimize!
331377
if (block->list.size() == 0 || !block->list.back()->is<Nop>()) {
332378
// We can't do this here, since we can't push to the block -

test/passes/simplify-locals.txt

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,4 +1024,53 @@
10241024
(get_local $x)
10251025
)
10261026
)
1027+
(func $br-value-reordering (type $FUNCSIG$i) (result i32)
1028+
(local $temp i32)
1029+
(block $outside
1030+
(loop $loop
1031+
(br_if $outside
1032+
(block $block (result i32)
1033+
(br_if $loop
1034+
(get_local $temp)
1035+
)
1036+
(unreachable)
1037+
(set_local $temp
1038+
(i32.const -1)
1039+
)
1040+
(i32.const 0)
1041+
)
1042+
)
1043+
)
1044+
(set_local $temp
1045+
(i32.const -1)
1046+
)
1047+
)
1048+
(unreachable)
1049+
)
1050+
(func $br-value-reordering-safe (type $FUNCSIG$i) (result i32)
1051+
(local $temp i32)
1052+
(set_local $temp
1053+
(block $outside (result i32)
1054+
(loop $loop
1055+
(drop
1056+
(get_local $temp)
1057+
)
1058+
(drop
1059+
(br_if $outside
1060+
(tee_local $temp
1061+
(i32.const -1)
1062+
)
1063+
(block $block (result i32)
1064+
(nop)
1065+
(i32.const 0)
1066+
)
1067+
)
1068+
)
1069+
)
1070+
(nop)
1071+
(i32.const -1)
1072+
)
1073+
)
1074+
(unreachable)
1075+
)
10271076
)

test/passes/simplify-locals.wast

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,4 +940,51 @@
940940
(drop (i32.atomic.load (i32.const 1028)))
941941
(drop (get_local $x))
942942
)
943+
(func $br-value-reordering (result i32)
944+
(local $temp i32)
945+
(block $outside
946+
(loop $loop ;; we should exit this loop, hit the unreachable outside
947+
;; loop logic
948+
(br_if $outside ;; we should not create a block value that adds a value to a br, if the value&condition of the br cannot be reordered,
949+
;; as the value comes first
950+
(block (result i32)
951+
(br_if $loop
952+
(get_local $temp) ;; false, don't loop
953+
)
954+
(unreachable) ;; the end
955+
(set_local $temp
956+
(i32.const -1)
957+
)
958+
(i32.const 0)
959+
)
960+
)
961+
)
962+
(set_local $temp
963+
(i32.const -1)
964+
)
965+
)
966+
(unreachable)
967+
)
968+
(func $br-value-reordering-safe (result i32)
969+
(local $temp i32)
970+
(block $outside
971+
(loop $loop ;; we should exit this loop, hit the unreachable outside
972+
;; loop logic
973+
(drop (get_local $temp)) ;; different from above - add a use here
974+
(br_if $outside ;; we should not create a block value that adds a value to a br, if the value&condition of the br cannot be reordered,
975+
;; as the value comes first
976+
(block (result i32)
977+
(set_local $temp ;; the use *is* in the condition, but it's ok, no conflicts
978+
(i32.const -1)
979+
)
980+
(i32.const 0)
981+
)
982+
)
983+
)
984+
(set_local $temp
985+
(i32.const -1)
986+
)
987+
)
988+
(unreachable)
989+
)
943990
)

0 commit comments

Comments
 (0)