/* * 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 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(); WalkerPass>::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 Expression* optimize(Expression* curr, bool resultUsed) { // an unreachable node must not be changed if (curr->type == unreachable) return curr; while (1) { 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::CallImportId: case Expression::Id::CallIndirectId: case Expression::Id::SetLocalId: case Expression::Id::StoreId: case Expression::Id::ReturnId: case Expression::Id::SetGlobalId: 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) if (!resultUsed && !EffectAnalyzer(getPassOptions(), curr).hasSideEffects()) { return curr->cast()->ptr; } return curr; } case Expression::Id::ConstId: case Expression::Id::GetLocalId: case Expression::Id::GetGlobalId: { 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