From dfb28a2ee23ee0ba835de4783d4bf6bf3e00b3ec Mon Sep 17 00:00:00 2001 From: Yi LIU Date: Tue, 10 Feb 2026 09:28:01 +0800 Subject: [PATCH] Fix swapped mergeIf arguments in no-else case in Dataflow IR (#8273) In doVisitIf, when an if has no else branch, the two state arguments passed to mergeIf were in the wrong order. This caused the phi node to pair the "condition true" value with the initial state and the "condition false" value with the after-if-true state. --- src/dataflow/graph.h | 2 +- test/gtest/CMakeLists.txt | 1 + test/gtest/dataflow.cpp | 76 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 test/gtest/dataflow.cpp diff --git a/src/dataflow/graph.h b/src/dataflow/graph.h index 6bd01d8954a..3e6676474f4 100644 --- a/src/dataflow/graph.h +++ b/src/dataflow/graph.h @@ -274,7 +274,7 @@ struct Graph : public UnifiedExpressionVisitor { auto afterIfFalseState = locals; // TODO: optimize mergeIf(afterIfTrueState, afterIfFalseState, condition, curr, locals); } else { - mergeIf(initialState, afterIfTrueState, condition, curr, locals); + mergeIf(afterIfTrueState, initialState, condition, curr, locals); } parent = oldParent; return &bad; diff --git a/test/gtest/CMakeLists.txt b/test/gtest/CMakeLists.txt index ecd351bc489..058766f58e4 100644 --- a/test/gtest/CMakeLists.txt +++ b/test/gtest/CMakeLists.txt @@ -9,6 +9,7 @@ set(unittest_SOURCES arena.cpp cast-check.cpp cfg.cpp + dataflow.cpp dfa_minimization.cpp disjoint_sets.cpp glbs.cpp diff --git a/test/gtest/dataflow.cpp b/test/gtest/dataflow.cpp new file mode 100644 index 00000000000..23ab5fcc00f --- /dev/null +++ b/test/gtest/dataflow.cpp @@ -0,0 +1,76 @@ +#include "dataflow/graph.h" +#include "dataflow/node.h" +#include "parser/wat-parser.h" +#include "wasm.h" +#include "gtest/gtest.h" + +using namespace wasm; +using namespace wasm::DataFlow; + +class DataflowTest : public ::testing::Test { +protected: + void parseWast(Module& wasm, const std::string& wast) { + auto parsed = WATParser::parseModule(wasm, wast); + if (auto* err = parsed.getErr()) { + Fatal() << err->msg << "\n"; + } + } +}; + +// Regression test for https://github.com/WebAssembly/binaryen/issues/8273 +// In the no-else case, mergeIf arguments were swapped: the initial state +// (before the if body) was paired with the ifTrue condition and the +// afterIfTrue state was paired with ifFalse. This test verifies the fix. +TEST_F(DataflowTest, IfNoElseMergeOrder) { + auto moduleText = R"wasm( + (module + (func $test (param $cond i32) + (local $x i32) + (local.set $x (i32.const 10)) + (if (local.get $cond) + (then + (local.set $x (i32.const 42)) + ) + ) + (drop (local.get $x)) + ) + ) + )wasm"; + + Module wasm; + parseWast(wasm, moduleText); + + auto* func = wasm.getFunction("test"); + Graph graph; + graph.build(func, &wasm); + + // Find the phi node for local $x (index 1). + Node* phi = nullptr; + for (auto& node : graph.nodes) { + if (node->isPhi() && node->index == 1) { + phi = node.get(); + break; + } + } + ASSERT_NE(phi, nullptr) << "Expected a phi node for local $x"; + + // Phi structure: values[0] = block, values[1] = ifTrue value, + // values[2] = ifFalse value. + ASSERT_EQ(phi->values.size(), 3u); + + auto* ifTrueNode = phi->values[1]; + auto* ifFalseNode = phi->values[2]; + + ASSERT_TRUE(ifTrueNode->isConst()); + ASSERT_TRUE(ifFalseNode->isConst()); + + int32_t ifTrueValue = ifTrueNode->expr->cast()->value.geti32(); + int32_t ifFalseValue = ifFalseNode->expr->cast()->value.geti32(); + + // When condition is true (body ran), $x should be 42. + EXPECT_EQ(ifTrueValue, 42) + << "When condition is TRUE, phi should select 42 (set in ifTrue body)"; + // When condition is false (body skipped), $x should be 10 (initial value). + EXPECT_EQ(ifFalseValue, 10) + << "When condition is FALSE, phi should select 10 (initial value)"; +}