/* * 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. */ // // Removes obviously unneeded code // #include #include #include #include #include #include #include #include namespace wasm { struct Vacuum : public WalkerPass> { bool isFunctionParallel() override { return true; } Pass* create() override { return new Vacuum; } TypeUpdater typeUpdater; Expression* replaceCurrent(Expression* expression) { auto* old = getCurrent(); super::replaceCurrent(expression); // also update the type updater typeUpdater.noteReplacement(old, expression); return expression; } void doWalkFunction(Function* func) { typeUpdater.walk(func->body); walk(func->body); } // Returns nullptr if curr is dead, curr if it must stay as is, or another // node if it can be replaced. Takes into account: // * The result may be used or unused. // * The type may or may not matter (a drop can drop anything, for example). Expression* optimize(Expression* curr, bool resultUsed, bool typeMatters) { auto type = curr->type; // An unreachable node must not be changed. if (type == Type::unreachable) { return curr; } // We iterate on possible replacements. If a replacement changes the type, // stop and go back. auto* prev = curr; while (1) { if (typeMatters && curr->type != type) { return prev; } prev = curr; switch (curr->_id) { case Expression::Id::NopId: return nullptr; // never needed case Expression::Id::BlockId: return curr; // not always needed, but handled in visitBlock() case Expression::Id::IfId: return curr; // not always needed, but handled in visitIf() case Expression::Id::LoopId: return curr; // not always needed, but handled in visitLoop() case Expression::Id::DropId: return curr; // not always needed, but handled in visitDrop() case Expression::Id::BreakId: case Expression::Id::SwitchId: case Expression::Id::CallId: case Expression::Id::CallIndirectId: case Expression::Id::LocalSetId: case Expression::Id::StoreId: case Expression::Id::ReturnId: case Expression::Id::GlobalSetId: case Expression::Id::HostId: case Expression::Id::UnreachableId: return curr; // always needed case Expression::Id::LoadId: { // it is ok to remove a load if the result is not used, and it has no // side effects (the load itself may trap, if we are not ignoring such // things) auto* load = curr->cast(); if (!resultUsed && !EffectAnalyzer(getPassOptions(), curr).hasSideEffects()) { if (!typeMatters || load->ptr->type == type) { return load->ptr; } } return curr; } case Expression::Id::ConstId: case Expression::Id::LocalGetId: case Expression::Id::GlobalGetId: { if (!resultUsed) { return nullptr; } return curr; } case Expression::Id::UnaryId: case Expression::Id::BinaryId: case Expression::Id::SelectId: { if (resultUsed) { return curr; // used, keep it } // for unary, binary, and select, we need to check their arguments for // side effects, as well as the node itself, as some unaries and // binaries have implicit traps if (auto* unary = curr->dynCast()) { EffectAnalyzer tester(getPassOptions()); tester.visitUnary(unary); if (tester.hasSideEffects()) { return curr; } if (EffectAnalyzer(getPassOptions(), unary->value) .hasSideEffects()) { curr = unary->value; continue; } else { return nullptr; } } else if (auto* binary = curr->dynCast()) { EffectAnalyzer tester(getPassOptions()); tester.visitBinary(binary); if (tester.hasSideEffects()) { return curr; } if (EffectAnalyzer(getPassOptions(), binary->left) .hasSideEffects()) { if (EffectAnalyzer(getPassOptions(), binary->right) .hasSideEffects()) { return curr; // leave them } else { curr = binary->left; continue; } } else { if (EffectAnalyzer(getPassOptions(), binary->right) .hasSideEffects()) { curr = binary->right; continue; } else { return nullptr; } } } else { // TODO: if two have side effects, we could replace the select with // say an add? auto* select = curr->cast