summaryrefslogtreecommitdiff
path: root/src/ast/branch-utils.h
diff options
context:
space:
mode:
authorAlon Zakai (kripken) <alonzakai@gmail.com>2017-07-10 11:22:19 -0700
committerAlon Zakai (kripken) <alonzakai@gmail.com>2017-07-11 11:07:44 -0700
commit987003944456c3d04f74c12067ac950feca1a81e (patch)
tree0d5d10bee94688ebed866e2c9399e3adf45f6b4a /src/ast/branch-utils.h
parent51f26947d7fe801224115abdd601d738eea8ee8d (diff)
downloadbinaryen-987003944456c3d04f74c12067ac950feca1a81e.tar.gz
binaryen-987003944456c3d04f74c12067ac950feca1a81e.tar.bz2
binaryen-987003944456c3d04f74c12067ac950feca1a81e.zip
add the option to seek named breaks, not just taken breaks; refactor headers to make this practical
Diffstat (limited to 'src/ast/branch-utils.h')
-rw-r--r--src/ast/branch-utils.h75
1 files changed, 75 insertions, 0 deletions
diff --git a/src/ast/branch-utils.h b/src/ast/branch-utils.h
index 853b998af..77ec70753 100644
--- a/src/ast/branch-utils.h
+++ b/src/ast/branch-utils.h
@@ -100,6 +100,81 @@ inline std::set<Name> getBranchTargets(Expression* ast) {
return scanner.targets;
}
+// 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.
+// By default we ignore untaken branches. You can set named to
+// note those as well, then any named branch is noted, even if untaken
+struct BranchSeeker : public PostWalker<BranchSeeker> {
+ Name target;
+ bool named = false;
+
+ Index found;
+ WasmType valueType;
+
+ BranchSeeker(Name target) : target(target), found(0) {}
+
+ void noteFound(Expression* value) {
+ found++;
+ if (found == 1) valueType = unreachable;
+ if (!value) valueType = none;
+ else if (value->type != unreachable) valueType = value->type;
+ }
+
+ void visitBreak(Break *curr) {
+ if (!named) {
+ // ignore an unreachable break
+ if (curr->condition && curr->condition->type == unreachable) return;
+ if (curr->value && curr->value->type == unreachable) return;
+ }
+ // check the break
+ if (curr->name == target) noteFound(curr->value);
+ }
+
+ void visitSwitch(Switch *curr) {
+ if (!named) {
+ // ignore an unreachable switch
+ if (curr->condition->type == unreachable) return;
+ if (curr->value && curr->value->type == unreachable) return;
+ }
+ // check the switch
+ for (auto name : curr->targets) {
+ if (name == target) noteFound(curr->value);
+ }
+ if (curr->default_ == target) noteFound(curr->value);
+ }
+
+ static bool has(Expression* tree, Name target) {
+ if (!target.is()) return false;
+ BranchSeeker seeker(target);
+ seeker.walk(tree);
+ return seeker.found > 0;
+ }
+
+ static Index count(Expression* tree, Name target) {
+ if (!target.is()) return 0;
+ BranchSeeker seeker(target);
+ seeker.walk(tree);
+ return seeker.found;
+ }
+
+ static bool hasNamed(Expression* tree, Name target) {
+ if (!target.is()) return false;
+ BranchSeeker seeker(target);
+ seeker.named = true;
+ seeker.walk(tree);
+ return seeker.found > 0;
+ }
+
+ static Index countNamed(Expression* tree, Name target) {
+ if (!target.is()) return 0;
+ BranchSeeker seeker(target);
+ seeker.named = true;
+ seeker.walk(tree);
+ return seeker.found;
+ }
+};
+
} // namespace BranchUtils
} // namespace wasm