/* * Copyright 2017 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. */ #ifndef wasm_ir_module_h #define wasm_ir_module_h #include "ir/find_all.h" #include "ir/manipulation.h" #include "pass.h" #include "support/unique_deferring_queue.h" #include "wasm.h" namespace wasm { namespace ModuleUtils { // Computes the indexes in a wasm binary, i.e., with function imports // and function implementations sharing a single index space, etc., // and with the imports first (the Module's functions and globals // arrays are not assumed to be in a particular order, so we can't // just use them directly). struct BinaryIndexes { std::unordered_map functionIndexes; std::unordered_map globalIndexes; std::unordered_map eventIndexes; BinaryIndexes(Module& wasm) { auto addIndexes = [&](auto& source, auto& indexes) { auto addIndex = [&](auto* curr) { auto index = indexes.size(); indexes[curr->name] = index; }; for (auto& curr : source) { if (curr->imported()) { addIndex(curr.get()); } } for (auto& curr : source) { if (!curr->imported()) { addIndex(curr.get()); } } }; addIndexes(wasm.functions, functionIndexes); addIndexes(wasm.globals, globalIndexes); addIndexes(wasm.events, eventIndexes); } }; inline Function* copyFunction(Function* func, Module& out) { auto* ret = new Function(); ret->name = func->name; ret->result = func->result; ret->params = func->params; ret->vars = func->vars; // start with no named type; the names in the other module may differ ret->type = Name(); ret->localNames = func->localNames; ret->localIndices = func->localIndices; ret->debugLocations = func->debugLocations; ret->body = ExpressionManipulator::copy(func->body, out); ret->module = func->module; ret->base = func->base; // TODO: copy Stack IR assert(!func->stackIR); out.addFunction(ret); return ret; } inline Global* copyGlobal(Global* global, Module& out) { auto* ret = new Global(); ret->name = global->name; ret->type = global->type; ret->mutable_ = global->mutable_; ret->module = global->module; ret->base = global->base; if (global->imported()) { ret->init = nullptr; } else { ret->init = ExpressionManipulator::copy(global->init, out); } out.addGlobal(ret); return ret; } inline Event* copyEvent(Event* event, Module& out) { auto* ret = new Event(); ret->name = event->name; ret->attribute = event->attribute; ret->sig = event->sig; out.addEvent(ret); return ret; } inline void copyModule(Module& in, Module& out) { // we use names throughout, not raw points, so simple copying is fine // for everything *but* expressions for (auto& curr : in.functionTypes) { out.addFunctionType(make_unique(*curr)); } for (auto& curr : in.exports) { out.addExport(new Export(*curr)); } for (auto& curr : in.functions) { copyFunction(curr.get(), out); } for (auto& curr : in.globals) { copyGlobal(curr.get(), out); } for (auto& curr : in.events) { copyEvent(curr.get(), out); } out.table = in.table; for (auto& segment : out.table.segments) { segment.offset = ExpressionManipulator::copy(segment.offset, out); } out.memory = in.memory; for (auto& segment : out.memory.segments) { segment.offset = ExpressionManipulator::copy(segment.offset, out); } out.start = in.start; out.userSections = in.userSections; out.debugInfoFileNames = in.debugInfoFileNames; } // Renaming // Rename functions along with all their uses. // Note that for this to work the functions themselves don't necessarily need // to exist. For example, it is possible to remove a given function and then // call this redirect all of its uses. template inline void renameFunctions(Module& wasm, T& map) { // Update the function itself. for (auto& pair : map) { if (Function* F = wasm.getFunctionOrNull(pair.first)) { assert(!wasm.getFunctionOrNull(pair.second)); F->name = pair.second; } } wasm.updateMaps(); // Update other global things. auto maybeUpdate = [&](Name& name) { auto iter = map.find(name); if (iter != map.end()) { name = iter->second; } }; maybeUpdate(wasm.start); for (auto& segment : wasm.table.segments) { for (auto& name : segment.data) { maybeUpdate(name); } } for (auto& exp : wasm.exports) { if (exp->kind == ExternalKind::Function) { maybeUpdate(exp->value); } } // Update call instructions. for (auto& func : wasm.functions) { // TODO: parallelize if (!func->imported()) { FindAll calls(func->body); for (auto* call : calls.list) { maybeUpdate(call->target); } } } } inline void renameFunction(Module& wasm, Name oldName, Name newName) { std::map map; map[oldName] = newName; renameFunctions(wasm, map); } // Convenient iteration over imported/non-imported module elements template inline void iterImportedMemories(Module& wasm, T visitor) { if (wasm.memory.exists && wasm.memory.imported()) { visitor(&wasm.memory); } } template inline void iterDefinedMemories(Module& wasm, T visitor) { if (wasm.memory.exists && !wasm.memory.imported()) { visitor(&wasm.memory); } } template inline void iterImportedTables(Module& wasm, T visitor) { if (wasm.table.exists && wasm.table.imported()) { visitor(&wasm.table); } } template inline void iterDefinedTables(Module& wasm, T visitor) { if (wasm.table.exists && !wasm.table.imported()) { visitor(&wasm.table); } } template inline void iterImportedGlobals(Module& wasm, T visitor) { for (auto& import : wasm.globals) { if (import->imported()) { visitor(import.get()); } } } template inline void iterDefinedGlobals(Module& wasm, T visitor) { for (auto& import : wasm.globals) { if (!import->imported()) { visitor(import.get()); } } } template inline void iterImportedFunctions(Module& wasm, T visitor) { for (auto& import : wasm.functions) { if (import->imported()) { visitor(import.get()); } } } template inline void iterDefinedFunctions(Module& wasm, T visitor) { for (auto& import : wasm.functions) { if (!import->imported()) { visitor(import.get()); } } } template inline void iterImportedEvents(Module& wasm, T visitor) { for (auto& import : wasm.events) { if (import->imported()) { visitor(import.get()); } } } template inline void iterDefinedEvents(Module& wasm, T visitor) { for (auto& import : wasm.events) { if (!import->imported()) { visitor(import.get()); } } } // Helper class for performing an operation on all the functions in the module, // in parallel, with an Info object for each one that can contain results of // some computation that the operation performs. // The operation performend should not modify the wasm module in any way. // TODO: enforce this template struct ParallelFunctionAnalysis { Module& wasm; typedef std::map Map; Map map; typedef std::function Func; ParallelFunctionAnalysis(Module& wasm, Func work) : wasm(wasm) { // Fill in map, as we operate on it in parallel (each function to its own // entry). for (auto& func : wasm.functions) { map[func.get()]; } // Run on the imports first. TODO: parallelize this too for (auto& func : wasm.functions) { if (func->imported()) { work(func.get(), map[func.get()]); } } struct Mapper : public WalkerPass> { bool isFunctionParallel() override { return true; } bool modifiesBinaryenIR() override { return false; } Mapper(Module& module, Map& map, Func work) : module(module), map(map), work(work) {} Mapper* create() override { return new Mapper(module, map, work); } void doWalkFunction(Function* curr) { assert(map.count(curr)); work(curr, map[curr]); } private: Module& module; Map& map; Func work; }; PassRunner runner(&wasm); Mapper(wasm, map, work).run(&runner, &wasm); } }; // Helper class for analyzing the call graph. // // Provides hooks for running some initial calculation on each function (which // is done in parallel), writing to a FunctionInfo structure for each function. // Then you can call propagateBack() to propagate a property of interest to the // calling functions, transitively. // // For example, if some functions are known to call an import "foo", then you // can use this to find which functions call something that might eventually // reach foo, by initially marking the direct callers as "calling foo" and // propagating that backwards. template struct CallGraphPropertyAnalysis { Module& wasm; // The basic information for each function about whom it calls and who is // called by it. struct FunctionInfo { std::set callsTo; std::set calledBy; }; typedef std::map Map; Map map; typedef std::function Func; CallGraphPropertyAnalysis(Module& wasm, Func work) : wasm(wasm) { ParallelFunctionAnalysis analysis(wasm, [&](Function* func, T& info) { work(func, info); if (func->imported()) { return; } struct Mapper : public PostWalker { Mapper(Module* module, T& info, Func work) : module(module), info(info), work(work) {} void visitCall(Call* curr) { info.callsTo.insert(module->getFunction(curr->target)); } private: Module* module; T& info; Func work; } mapper(&wasm, info, work); mapper.walk(func->body); }); map.swap(analysis.map); // Find what is called by what. for (auto& pair : map) { auto* func = pair.first; auto& info = pair.second; for (auto* target : info.callsTo) { map[target].calledBy.insert(func); } } } // Propagate a property from a function to those that call it. void propagateBack(std::function hasProperty, std::function canHaveProperty, std::function addProperty) { // The work queue contains items we just learned can change the state. UniqueDeferredQueue work; for (auto& func : wasm.functions) { if (hasProperty(map[func.get()])) { work.push(func.get()); } } while (!work.empty()) { auto* func = work.pop(); for (auto* caller : map[func].calledBy) { // If we don't already have the property, and we are not forbidden // from getting it, then it propagates back to us now. if (!hasProperty(map[caller]) && canHaveProperty(map[caller])) { addProperty(map[caller]); work.push(caller); } } } } }; } // namespace ModuleUtils } // namespace wasm #endif // wasm_ir_module_h