/* * Copyright 2016 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. */ // // WebAssembly AST visitor. Useful for anything that wants to do something // different for each AST node type, like printing, interpreting, etc. // // This class is specifically designed as a template to avoid virtual function // call overhead. To write a visitor, derive from this class as follows: // // struct MyVisitor : public WasmVisitor { .. } // #ifndef wasm_wasm_traversal_h #define wasm_wasm_traversal_h #include "support/small_vector.h" #include "support/threads.h" #include "wasm.h" namespace wasm { // A generic visitor, defaulting to doing nothing on each visit template struct Visitor { // Expression visitors #define DELEGATE(CLASS_TO_VISIT) \ ReturnType visit##CLASS_TO_VISIT(CLASS_TO_VISIT* curr) { \ return ReturnType(); \ } #include "wasm-delegations.h" #undef DELEGATE // Module-level visitors ReturnType visitExport(Export* curr) { return ReturnType(); } ReturnType visitGlobal(Global* curr) { return ReturnType(); } ReturnType visitFunction(Function* curr) { return ReturnType(); } ReturnType visitTable(Table* curr) { return ReturnType(); } ReturnType visitMemory(Memory* curr) { return ReturnType(); } ReturnType visitEvent(Event* curr) { return ReturnType(); } ReturnType visitModule(Module* curr) { return ReturnType(); } ReturnType visit(Expression* curr) { assert(curr); switch (curr->_id) { #define DELEGATE(CLASS_TO_VISIT) \ case Expression::Id::CLASS_TO_VISIT##Id: \ return static_cast(this)->visit##CLASS_TO_VISIT( \ static_cast(curr)) #include "wasm-delegations.h" #undef DELEGATE default: WASM_UNREACHABLE("unexpected expression type"); } } }; // A visitor which must be overridden for each visitor that is reached. template struct OverriddenVisitor { // Expression visitors, which must be overridden #define DELEGATE(CLASS_TO_VISIT) \ ReturnType visit##CLASS_TO_VISIT(CLASS_TO_VISIT* curr) { \ static_assert( \ &SubType::visit##CLASS_TO_VISIT != \ &OverriddenVisitor::visit##CLASS_TO_VISIT, \ "Derived class must implement visit" #CLASS_TO_VISIT); \ WASM_UNREACHABLE("Derived class must implement visit" #CLASS_TO_VISIT); \ } #include "wasm-delegations.h" #undef DELEGATE ReturnType visit(Expression* curr) { assert(curr); switch (curr->_id) { #define DELEGATE(CLASS_TO_VISIT) \ case Expression::Id::CLASS_TO_VISIT##Id: \ return static_cast(this)->visit##CLASS_TO_VISIT( \ static_cast(curr)) #include "wasm-delegations.h" #undef DELEGATE default: WASM_UNREACHABLE("unexpected expression type"); } } }; // Visit with a single unified visitor, called on every node, instead of // separate visit* per node template struct UnifiedExpressionVisitor : public Visitor { // called on each node ReturnType visitExpression(Expression* curr) { return ReturnType(); } // redirects #define DELEGATE(CLASS_TO_VISIT) \ ReturnType visit##CLASS_TO_VISIT(CLASS_TO_VISIT* curr) { \ return static_cast(this)->visitExpression(curr); \ } #include "wasm-delegations.h" #undef DELEGATE }; // // Base class for all WasmWalkers, which can traverse an AST // and provide the option to replace nodes while doing so. // // Subclass and implement the visit*() // calls to run code on different node types. // template struct Walker : public VisitorType { // Useful methods for visitor implementions // Replace the current node. You can call this in your visit*() methods. // Note that the visit*() for the result node is not called for you (i.e., // just one visit*() method is called by the traversal; if you replace a node, // and you want to process the output, you must do that explicitly). Expression* replaceCurrent(Expression* expression) { // Copy debug info, if present. if (currFunction) { auto& debugLocations = currFunction->debugLocations; if (!debugLocations.empty()) { auto* curr = getCurrent(); auto iter = debugLocations.find(curr); if (iter != debugLocations.end()) { auto location = iter->second; debugLocations.erase(iter); debugLocations[expression] = location; } } } return *replacep = expression; } Expression* getCurrent() { return *replacep; } Expression** getCurrentPointer() { return replacep; } // Get the current module Module* getModule() { return currModule; } // Get the current function Function* getFunction() { return currFunction; } // Walk starting void walkGlobal(Global* global) { walk(global->init); static_cast(this)->visitGlobal(global); } void walkFunction(Function* func) { setFunction(func); static_cast(this)->doWalkFunction(func); static_cast(this)->visitFunction(func); setFunction(nullptr); } void walkEvent(Event* event) { static_cast(this)->visitEvent(event); } void walkFunctionInModule(Function* func, Module* module) { setModule(module); setFunction(func); static_cast(this)->doWalkFunction(func); static_cast(this)->visitFunction(func); setFunction(nullptr); setModule(nullptr); } // override this to provide custom functionality void doWalkFunction(Function* func) { walk(func->body); } void walkTable(Table* table) { for (auto& segment : table->segments) { walk(segment.offset); } static_cast(this)->visitTable(table); } void walkMemory(Memory* memory) { for (auto& segment : memory->segments) { if (!segment.isPassive) { walk(segment.offset); } } static_cast(this)->visitMemory(memory); } void walkModule(Module* module) { setModule(module); static_cast(this)->doWalkModule(module); static_cast(this)->visitModule(module); setModule(nullptr); } // override this to provide custom functionality void doWalkModule(Module* module) { // Dispatch statically through the SubType. SubType* self = static_cast(this); for (auto& curr : module->exports) { self->visitExport(curr.get()); } for (auto& curr : module->globals) { if (curr->imported()) { self->visitGlobal(curr.get()); } else { self->walkGlobal(curr.get()); } } for (auto& curr : module->functions) { if (curr->imported()) { self->visitFunction(curr.get()); } else { self->walkFunction(curr.get()); } } for (auto& curr : module->events) { if (curr->imported()) { self->visitEvent(curr.get()); } else { self->walkEvent(curr.get()); } } self->walkTable(&module->table); self->walkMemory(&module->memory); } // Walk implementation. We don't use recursion as ASTs may be highly // nested. // Tasks receive the this pointer and a pointer to the pointer to operate on typedef void (*TaskFunc)(SubType*, Expression**); struct Task { TaskFunc func; Expression** currp; Task() {} Task(TaskFunc func, Expression** currp) : func(func), currp(currp) {} }; void pushTask(TaskFunc func, Expression** currp) { assert(*currp); stack.emplace_back(func, currp); } void maybePushTask(TaskFunc func, Expression** currp) { if (*currp) { stack.emplace_back(func, currp); } } Task popTask() { auto ret = stack.back(); stack.pop_back(); return ret; } void walk(Expression*& root) { assert(stack.size() == 0); pushTask(SubType::scan, &root); while (stack.size() > 0) { auto task = popTask(); replacep = task.currp; assert(*task.currp); task.func(static_cast(this), task.currp); } } // subclasses implement this to define the proper order of execution static void scan(SubType* self, Expression** currp) { abort(); } // task hooks to call visitors #define DELEGATE(CLASS_TO_VISIT) \ static void doVisit##CLASS_TO_VISIT(SubType* self, Expression** currp) { \ self->visit##CLASS_TO_VISIT((*currp)->cast()); \ } #include "wasm-delegations.h" #undef DELEGATE void setModule(Module* module) { currModule = module; } void setFunction(Function* func) { currFunction = func; } private: // the address of the current node, used to replace it Expression** replacep = nullptr; SmallVector stack; // stack of tasks Function* currFunction = nullptr; // current function being processed Module* currModule = nullptr; // current module being processed }; // Walks in post-order, i.e., children first. When there isn't an obvious // order to operands, we follow them in order of execution. template> struct PostWalker : public Walker { static void scan(SubType* self, Expression** currp) { Expression* curr = *currp; switch (curr->_id) { case Expression::Id::InvalidId: abort(); case Expression::Id::BlockId: { self->pushTask(SubType::doVisitBlock, currp); auto& list = curr->cast()->list; for (int i = int(list.size()) - 1; i >= 0; i--) { self->pushTask(SubType::scan, &list[i]); } break; } case Expression::Id::IfId: { self->pushTask(SubType::doVisitIf, currp); self->maybePushTask(SubType::scan, &curr->cast()->ifFalse); self->pushTask(SubType::scan, &curr->cast()->ifTrue); self->pushTask(SubType::scan, &curr->cast()->condition); break; } case Expression::Id::LoopId: { self->pushTask(SubType::doVisitLoop, currp); self->pushTask(SubType::scan, &curr->cast()->body); break; } case Expression::Id::BreakId: { self->pushTask(SubType::doVisitBreak, currp); self->maybePushTask(SubType::scan, &curr->cast()->condition); self->maybePushTask(SubType::scan, &curr->cast()->value); break; } case Expression::Id::SwitchId: { self->pushTask(SubType::doVisitSwitch, currp); self->pushTask(SubType::scan, &curr->cast()->condition); self->maybePushTask(SubType::scan, &curr->cast()->value); break; } case Expression::Id::CallId: { self->pushTask(SubType::doVisitCall, currp); auto& list = curr->cast()->operands; for (int i = int(list.size()) - 1; i >= 0; i--) { self->pushTask(SubType::scan, &list[i]); } break; } case Expression::Id::CallIndirectId: { self->pushTask(SubType::doVisitCallIndirect, currp); auto& list = curr->cast()->operands; self->pushTask(SubType::scan, &curr->cast()->target); for (int i = int(list.size()) - 1; i >= 0; i--) { self->pushTask(SubType::scan, &list[i]); } break; } case Expression::Id::LocalGetId: { // TODO: optimize leaves with a direct call? self->pushTask(SubType::doVisitLocalGet, currp); break; } case Expression::Id::LocalSetId: { self->pushTask(SubType::doVisitLocalSet, currp); self->pushTask(SubType::scan, &curr->cast()->value); break; } case Expression::Id::GlobalGetId: { self->pushTask(SubType::doVisitGlobalGet, currp); break; } case Expression::Id::GlobalSetId: { self->pushTask(SubType::doVisitGlobalSet, currp); self->pushTask(SubType::scan, &curr->cast()->value); break; } case Expression::Id::LoadId: { self->pushTask(SubType::doVisitLoad, currp); self->pushTask(SubType::scan, &curr->cast()->ptr); break; } case Expression::Id::StoreId: { self->pushTask(SubType::doVisitStore, currp); self->pushTask(SubType::scan, &curr->cast()->value); self->pushTask(SubType::scan, &curr->cast()->ptr); break; } case Expression::Id::AtomicRMWId: { self->pushTask(SubType::doVisitAtomicRMW, currp); self->pushTask(SubType::scan, &curr->cast()->value); self->pushTask(SubType::scan, &curr->cast()->ptr); break; } case Expression::Id::AtomicCmpxchgId: { self->pushTask(SubType::doVisitAtomicCmpxchg, currp); self->pushTask(SubType::scan, &curr->cast()->replacement); self->pushTask(SubType::scan, &curr->cast()->expected); self->pushTask(SubType::scan, &curr->cast()->ptr); break; } case Expression::Id::AtomicWaitId: { self->pushTask(SubType::doVisitAtomicWait, currp); self->pushTask(SubType::scan, &curr->cast()->timeout); self->pushTask(SubType::scan, &curr->cast()->expected); self->pushTask(SubType::scan, &curr->cast()->ptr); break; } case Expression::Id::AtomicNotifyId: { self->pushTask(SubType::doVisitAtomicNotify, currp); self->pushTask(SubType::scan, &curr->cast()->notifyCount); self->pushTask(SubType::scan, &curr->cast()->ptr); break; } case Expression::Id::AtomicFenceId: { self->pushTask(SubType::doVisitAtomicFence, currp); break; } case Expression::Id::SIMDExtractId: { self->pushTask(SubType::doVisitSIMDExtract, currp); self->pushTask(SubType::scan, &curr->cast()->vec); break; } case Expression::Id::SIMDReplaceId: { self->pushTask(SubType::doVisitSIMDReplace, currp); self->pushTask(SubType::scan, &curr->cast()->value); self->pushTask(SubType::scan, &curr->cast()->vec); break; } case Expression::Id::SIMDShuffleId: { self->pushTask(SubType::doVisitSIMDShuffle, currp); self->pushTask(SubType::scan, &curr->cast()->right); self->pushTask(SubType::scan, &curr->cast()->left); break; } case Expression::Id::SIMDTernaryId: { self->pushTask(SubType::doVisitSIMDTernary, currp); self->pushTask(SubType::scan, &curr->cast()->c); self->pushTask(SubType::scan, &curr->cast()->b); self->pushTask(SubType::scan, &curr->cast()->a); break; } case Expression::Id::SIMDShiftId: { self->pushTask(SubType::doVisitSIMDShift, currp); self->pushTask(SubType::scan, &curr->cast()->shift); self->pushTask(SubType::scan, &curr->cast()->vec); break; } case Expression::Id::SIMDLoadId: { self->pushTask(SubType::doVisitSIMDLoad, currp); self->pushTask(SubType::scan, &curr->cast()->ptr); break; } case Expression::Id::SIMDLoadStoreLaneId: { self->pushTask(SubType::doVisitSIMDLoadStoreLane, currp); self->pushTask(SubType::scan, &curr->cast()->vec); self->pushTask(SubType::scan, &curr->cast()->ptr); break; } case Expression::Id::MemoryInitId: { self->pushTask(SubType::doVisitMemoryInit, currp); self->pushTask(SubType::scan, &curr->cast()->size); self->pushTask(SubType::scan, &curr->cast()->offset); self->pushTask(SubType::scan, &curr->cast()->dest); break; } case Expression::Id::DataDropId: { self->pushTask(SubType::doVisitDataDrop, currp); break; } case Expression::Id::MemoryCopyId: { self->pushTask(SubType::doVisitMemoryCopy, currp); self->pushTask(SubType::scan, &curr->cast()->size); self->pushTask(SubType::scan, &curr->cast()->source); self->pushTask(SubType::scan, &curr->cast()->dest); break; } case Expression::Id::MemoryFillId: { self->pushTask(SubType::doVisitMemoryFill, currp); self->pushTask(SubType::scan, &curr->cast()->size); self->pushTask(SubType::scan, &curr->cast()->value); self->pushTask(SubType::scan, &curr->cast()->dest); break; } case Expression::Id::ConstId: { self->pushTask(SubType::doVisitConst, currp); break; } case Expression::Id::UnaryId: { self->pushTask(SubType::doVisitUnary, currp); self->pushTask(SubType::scan, &curr->cast()->value); break; } case Expression::Id::BinaryId: { self->pushTask(SubType::doVisitBinary, currp); self->pushTask(SubType::scan, &curr->cast()->right); self->pushTask(SubType::scan, &curr->cast()->left); break; } case Expression::Id::SelectId: { self->pushTask(SubType::doVisitSelect, currp); self->pushTask(SubType::scan, &curr->cast()->ifFalse); self->pushTask(SubType::scan, &curr->cast