Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/dataflow/graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ struct Graph : public UnifiedExpressionVisitor<Graph, Node*> {
auto afterIfFalseState = locals; // TODO: optimize
mergeIf(afterIfTrueState, afterIfFalseState, condition, curr, locals);
} else {
mergeIf(initialState, afterIfTrueState, condition, curr, locals);
mergeIf(afterIfTrueState, initialState, condition, curr, locals);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does the order matter here? I can't figure that out either from the code or from the PR description, sorry.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's so that the phi value indices are the same for the ifTrue and ifFalse edges in both the if-end and if-else-end cases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks, looks like mergeIf assumes the first is executed if the condition is true. Makes sense.

I opened #8299 to clarify.

}
parent = oldParent;
return &bad;
Expand Down
1 change: 1 addition & 0 deletions test/gtest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ set(unittest_SOURCES
arena.cpp
cast-check.cpp
cfg.cpp
dataflow.cpp
dfa_minimization.cpp
disjoint_sets.cpp
glbs.cpp
Expand Down
76 changes: 76 additions & 0 deletions test/gtest/dataflow.cpp
Original file line number Diff line number Diff line change
@@ -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<Const>()->value.geti32();
int32_t ifFalseValue = ifFalseNode->expr->cast<Const>()->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)";
}
Loading