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
34 changes: 32 additions & 2 deletions src/wasm-traversal.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,27 @@ 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 leaves.cpp.
template<typename T> struct IsLeaf : std::false_type {};

template<> struct IsLeaf<LocalGet> : std::true_type {};
template<> struct IsLeaf<GlobalGet> : std::true_type {};
template<> struct IsLeaf<AtomicFence> : std::true_type {};
template<> struct IsLeaf<Pause> : std::true_type {};
template<> struct IsLeaf<DataDrop> : std::true_type {};
template<> struct IsLeaf<Const> : std::true_type {};
template<> struct IsLeaf<MemorySize> : std::true_type {};
template<> struct IsLeaf<RefNull> : std::true_type {};
template<> struct IsLeaf<RefFunc> : std::true_type {};
template<> struct IsLeaf<TableSize> : std::true_type {};
template<> struct IsLeaf<ElemDrop> : std::true_type {};
template<> struct IsLeaf<Rethrow> : std::true_type {};
template<> struct IsLeaf<Nop> : std::true_type {};
template<> struct IsLeaf<Unreachable> : std::true_type {};
template<> struct IsLeaf<Pop> : std::true_type {};
template<> struct IsLeaf<StringConst> : 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.

Expand All @@ -369,6 +390,10 @@ struct PostWalker : public Walker<SubType, VisitorType> {
// 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.
//
// 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 != \
Expand All @@ -377,17 +402,22 @@ struct PostWalker : public Walker<SubType, VisitorType> {
self->pushTask(SubType::doVisit##id, currp); \
} \
[[maybe_unused]] auto* cast = curr->cast<id>();
#else
#else // constexpr
#define DELEGATE_START(id) \
if constexpr (&SubType::visit##id != \
&Visitor<SubType, \
typename SubType::ReturnType>::visit##id || \
&SubType::doVisit##id != \
&Walker<SubType, VisitorType>::doVisit##id) { \
if constexpr (IsLeaf<id>::value && \
&SubType::scan == &PostWalker<SubType, VisitorType>::scan) { \
SubType::doVisit##id(self, currp); \
return; \
} \
self->pushTask(SubType::doVisit##id, currp); \
} \
[[maybe_unused]] auto* cast = curr->cast<id>();
#endif
#endif // constexpr

#define DELEGATE_GET_FIELD(id, field) cast->field

Expand Down
1 change: 1 addition & 0 deletions test/gtest/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ set(unittest_SOURCES
dataflow.cpp
dfa_minimization.cpp
disjoint_sets.cpp
leaves.cpp
glbs.cpp
interpreter.cpp
intervals.cpp
Expand Down
73 changes: 73 additions & 0 deletions test/gtest/leaves.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include "wasm-traversal.h"
#include "wasm.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<LocalGet>::value);
// GlobalSet is not a leaf due to a child.
EXPECT_FALSE(IsLeaf<GlobalSet>::value);
// Return is not a leaf due to an optional child.
EXPECT_FALSE(IsLeaf<Return>::value);
// Call is not a leaf due to a vector of children.
EXPECT_FALSE(IsLeaf<Call>::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_FIELD_CASE_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_FIELD_CASE_END(id) \
EXPECT_EQ(IsLeaf<id>::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)

#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
// something).
EXPECT_LT(totalWithChildren, total);
EXPECT_GT(totalWithChildren, 0);
EXPECT_GT(total, 0);
}
Loading