summaryrefslogtreecommitdiff
path: root/src/dataflow/node.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/dataflow/node.h')
-rw-r--r--src/dataflow/node.h210
1 files changed, 210 insertions, 0 deletions
diff --git a/src/dataflow/node.h b/src/dataflow/node.h
new file mode 100644
index 000000000..d6514588e
--- /dev/null
+++ b/src/dataflow/node.h
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ */
+
+//
+// DataFlow IR is an SSA representation. It can be built from the main
+// Binaryen IR.
+//
+// THe main initial use case was an IR that could easily be converted to
+// Souper IR, and the design favors that.
+//
+
+#ifndef wasm_dataflow_node_h
+#define wasm_dataflow_node_h
+
+#include "wasm.h"
+
+namespace wasm {
+
+namespace DataFlow {
+
+//
+// The core IR representation in DataFlow: a Node.
+//
+// We reuse the Binaryen IR as much as possible: when things are identical between
+// the two IRs, we just create an Expr node, which stores the opcode and other
+// details, and we can emit them to Souper by reading the Binaryen Expression.
+// Other node types here are special things from Souper IR that we can't
+// represent that way.
+//
+// * Souper comparisons return an i1. We extend them immediately if they are
+// going to be used as i32s or i64s.
+// * When we use an Expression node, we just use its immediate fields, like the
+// op in a binary, alignment etc. in a load, etc. We don't look into the
+// pointers to child nodes. Instead, the DataFlow IR has its own pointers
+// directly to DataFlow children. In particular, this means that it's easy
+// to create an Expression with the info you need and not care about linking
+// it up to other Expressions.
+//
+
+struct Node {
+ enum Type {
+ Var, // an unknown variable number (not to be confused with var/param/local in wasm)
+ Expr, // a value represented by a Binaryen Expression
+ Phi, // a phi from converging control flow
+ Cond, // a blockpc, representing one of the branchs for a Block
+ Block, // a source of phis
+ Zext, // zero-extend an i1 (from an op where Souper returns i1 but wasm does not,
+ // and so we need a special way to get back to an i32/i64 if we operate
+ // on that value instead of just passing it straight to Souper).
+ Bad // something we can't handle and should ignore
+ } type;
+
+ Node(Type type) : type(type) {}
+
+ // TODO: the others, if we need them
+ bool isVar() { return type == Var; }
+ bool isExpr() { return type == Expr; }
+ bool isPhi() { return type == Phi; }
+ bool isCond() { return type == Cond; }
+ bool isBlock() { return type == Block; }
+ bool isZext() { return type == Zext; }
+ bool isBad() { return type == Bad; }
+
+ bool isConst() { return type == Expr && expr->is<Const>(); }
+
+ union {
+ // For Var
+ wasm::Type wasmType;
+ // For Expr
+ Expression* expr;
+ // For Phi and Cond (the local index for phi, the block
+ // index for cond)
+ Index index;
+ };
+
+ // The wasm expression that we originate from (if such exists). A single
+ // wasm instruction may be turned into multiple dataflow IR nodes, and some
+ // nodes have no wasm origin (like phis).
+ Expression* origin = nullptr;
+
+ // Extra list of related nodes.
+ // For Expr, these are the Nodes for the inputs to the expression (e.g.
+ // a binary would have 2 in this vector here).
+ // For Phi, this is the block and then the list of values to pick from.
+ // For Cond, this is the block and node.
+ // For Block, this is the list of Conds. Note that that block does not
+ // depend on them - the Phis do, but we store them in the block so that
+ // we can avoid duplication.
+ // For Zext, this is the value we extend.
+ std::vector<Node*> values;
+
+ // Constructors
+ static Node* makeVar(wasm::Type wasmType) {
+ Node* ret = new Node(Var);
+ ret->wasmType = wasmType;
+ return ret;
+ }
+ static Node* makeExpr(Expression* expr, Expression* origin) {
+ Node* ret = new Node(Expr);
+ ret->expr = expr;
+ ret->origin = origin;
+ return ret;
+ }
+ static Node* makePhi(Node* block, Index index) {
+ Node* ret = new Node(Phi);
+ ret->addValue(block);
+ ret->index = index;
+ return ret;
+ }
+ static Node* makeCond(Node* block, Index index, Node* node) {
+ Node* ret = new Node(Cond);
+ ret->addValue(block);
+ ret->index = index;
+ ret->addValue(node);
+ return ret;
+ }
+ static Node* makeBlock() {
+ Node* ret = new Node(Block);
+ return ret;
+ }
+ static Node* makeZext(Node* child, Expression* origin) {
+ Node* ret = new Node(Zext);
+ ret->addValue(child);
+ ret->origin = origin;
+ return ret;
+ }
+ static Node* makeBad() {
+ Node* ret = new Node(Bad);
+ return ret;
+ }
+
+ // Helpers
+
+ void addValue(Node* value) {
+ values.push_back(value);
+ }
+ Node* getValue(Index i) {
+ return values.at(i);
+ }
+
+ // Gets the wasm type of the node. If there isn't a valid one,
+ // return unreachable.
+ wasm::Type getWasmType() {
+ switch (type) {
+ case Var: return wasmType;
+ case Expr: return expr->type;
+ case Phi: return getValue(1)->getWasmType();
+ case Zext: return getValue(0)->getWasmType();
+ case Bad: return unreachable;
+ default: WASM_UNREACHABLE();
+ }
+ }
+
+ bool operator==(const Node& other) {
+ if (type != other.type) return false;
+ switch (type) {
+ case Var:
+ case Block: return this == &other;
+ case Expr: {
+ if (!ExpressionAnalyzer::equal(expr, other.expr)) {
+ return false;
+ }
+ break;
+ }
+ case Cond: if (index != other.index) return false;
+ default: {}
+ }
+ if (values.size() != other.values.size()) return false;
+ for (Index i = 0; i < values.size(); i++) {
+ if (*(values[i]) != *(other.values[i])) return false;
+ }
+ return true;
+ }
+
+ bool operator!=(const Node& other) {
+ return !(*this == other);
+ }
+
+ // As mentioned above, comparisons return i1. This checks
+ // if an operation is of that sort.
+ bool returnsI1() {
+ if (isExpr()) {
+ if (auto* binary = expr->dynCast<Binary>()) {
+ return binary->isRelational();
+ } else if (auto* unary = expr->dynCast<Unary>()) {
+ return unary->isRelational();
+ }
+ }
+ return false;
+ }
+};
+
+} // namespace DataFlow
+
+} // namespace wasm
+
+#endif // wasm_dataflow_node