summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/CMakeLists.txt1
-rw-r--r--src/ir/ReFinalize.cpp198
-rw-r--r--src/ir/branch-utils.h6
-rw-r--r--src/ir/utils.h162
4 files changed, 242 insertions, 125 deletions
diff --git a/src/ir/CMakeLists.txt b/src/ir/CMakeLists.txt
index 607207968..e89ece6a2 100644
--- a/src/ir/CMakeLists.txt
+++ b/src/ir/CMakeLists.txt
@@ -2,5 +2,6 @@ SET(ir_SOURCES
ExpressionAnalyzer.cpp
ExpressionManipulator.cpp
LocalGraph.cpp
+ ReFinalize.cpp
)
ADD_LIBRARY(ir STATIC ${ir_SOURCES})
diff --git a/src/ir/ReFinalize.cpp b/src/ir/ReFinalize.cpp
new file mode 100644
index 000000000..31140837f
--- /dev/null
+++ b/src/ir/ReFinalize.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2018 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ir/branch-utils.h"
+#include "ir/find_all.h"
+#include "ir/utils.h"
+
+namespace wasm {
+
+static Type getValueType(Expression* value) {
+ return value ? value->type : none;
+}
+
+namespace {
+
+// Handles a branch fixup for visitBlock: if the branch goes to the
+// target name, give it a value which is unreachable.
+template<typename T>
+void handleBranchForVisitBlock(T* curr, Name name, Module* module) {
+ if (BranchUtils::getUniqueTargets(curr).count(name)) {
+ assert(!curr->value);
+ Builder builder(*module);
+ curr->value = builder.makeUnreachable();
+ }
+}
+
+} // anonymous namespace
+
+void ReFinalize::visitBlock(Block* curr) {
+ if (curr->list.size() == 0) {
+ curr->type = none;
+ return;
+ }
+ auto old = curr->type;
+ // do this quickly, without any validation
+ // last element determines type
+ curr->type = curr->list.back()->type;
+ // if concrete, it doesn't matter if we have an unreachable child, and we
+ // don't need to look at breaks
+ if (isConcreteType(curr->type)) {
+ // make sure our branches make sense for us - we may have just made ourselves
+ // concrete for a value flowing out, while branches did not send a value. such
+ // branches could not have been actually taken before, that is, there were in
+ // unreachable code, but we do still need to fix them up here.
+ if (!isConcreteType(old)) {
+ auto iter = breakValues.find(curr->name);
+ if (iter != breakValues.end()) {
+ // there is a break to here
+ auto type = iter->second;
+ if (type == none) {
+ // we need to fix this up. set the values to unreachables
+ for (auto* br : FindAll<Break>(curr).list) {
+ handleBranchForVisitBlock(br, curr->name, getModule());
+ }
+ for (auto* sw : FindAll<Switch>(curr).list) {
+ handleBranchForVisitBlock(sw, curr->name, getModule());
+ }
+ // and we need to propagate that type out, re-walk
+ ReFinalize fixer;
+ fixer.setModule(getModule());
+ Expression* temp = curr;
+ fixer.walk(temp);
+ assert(temp == curr);
+ }
+ }
+ }
+ return;
+ }
+ // otherwise, we have no final fallthrough element to determine the type,
+ // could be determined by breaks
+ if (curr->name.is()) {
+ auto iter = breakValues.find(curr->name);
+ if (iter != breakValues.end()) {
+ // there is a break to here
+ auto type = iter->second;
+ assert(type != unreachable); // we would have removed such branches
+ curr->type = type;
+ return;
+ }
+ }
+ if (curr->type == unreachable) return;
+ // type is none, but we might be unreachable
+ if (curr->type == none) {
+ for (auto* child : curr->list) {
+ if (child->type == unreachable) {
+ curr->type = unreachable;
+ break;
+ }
+ }
+ }
+}
+void ReFinalize::visitIf(If* curr) { curr->finalize(); }
+void ReFinalize::visitLoop(Loop* curr) { curr->finalize(); }
+void ReFinalize::visitBreak(Break* curr) {
+ curr->finalize();
+ auto valueType = getValueType(curr->value);
+ if (valueType == unreachable) {
+ replaceUntaken(curr->value, curr->condition);
+ } else {
+ updateBreakValueType(curr->name, valueType);
+ }
+}
+void ReFinalize::visitSwitch(Switch* curr) {
+ curr->finalize();
+ auto valueType = getValueType(curr->value);
+ if (valueType == unreachable) {
+ replaceUntaken(curr->value, curr->condition);
+ } else {
+ for (auto target : curr->targets) {
+ updateBreakValueType(target, valueType);
+ }
+ updateBreakValueType(curr->default_, valueType);
+ }
+}
+void ReFinalize::visitCall(Call* curr) { curr->finalize(); }
+void ReFinalize::visitCallIndirect(CallIndirect* curr) { curr->finalize(); }
+void ReFinalize::visitGetLocal(GetLocal* curr) { curr->finalize(); }
+void ReFinalize::visitSetLocal(SetLocal* curr) { curr->finalize(); }
+void ReFinalize::visitGetGlobal(GetGlobal* curr) { curr->finalize(); }
+void ReFinalize::visitSetGlobal(SetGlobal* curr) { curr->finalize(); }
+void ReFinalize::visitLoad(Load* curr) { curr->finalize(); }
+void ReFinalize::visitStore(Store* curr) { curr->finalize(); }
+void ReFinalize::visitAtomicRMW(AtomicRMW* curr) { curr->finalize(); }
+void ReFinalize::visitAtomicCmpxchg(AtomicCmpxchg* curr) { curr->finalize(); }
+void ReFinalize::visitAtomicWait(AtomicWait* curr) { curr->finalize(); }
+void ReFinalize::visitAtomicWake(AtomicWake* curr) { curr->finalize(); }
+void ReFinalize::visitConst(Const* curr) { curr->finalize(); }
+void ReFinalize::visitUnary(Unary* curr) { curr->finalize(); }
+void ReFinalize::visitBinary(Binary* curr) { curr->finalize(); }
+void ReFinalize::visitSelect(Select* curr) { curr->finalize(); }
+void ReFinalize::visitDrop(Drop* curr) { curr->finalize(); }
+void ReFinalize::visitReturn(Return* curr) { curr->finalize(); }
+void ReFinalize::visitHost(Host* curr) { curr->finalize(); }
+void ReFinalize::visitNop(Nop* curr) { curr->finalize(); }
+void ReFinalize::visitUnreachable(Unreachable* curr) { curr->finalize(); }
+
+void ReFinalize::visitFunction(Function* curr) {
+ // we may have changed the body from unreachable to none, which might be bad
+ // if the function has a return value
+ if (curr->result != none && curr->body->type == none) {
+ Builder builder(*getModule());
+ curr->body = builder.blockify(curr->body, builder.makeUnreachable());
+ }
+}
+
+void ReFinalize::visitFunctionType(FunctionType* curr) { WASM_UNREACHABLE(); }
+void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE(); }
+void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE(); }
+void ReFinalize::visitTable(Table* curr) { WASM_UNREACHABLE(); }
+void ReFinalize::visitMemory(Memory* curr) { WASM_UNREACHABLE(); }
+void ReFinalize::visitModule(Module* curr) { WASM_UNREACHABLE(); }
+
+void ReFinalize::updateBreakValueType(Name name, Type type) {
+ if (type != unreachable || breakValues.count(name) == 0) {
+ breakValues[name] = type;
+ }
+}
+
+// Replace an untaken branch/switch with an unreachable value.
+// A condition may also exist and may or may not be unreachable.
+void ReFinalize::replaceUntaken(Expression* value, Expression* condition) {
+ assert(value->type == unreachable);
+ auto* replacement = value;
+ if (condition) {
+ Builder builder(*getModule());
+ // Even if we have
+ // (block
+ // (unreachable)
+ // (i32.const 1)
+ // )
+ // we want the block type to be unreachable. That is valid as
+ // the value is unreachable, and necessary since the type of
+ // the condition did not have an impact before (the break/switch
+ // type was unreachable), and might not fit in.
+ if (isConcreteType(condition->type)) {
+ condition = builder.makeDrop(condition);
+ }
+ replacement = builder.makeSequence(value, condition);
+ assert(replacement->type);
+ }
+ replaceCurrent(replacement);
+}
+
+} // namespace wasm
+
diff --git a/src/ir/branch-utils.h b/src/ir/branch-utils.h
index 6f9299bf5..50c71e8c9 100644
--- a/src/ir/branch-utils.h
+++ b/src/ir/branch-utils.h
@@ -45,6 +45,12 @@ inline bool isBranchReachable(Expression* expr) {
WASM_UNREACHABLE();
}
+inline std::set<Name> getUniqueTargets(Break* br) {
+ std::set<Name> ret;
+ ret.insert(br->name);
+ return ret;
+}
+
inline std::set<Name> getUniqueTargets(Switch* sw) {
std::set<Name> ret;
for (auto target : sw->targets) {
diff --git a/src/ir/utils.h b/src/ir/utils.h
index 8b4e028f4..a4082b6bc 100644
--- a/src/ir/utils.h
+++ b/src/ir/utils.h
@@ -112,136 +112,48 @@ struct ReFinalize : public WalkerPass<PostWalker<ReFinalize, OverriddenVisitor<R
std::map<Name, Type> breakValues;
- void visitBlock(Block* curr) {
- if (curr->list.size() == 0) {
- curr->type = none;
- return;
- }
- // do this quickly, without any validation
- // last element determines type
- curr->type = curr->list.back()->type;
- // if concrete, it doesn't matter if we have an unreachable child, and we
- // don't need to look at breaks
- if (isConcreteType(curr->type)) return;
- // otherwise, we have no final fallthrough element to determine the type,
- // could be determined by breaks
- if (curr->name.is()) {
- auto iter = breakValues.find(curr->name);
- if (iter != breakValues.end()) {
- // there is a break to here
- auto type = iter->second;
- assert(type != unreachable); // we would have removed such branches
- curr->type = type;
- return;
- }
- }
- if (curr->type == unreachable) return;
- // type is none, but we might be unreachable
- if (curr->type == none) {
- for (auto* child : curr->list) {
- if (child->type == unreachable) {
- curr->type = unreachable;
- break;
- }
- }
- }
- }
- void visitIf(If* curr) { curr->finalize(); }
- void visitLoop(Loop* curr) { curr->finalize(); }
- void visitBreak(Break* curr) {
- curr->finalize();
- auto valueType = getValueType(curr->value);
- if (valueType == unreachable) {
- replaceUntaken(curr->value, curr->condition);
- } else {
- updateBreakValueType(curr->name, valueType);
- }
- }
- void visitSwitch(Switch* curr) {
- curr->finalize();
- auto valueType = getValueType(curr->value);
- if (valueType == unreachable) {
- replaceUntaken(curr->value, curr->condition);
- } else {
- for (auto target : curr->targets) {
- updateBreakValueType(target, valueType);
- }
- updateBreakValueType(curr->default_, valueType);
- }
- }
- void visitCall(Call* curr) { curr->finalize(); }
- void visitCallIndirect(CallIndirect* curr) { curr->finalize(); }
- void visitGetLocal(GetLocal* curr) { curr->finalize(); }
- void visitSetLocal(SetLocal* curr) { curr->finalize(); }
- void visitGetGlobal(GetGlobal* curr) { curr->finalize(); }
- void visitSetGlobal(SetGlobal* curr) { curr->finalize(); }
- void visitLoad(Load* curr) { curr->finalize(); }
- void visitStore(Store* curr) { curr->finalize(); }
- void visitAtomicRMW(AtomicRMW* curr) { curr->finalize(); }
- void visitAtomicCmpxchg(AtomicCmpxchg* curr) { curr->finalize(); }
- void visitAtomicWait(AtomicWait* curr) { curr->finalize(); }
- void visitAtomicWake(AtomicWake* curr) { curr->finalize(); }
- void visitConst(Const* curr) { curr->finalize(); }
- void visitUnary(Unary* curr) { curr->finalize(); }
- void visitBinary(Binary* curr) { curr->finalize(); }
- void visitSelect(Select* curr) { curr->finalize(); }
- void visitDrop(Drop* curr) { curr->finalize(); }
- void visitReturn(Return* curr) { curr->finalize(); }
- void visitHost(Host* curr) { curr->finalize(); }
- void visitNop(Nop* curr) { curr->finalize(); }
- void visitUnreachable(Unreachable* curr) { curr->finalize(); }
-
- void visitFunction(Function* curr) {
- // we may have changed the body from unreachable to none, which might be bad
- // if the function has a return value
- if (curr->result != none && curr->body->type == none) {
- Builder builder(*getModule());
- curr->body = builder.blockify(curr->body, builder.makeUnreachable());
- }
- }
-
- void visitFunctionType(FunctionType* curr) { WASM_UNREACHABLE(); }
- void visitExport(Export* curr) { WASM_UNREACHABLE(); }
- void visitGlobal(Global* curr) { WASM_UNREACHABLE(); }
- void visitTable(Table* curr) { WASM_UNREACHABLE(); }
- void visitMemory(Memory* curr) { WASM_UNREACHABLE(); }
- void visitModule(Module* curr) { WASM_UNREACHABLE(); }
+ void visitBlock(Block* curr);
+ void visitIf(If* curr);
+ void visitLoop(Loop* curr);
+ void visitBreak(Break* curr);
+ void visitSwitch(Switch* curr);
+ void visitCall(Call* curr);
+ void visitCallIndirect(CallIndirect* curr);
+ void visitGetLocal(GetLocal* curr);
+ void visitSetLocal(SetLocal* curr);
+ void visitGetGlobal(GetGlobal* curr);
+ void visitSetGlobal(SetGlobal* curr);
+ void visitLoad(Load* curr);
+ void visitStore(Store* curr);
+ void visitAtomicRMW(AtomicRMW* curr);
+ void visitAtomicCmpxchg(AtomicCmpxchg* curr);
+ void visitAtomicWait(AtomicWait* curr);
+ void visitAtomicWake(AtomicWake* curr);
+ void visitConst(Const* curr);
+ void visitUnary(Unary* curr);
+ void visitBinary(Binary* curr);
+ void visitSelect(Select* curr);
+ void visitDrop(Drop* curr);
+ void visitReturn(Return* curr);
+ void visitHost(Host* curr);
+ void visitNop(Nop* curr);
+ void visitUnreachable(Unreachable* curr);
+
+ void visitFunction(Function* curr);
+
+ void visitFunctionType(FunctionType* curr);
+ void visitExport(Export* curr);
+ void visitGlobal(Global* curr);
+ void visitTable(Table* curr);
+ void visitMemory(Memory* curr);
+ void visitModule(Module* curr);
private:
- Type getValueType(Expression* value) {
- return value ? value->type : none;
- }
-
- void updateBreakValueType(Name name, Type type) {
- if (type != unreachable || breakValues.count(name) == 0) {
- breakValues[name] = type;
- }
- }
+ void updateBreakValueType(Name name, Type type);
// Replace an untaken branch/switch with an unreachable value.
// A condition may also exist and may or may not be unreachable.
- void replaceUntaken(Expression* value, Expression* condition) {
- assert(value->type == unreachable);
- auto* replacement = value;
- if (condition) {
- Builder builder(*getModule());
- // Even if we have
- // (block
- // (unreachable)
- // (i32.const 1)
- // )
- // we want the block type to be unreachable. That is valid as
- // the value is unreachable, and necessary since the type of
- // the condition did not have an impact before (the break/switch
- // type was unreachable), and might not fit in.
- if (isConcreteType(condition->type)) {
- condition = builder.makeDrop(condition);
- }
- replacement = builder.makeSequence(value, condition);
- assert(replacement->type);
- }
- replaceCurrent(replacement);
- }
+ void replaceUntaken(Expression* value, Expression* condition);
};
// Re-finalize a single node. This is slow, if you want to refinalize