diff options
Diffstat (limited to 'src/ir/branch-utils.h')
-rw-r--r-- | src/ir/branch-utils.h | 212 |
1 files changed, 98 insertions, 114 deletions
diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h index 9b439be89..e6a3486ca 100644 --- a/src/ir/branch-utils.h +++ b/src/ir/branch-utils.h @@ -28,111 +28,106 @@ namespace BranchUtils { // Some branches are obviously not actually reachable (e.g. (br $out // (unreachable))) -inline bool isBranchReachable(Break* br) { - return !(br->value && br->value->type == Type::unreachable) && - !(br->condition && br->condition->type == Type::unreachable); +inline bool isBranchReachable(Expression* expr) { + // If any child is unreachable, the branch is not taken. Note that expr itself + // may be unreachable regardless (as in the case of a simple Break with no + // condition, which is still taken). + for (auto child : ChildIterator(expr)) { + if (child->type == Type::unreachable) { + return false; + } + } + return true; } -inline bool isBranchReachable(Switch* sw) { - return !(sw->value && sw->value->type == Type::unreachable) && - sw->condition->type != Type::unreachable; -} +// Perform a generic operation on uses of scope names (branch targets) in an +// expression. The provided function receives a Name& which it can modify if it +// needs to. +template<typename T> void operateOnScopeNameUses(Expression* expr, T func) { +#define DELEGATE_ID expr->_id + +#define DELEGATE_START(id) \ + auto* cast = expr->cast<id>(); \ + WASM_UNUSED(cast); -inline bool isBranchReachable(BrOnExn* br) { - return br->exnref->type != Type::unreachable; +#define DELEGATE_GET_FIELD(id, name) cast->name + +#define DELEGATE_FIELD_SCOPE_NAME_USE(id, name) func(cast->name); + +#define DELEGATE_FIELD_CHILD(id, name) +#define DELEGATE_FIELD_INT(id, name) +#define DELEGATE_FIELD_LITERAL(id, name) +#define DELEGATE_FIELD_NAME(id, name) +#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, name) +#define DELEGATE_FIELD_SIGNATURE(id, name) +#define DELEGATE_FIELD_TYPE(id, name) +#define DELEGATE_FIELD_ADDRESS(id, name) +#define DELEGATE_FIELD_CHILD_VECTOR(id, name) +#define DELEGATE_FIELD_INT_ARRAY(id, name) + +#include "wasm-delegations-fields.h" } -inline bool isBranchReachable(Expression* expr) { - if (auto* br = expr->dynCast<Break>()) { - return isBranchReachable(br); - } else if (auto* sw = expr->dynCast<Switch>()) { - return isBranchReachable(sw); - } else if (auto* br = expr->dynCast<BrOnExn>()) { - return isBranchReachable(br); - } - WASM_UNREACHABLE("unexpected expression type"); +// Perform a generic operation on definitions of scope names in an expression. +// The provided function receives a Name& which it can modify if it needs to. +template<typename T> void operateOnScopeNameDefs(Expression* expr, T func) { +#define DELEGATE_ID expr->_id + +#define DELEGATE_START(id) \ + auto* cast = expr->cast<id>(); \ + WASM_UNUSED(cast); + +#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, name) func(cast->name) + +#define DELEGATE_FIELD_CHILD(id, name) +#define DELEGATE_FIELD_INT(id, name) +#define DELEGATE_FIELD_LITERAL(id, name) +#define DELEGATE_FIELD_NAME(id, name) +#define DELEGATE_FIELD_SIGNATURE(id, name) +#define DELEGATE_FIELD_TYPE(id, name) +#define DELEGATE_FIELD_ADDRESS(id, name) +#define DELEGATE_FIELD_CHILD_VECTOR(id, name) +#define DELEGATE_FIELD_INT_ARRAY(id, name) +#define DELEGATE_FIELD_SCOPE_NAME_USE(id, name) +#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, name) + +#include "wasm-delegations-fields.h" } using NameSet = std::set<Name>; -inline NameSet getUniqueTargets(Break* br) { return {br->name}; } - -inline NameSet getUniqueTargets(Switch* sw) { +inline NameSet getUniqueTargets(Expression* expr) { NameSet ret; - for (auto target : sw->targets) { - ret.insert(target); - } - ret.insert(sw->default_); + operateOnScopeNameUses(expr, [&](Name& name) { ret.insert(name); }); return ret; } -inline NameSet getUniqueTargets(BrOnExn* br) { return {br->name}; } - -inline NameSet getUniqueTargets(Expression* expr) { - if (auto* br = expr->dynCast<Break>()) { - return getUniqueTargets(br); - } - if (auto* br = expr->dynCast<Switch>()) { - return getUniqueTargets(br); - } - if (auto* br = expr->dynCast<BrOnExn>()) { - return getUniqueTargets(br); - } - return {}; -} // If we branch to 'from', change that to 'to' instead. inline bool replacePossibleTarget(Expression* branch, Name from, Name to) { bool worked = false; - if (auto* br = branch->dynCast<Break>()) { - if (br->name == from) { - br->name = to; - worked = true; - } - } else if (auto* sw = branch->dynCast<Switch>()) { - for (auto& target : sw->targets) { - if (target == from) { - target = to; - worked = true; - } - } - if (sw->default_ == from) { - sw->default_ = to; - worked = true; - } - } else if (auto* br = branch->dynCast<BrOnExn>()) { - if (br->name == from) { - br->name = to; + operateOnScopeNameUses(branch, [&](Name& name) { + if (name == from) { + name = to; worked = true; } - } else { - WASM_UNREACHABLE("unexpected expression type"); - } + }); return worked; } -// returns the set of targets to which we branch that are -// outside of a node +// Returns the set of targets to which we branch that are +// outside of an expression. inline NameSet getExitingBranches(Expression* ast) { - struct Scanner : public PostWalker<Scanner> { + struct Scanner + : public PostWalker<Scanner, UnifiedExpressionVisitor<Scanner>> { NameSet targets; - void visitBreak(Break* curr) { targets.insert(curr->name); } - void visitSwitch(Switch* curr) { - for (auto target : curr->targets) { - targets.insert(target); - } - targets.insert(curr->default_); - } - void visitBrOnExn(BrOnExn* curr) { targets.insert(curr->name); } - void visitBlock(Block* curr) { - if (curr->name.is()) { - targets.erase(curr->name); - } - } - void visitLoop(Loop* curr) { - if (curr->name.is()) { - targets.erase(curr->name); - } + void visitExpression(Expression* curr) { + operateOnScopeNameDefs(curr, [&](Name& name) { + if (name.is()) { + targets.erase(name); + } + }); + operateOnScopeNameUses(curr, [&](Name& name) { targets.insert(name); }); } }; Scanner scanner; @@ -144,18 +139,16 @@ inline NameSet getExitingBranches(Expression* ast) { // returns the list of all branch targets in a node inline NameSet getBranchTargets(Expression* ast) { - struct Scanner : public PostWalker<Scanner> { + struct Scanner + : public PostWalker<Scanner, UnifiedExpressionVisitor<Scanner>> { NameSet targets; - void visitBlock(Block* curr) { - if (curr->name.is()) { - targets.insert(curr->name); - } - } - void visitLoop(Loop* curr) { - if (curr->name.is()) { - targets.insert(curr->name); - } + void visitExpression(Expression* curr) { + operateOnScopeNameDefs(curr, [&](Name& name) { + if (name.is()) { + targets.insert(name); + } + }); } }; Scanner scanner; @@ -166,7 +159,8 @@ inline NameSet getBranchTargets(Expression* ast) { // Finds if there are branches targeting a name. Note that since names are // unique in our IR, we just need to look for the name, and do not need // to analyze scoping. -struct BranchSeeker : public PostWalker<BranchSeeker> { +struct BranchSeeker + : public PostWalker<BranchSeeker, UnifiedExpressionVisitor<BranchSeeker>> { Name target; Index found = 0; @@ -188,30 +182,20 @@ struct BranchSeeker : public PostWalker<BranchSeeker> { } } - void visitBreak(Break* curr) { - // check the break - if (curr->name == target) { - noteFound(curr->value); - } - } - - void visitSwitch(Switch* curr) { - // check the switch - for (auto name : curr->targets) { + void visitExpression(Expression* curr) { + operateOnScopeNameUses(curr, [&](Name& name) { if (name == target) { - noteFound(curr->value); + if (auto* br = curr->dynCast<Break>()) { + noteFound(br->value); + } else if (auto* sw = curr->dynCast<Switch>()) { + noteFound(sw->value); + } else if (auto* br = curr->dynCast<BrOnExn>()) { + noteFound(br->sent); + } else { + WASM_UNREACHABLE("bad br type"); + } } - } - if (curr->default_ == target) { - noteFound(curr->value); - } - } - - void visitBrOnExn(BrOnExn* curr) { - // check the br_on_exn - if (curr->name == target) { - noteFound(curr->sent); - } + }); } static bool has(Expression* tree, Name target) { |