From e46c68c9cb59bc05b369faa8d09a74129f098d13 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Apr 2026 13:15:33 -0700 Subject: [PATCH 1/9] go --- src/wasm-traversal.h | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 9a5c5d83226..39f0978f447 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -343,6 +343,28 @@ struct Walker : public VisitorType { Module* currModule = nullptr; // current module being processed }; +// Define which expression classes are leaves. We can handle them more +// optimally below. The accuracy of this list is tested in XXX +template +struct IsLeaf : std::false_type {}; + +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; +template <> struct IsLeaf : std::true_type {}; + // Walks in post-order, i.e., children first. When there isn't an obvious // order to operands, we follow them in order of execution. @@ -369,6 +391,8 @@ struct PostWalker : public Walker { // Note that even if this ends up being a runtime check, it should be faster // than pushing empty tasks, as the check is much faster than the push/pop/ // call, and a large number of our calls (most, perhaps) are not overridden. + // + // Likewise, just visit TODO comment + test #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 11 #define DELEGATE_START(id) \ if (&SubType::visit##id != \ @@ -377,17 +401,21 @@ struct PostWalker : public Walker { self->pushTask(SubType::doVisit##id, currp); \ } \ [[maybe_unused]] auto* cast = curr->cast(); -#else +#else // constexpr #define DELEGATE_START(id) \ if constexpr (&SubType::visit##id != \ &Visitor::visit##id || \ &SubType::doVisit##id != \ &Walker::doVisit##id) { \ - self->pushTask(SubType::doVisit##id, currp); \ - } \ + if constexpr (IsLeaf::value && &SubType::scan == &PostWalker::scan ) { \ + SubType::doVisit##id(self, currp); \ + } else { \ + self->pushTask(SubType::doVisit##id, currp); \ + } \ + } \ [[maybe_unused]] auto* cast = curr->cast(); -#endif +#endif // constexpr #define DELEGATE_GET_FIELD(id, field) cast->field From d6161fd7e22fef34c4efaebbd25ef009933c1924 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Apr 2026 13:22:22 -0700 Subject: [PATCH 2/9] format --- src/wasm-traversal.h | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 39f0978f447..8e09db0b210 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -345,25 +345,24 @@ struct Walker : public VisitorType { // Define which expression classes are leaves. We can handle them more // optimally below. The accuracy of this list is tested in XXX -template -struct IsLeaf : std::false_type {}; - -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; -template <> struct IsLeaf : std::true_type {}; +template struct IsLeaf : std::false_type {}; + +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; +template<> struct IsLeaf : std::true_type {}; // Walks in post-order, i.e., children first. When there isn't an obvious // order to operands, we follow them in order of execution. @@ -408,12 +407,13 @@ struct PostWalker : public Walker { typename SubType::ReturnType>::visit##id || \ &SubType::doVisit##id != \ &Walker::doVisit##id) { \ - if constexpr (IsLeaf::value && &SubType::scan == &PostWalker::scan ) { \ - SubType::doVisit##id(self, currp); \ - } else { \ + if constexpr (IsLeaf::value && \ + &SubType::scan == &PostWalker::scan) { \ + SubType::doVisit##id(self, currp); \ + } else { \ self->pushTask(SubType::doVisit##id, currp); \ - } \ - } \ + } \ + } \ [[maybe_unused]] auto* cast = curr->cast(); #endif // constexpr From d608baf9e6e135029036ad0f0a30c18183351875 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Apr 2026 13:42:16 -0700 Subject: [PATCH 3/9] go --- src/wasm-traversal.h | 6 ++-- test/gtest/CMakeLists.txt | 1 + test/gtest/leaves.cpp | 71 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 test/gtest/leaves.cpp diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 8e09db0b210..d2703671f35 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -344,7 +344,7 @@ struct Walker : public VisitorType { }; // Define which expression classes are leaves. We can handle them more -// optimally below. The accuracy of this list is tested in XXX +// optimally below. The accuracy of this list is tested in leaves.cpp. template struct IsLeaf : std::false_type {}; template<> struct IsLeaf : std::true_type {}; @@ -391,7 +391,9 @@ struct PostWalker : public Walker { // than pushing empty tasks, as the check is much faster than the push/pop/ // call, and a large number of our calls (most, perhaps) are not overridden. // - // Likewise, just visit TODO comment + test + // If we do *not* have an empty visitor, we can still optimize in the case + // of a leaf: leaves have no children, so we can just call doVisit* rather + // than push that task, pop it later, and call that. #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 11 #define DELEGATE_START(id) \ if (&SubType::visit##id != \ diff --git a/test/gtest/CMakeLists.txt b/test/gtest/CMakeLists.txt index 058766f58e4..41d16f28e92 100644 --- a/test/gtest/CMakeLists.txt +++ b/test/gtest/CMakeLists.txt @@ -12,6 +12,7 @@ set(unittest_SOURCES dataflow.cpp dfa_minimization.cpp disjoint_sets.cpp + leaves.cpp glbs.cpp interpreter.cpp intervals.cpp diff --git a/test/gtest/leaves.cpp b/test/gtest/leaves.cpp new file mode 100644 index 00000000000..43530f6a42b --- /dev/null +++ b/test/gtest/leaves.cpp @@ -0,0 +1,71 @@ +#include "wasm.h" +#include "wasm-traversal.h" + +#include "gtest/gtest.h" + +using LeavesTest = ::testing::Test; + +using namespace wasm; + +TEST_F(LeavesTest, Manual) { + // Verify some interesting cases manually. + + // LocalGet is a leaf. + EXPECT_TRUE(IsLeaf::value); + // GlobalSet is not a leaf due to a child. + EXPECT_FALSE(IsLeaf::value); + // Return is not a leaf due to an optional child. + EXPECT_FALSE(IsLeaf::value); + // Call is not a leaf due to a vector of children. + EXPECT_FALSE(IsLeaf::value); +} + +TEST_F(LeavesTest, Automatic) { + // Verify them all automatically. + + // Count total expression classes and total with children. + size_t total = 0, totalWithChildren = 0; + +#define DELEGATE_START(id) \ + bool hasChildren = false; + +#define DELEGATE_FIELD_CHILD(id, field) \ + hasChildren = true; + +#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) \ + hasChildren = true; + +#define DELEGATE_FIELD_CHILD_VECTOR(id, field) \ + hasChildren = true; + + // Verify that IsLeaf has the right value. +#define DELEGATE_END(id) \ + EXPECT_EQ(IsLeaf::value, hasChildren); \ + total++; \ + if (hasChildren) { \ + totalWithChildren++; \ + } + +#define DELEGATE_FIELD_INT(id, field) +#define DELEGATE_FIELD_LITERAL(id, field) +#define DELEGATE_FIELD_NAME(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field) +#define DELEGATE_FIELD_TYPE(id, field) +#define DELEGATE_FIELD_HEAPTYPE(id, field) +#define DELEGATE_FIELD_ADDRESS(id, field) +#define DELEGATE_FIELD_INT_ARRAY(id, field) +#define DELEGATE_FIELD_INT_VECTOR(id, field) +#define DELEGATE_FIELD_NAME_VECTOR(id, field) +#define DELEGATE_FIELD_NAME_USE_VECTOR(id, field) +#define DELEGATE_FIELD_TYPE_VECTOR(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) + +#include "wasm-delegations-fields.def" + + // Not all have children (this just verifies the macros are actually doing + // something). + EXPECT_LT(totalWithChildren, total); + EXPECT_GT(totalWithChildren, 0); + EXPECT_GT(total, 0); +} From d77ec9b5d7b7b6daf19677d89b6587e7e4afa56e Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Apr 2026 13:51:49 -0700 Subject: [PATCH 4/9] test --- test/gtest/leaves.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/test/gtest/leaves.cpp b/test/gtest/leaves.cpp index 43530f6a42b..d64605fa40d 100644 --- a/test/gtest/leaves.cpp +++ b/test/gtest/leaves.cpp @@ -26,24 +26,26 @@ TEST_F(LeavesTest, Automatic) { // Count total expression classes and total with children. size_t total = 0, totalWithChildren = 0; -#define DELEGATE_START(id) \ - bool hasChildren = false; +#define DELEGATE_FIELD_CASE_START(id) \ + { \ + bool hasChildren = false; #define DELEGATE_FIELD_CHILD(id, field) \ - hasChildren = true; + hasChildren = true; #define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) \ - hasChildren = true; + hasChildren = true; #define DELEGATE_FIELD_CHILD_VECTOR(id, field) \ - hasChildren = true; + hasChildren = true; // Verify that IsLeaf has the right value. -#define DELEGATE_END(id) \ - EXPECT_EQ(IsLeaf::value, hasChildren); \ - total++; \ - if (hasChildren) { \ - totalWithChildren++; \ +#define DELEGATE_FIELD_CASE_END(id) \ + EXPECT_EQ(IsLeaf::value, !hasChildren); \ + total++; \ + if (hasChildren) { \ + totalWithChildren++; \ + } \ } #define DELEGATE_FIELD_INT(id, field) @@ -61,6 +63,9 @@ TEST_F(LeavesTest, Automatic) { #define DELEGATE_FIELD_TYPE_VECTOR(id, field) #define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) +#define DELEGATE_FIELD_MAIN_START +#define DELEGATE_FIELD_MAIN_END + #include "wasm-delegations-fields.def" // Not all have children (this just verifies the macros are actually doing From f8ca8aa1f6b55403841bea4fcbc3cf9d417f3e40 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Apr 2026 13:51:59 -0700 Subject: [PATCH 5/9] format --- test/gtest/leaves.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/test/gtest/leaves.cpp b/test/gtest/leaves.cpp index d64605fa40d..2457f0d35f7 100644 --- a/test/gtest/leaves.cpp +++ b/test/gtest/leaves.cpp @@ -1,5 +1,5 @@ -#include "wasm.h" #include "wasm-traversal.h" +#include "wasm.h" #include "gtest/gtest.h" @@ -26,26 +26,23 @@ TEST_F(LeavesTest, Automatic) { // Count total expression classes and total with children. size_t total = 0, totalWithChildren = 0; -#define DELEGATE_FIELD_CASE_START(id) \ - { \ +#define DELEGATE_FIELD_CASE_START(id) \ + { \ bool hasChildren = false; -#define DELEGATE_FIELD_CHILD(id, field) \ - hasChildren = true; +#define DELEGATE_FIELD_CHILD(id, field) hasChildren = true; -#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) \ - hasChildren = true; +#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) hasChildren = true; -#define DELEGATE_FIELD_CHILD_VECTOR(id, field) \ - hasChildren = true; +#define DELEGATE_FIELD_CHILD_VECTOR(id, field) hasChildren = true; // Verify that IsLeaf has the right value. -#define DELEGATE_FIELD_CASE_END(id) \ - EXPECT_EQ(IsLeaf::value, !hasChildren); \ - total++; \ - if (hasChildren) { \ - totalWithChildren++; \ - } \ +#define DELEGATE_FIELD_CASE_END(id) \ + EXPECT_EQ(IsLeaf::value, !hasChildren); \ + total++; \ + if (hasChildren) { \ + totalWithChildren++; \ + } \ } #define DELEGATE_FIELD_INT(id, field) From a24e44e9fd1871accd5fa0d6b0d71a10d418e4d4 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Apr 2026 13:55:05 -0700 Subject: [PATCH 6/9] form --- test/gtest/leaves.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/gtest/leaves.cpp b/test/gtest/leaves.cpp index 2457f0d35f7..8d4a5a7d284 100644 --- a/test/gtest/leaves.cpp +++ b/test/gtest/leaves.cpp @@ -38,11 +38,11 @@ TEST_F(LeavesTest, Automatic) { // Verify that IsLeaf has the right value. #define DELEGATE_FIELD_CASE_END(id) \ - EXPECT_EQ(IsLeaf::value, !hasChildren); \ - total++; \ - if (hasChildren) { \ - totalWithChildren++; \ - } \ + EXPECT_EQ(IsLeaf::value, !hasChildren); \ + total++; \ + if (hasChildren) { \ + totalWithChildren++; \ + } \ } #define DELEGATE_FIELD_INT(id, field) From 1a871ef43d434a493bbd8b4a692c67fb7483c853 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Apr 2026 13:55:13 -0700 Subject: [PATCH 7/9] fmrt --- test/gtest/leaves.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/gtest/leaves.cpp b/test/gtest/leaves.cpp index 8d4a5a7d284..2457f0d35f7 100644 --- a/test/gtest/leaves.cpp +++ b/test/gtest/leaves.cpp @@ -38,11 +38,11 @@ TEST_F(LeavesTest, Automatic) { // Verify that IsLeaf has the right value. #define DELEGATE_FIELD_CASE_END(id) \ - EXPECT_EQ(IsLeaf::value, !hasChildren); \ - total++; \ - if (hasChildren) { \ - totalWithChildren++; \ - } \ + EXPECT_EQ(IsLeaf::value, !hasChildren); \ + total++; \ + if (hasChildren) { \ + totalWithChildren++; \ + } \ } #define DELEGATE_FIELD_INT(id, field) From ed34a153e158b50ed62f7b55b0fcdb6477590e0b Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Apr 2026 15:27:38 -0700 Subject: [PATCH 8/9] fix --- src/wasm-traversal.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index d2703671f35..4178546a4a3 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -411,10 +411,10 @@ struct PostWalker : public Walker { &Walker::doVisit##id) { \ if constexpr (IsLeaf::value && \ &SubType::scan == &PostWalker::scan) { \ - SubType::doVisit##id(self, currp); \ - } else { \ - self->pushTask(SubType::doVisit##id, currp); \ - } \ + SubType::doVisit##id(self, currp); \ + return; \ + } \ + self->pushTask(SubType::doVisit##id, currp); \ } \ [[maybe_unused]] auto* cast = curr->cast(); #endif // constexpr From f895c0e070acabc6b3df7a886bba9be8c3b7e3aa Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Tue, 7 Apr 2026 15:27:58 -0700 Subject: [PATCH 9/9] fix --- src/wasm-traversal.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wasm-traversal.h b/src/wasm-traversal.h index 4178546a4a3..9717c053d8c 100644 --- a/src/wasm-traversal.h +++ b/src/wasm-traversal.h @@ -411,10 +411,10 @@ struct PostWalker : public Walker { &Walker::doVisit##id) { \ if constexpr (IsLeaf::value && \ &SubType::scan == &PostWalker::scan) { \ - SubType::doVisit##id(self, currp); \ - return; \ - } \ - self->pushTask(SubType::doVisit##id, currp); \ + SubType::doVisit##id(self, currp); \ + return; \ + } \ + self->pushTask(SubType::doVisit##id, currp); \ } \ [[maybe_unused]] auto* cast = curr->cast(); #endif // constexpr