/* * 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_graph_h #define wasm_dataflow_graph_h #include "dataflow/node.h" #include "ir/abstract.h" #include "ir/iteration.h" #include "ir/literal-utils.h" #include "wasm.h" namespace wasm { namespace DataFlow { // Main logic to generate IR for a function. This is implemented as a // visitor on the wasm, where visitors return a Node* that either // contains the DataFlow IR for that expression, which can be a // Bad node if not supported, or nullptr if not relevant (we only // use the return value for internal expressions, that is, the // value of a local.set or the condition of an if etc). struct Graph : public UnifiedExpressionVisitor { // We only need one canonical bad node. It is never modified. Node bad = Node(Node::Type::Bad); // Connects a specific set to the data in its value. std::unordered_map setNodeMap; // Maps a control-flow expression to the conditions for it. Currently, // this maps an if to the conditions for its arms std::unordered_map> expressionConditionMap; // Maps each expression to its control-flow parent (or null if // there is none). We only map expressions we need to know about, // which are sets, set values, and control-flow constructs. std::unordered_map expressionParentMap; // The same, for nodes. Note that we currently don't know the parents // of nodes like phis that don't exist in wasm - we need to add more // stuff to handle that. But we will know the parent of anything using // that phi and storing to a local, which is probably enough anyhow // for pc generation. std::unordered_map nodeParentMap; // All the sets, in order of appearance. std::vector sets; // The function being processed. Function* func; // The module we are working in. Module* module; // All of our nodes std::vector> nodes; // Tracking state during building // We need to track the parents of control flow nodes. Expression* parent = nullptr; // Tracks the state of locals in a control flow path: // locals[i] = the node whose value it contains // When we are in unreachable code (i.e., a path that does not // need to be merged in anywhere), we set the length of this // vector to 0 to indicate that. typedef std::vector Locals; // The current local state in the control flow path being emitted. Locals locals; // The local states on branches to a specific target. std::unordered_map> breakStates; // The local state in a control flow path, including a possible // condition as well. struct FlowState { Locals locals; Node* condition; FlowState(Locals locals, Node* condition) : locals(locals), condition(condition) {} }; // API void build(Function* funcInit, Module* moduleInit) { func = funcInit; module = moduleInit; auto numLocals = func->getNumLocals(); if (numLocals == 0) { return; // nothing to do } // Set up initial local state IR. setInReachable(); for (Index i = 0; i < numLocals; i++) { if (!isRelevantType(func->getLocalType(i))) { continue; } Node* node; auto type = func->getLocalType(i); if (func->isParam(i)) { node = makeVar(type); } else { node = makeZero(type); } locals[i] = node; } // Process the function body, generating the rest of the IR. visit(func->body); } // Makes a Var node, representing a value that could be anything. Node* makeVar(wasm::Type type) { if (isRelevantType(type)) { return addNode(Node::makeVar(type)); } else { return &bad; } } // We create one node per constant value std::unordered_map constantNodes; Node* makeConst(Literal value) { auto iter = constantNodes.find(value); if (iter != constantNodes.end()) { return iter->second; } // Create one for this literal. Builder builder(*module); auto* c = builder.makeConst(value); auto* ret = addNode(Node::makeExpr(c, c)); constantNodes[value] = ret; return ret; } Node* makeZero(wasm::Type type) { return makeConst(Literal::makeZero(type)); } // Add a new node to our list of owned nodes. Node* addNode(Node* node) { nodes.push_back(std::unique_ptr(node)); return node; } Node* makeZeroComp(Node* node, bool equal, Expression* origin) { assert(!node->isBad()); Builder builder(*module); auto type = node->getWasmType(); if (!type.isConcrete()) { return &bad; } auto* zero = makeZero(type); auto* expr = builder.makeBinary( Abstract::getBinary(type, equal ? Abstract::Eq : Abstract::Ne), makeUse(node), makeUse(zero)); auto* check = addNode(Node::makeExpr(expr, origin)); check->addValue(expandFromI1(node, origin)); check->addValue(zero); return check; } void setInUnreachable() { locals.clear(); } void setInReachable() { locals.resize(func->getNumLocals()); } bool isInUnreachable() { return isInUnreachable(locals); } bool isInUnreachable(const Locals& state) { return state.empty(); } bool isInUnreachable(const FlowState& state) { return isInUnreachable(state.locals); } // Visiting. Node* visitExpression(Expression* curr) { // TODO Exception handling instruction support // Control flow and get/set etc. are special. Aside from them, we just need // to do something very generic. if (auto* block = curr->dynCast()) { return doVisitBlock(block); } else if (auto* iff = curr->dynCast()) { return doVisitIf(iff); } else if (auto* loop = curr->dynCast()) { return doVisitLoop(loop); } else if (auto* get = curr->dynCast()) { return doVisitLocalGet(get); } else if (auto* set = curr->dynCast()) { return doVisitLocalSet(set); } else if (auto* br = curr->dynCast()) { return doVisitBreak(br); } else if (auto* sw = curr->dynCast()) { return doVisitSwitch(sw); } else if (auto* c = curr->dynCast()) { return doVisitConst(c); } else if (auto* unary = curr->dynCast()) { return doVisitUnary(unary); } else if (auto* binary = curr->dynCast()) { return doVisitBinary(binary); } else if (auto* select = curr->dynCast