/* This file is part of solidity. solidity is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. solidity is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with solidity. If not, see . */ /** * @file PeepholeOptimiser.cpp * Performs local optimising code changes to assembly. */ #include "PeepholeOptimiser.h" #include #include using namespace std; using namespace dev::eth; using namespace dev; // TODO: Extend this to use the tools from ExpressionClasses.cpp struct Identity { static size_t windowSize() { return 1; } static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator _out) { *_out = *_in; return true; } }; struct PushPop { static size_t windowSize() { return 2; } static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator) { auto t = _in[0].type(); if (_in[1] == Instruction::POP && ( SemanticInformation::isDupInstruction(_in[0]) || t == Push || t == PushString || t == PushTag || t == PushSub || t == PushSubSize || t == PushProgramSize || t == PushData || t == PushLibraryAddress )) return true; else return false; } }; struct AddPop { static size_t windowSize() { return 2; } static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator _out) { if (_in[1] == Instruction::POP && _in[0].type() == Operation ) { Instruction i0 = _in[0].instruction(); if (instructionInfo(i0).ret == 1 && !SemanticInformation::invalidatesMemory(i0) && !SemanticInformation::invalidatesStorage(i0) && !SemanticInformation::altersControlFlow(i0) && !instructionInfo(i0).sideEffects ) { for (int j = 0; j < instructionInfo(i0).args; j++) *_out = Instruction::POP; return true; } } return false; } }; struct DoubleSwap { static size_t windowSize() { return 2; } static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator) { if (_in[0] == _in[1] && SemanticInformation::isSwapInstruction(_in[0])) return true; else return false; } }; struct JumpToNext { static size_t windowSize() { return 3; } static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator _out) { if ( _in[0].type() == PushTag && (_in[1] == Instruction::JUMP || _in[1] == Instruction::JUMPI) && _in[2].type() == Tag && _in[0].data() == _in[2].data() ) { if (_in[1] == Instruction::JUMPI) *_out = AssemblyItem(Instruction::POP, _in[1].location()); *_out = _in[2]; return true; } else return false; } }; struct TagConjunctions { static size_t windowSize() { return 3; } static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator _out) { if ( _in[0].type() == PushTag && _in[2] == Instruction::AND && _in[1].type() == Push && (_in[1].data() & u256(0xFFFFFFFF)) == u256(0xFFFFFFFF) ) { *_out = _in[0]; return true; } else return false; } }; struct OptimiserState { AssemblyItems const& items; size_t i; std::back_insert_iterator out; }; void applyMethods(OptimiserState&) { assertThrow(false, OptimizerException, "Peephole optimizer failed to apply identity."); } template void applyMethods(OptimiserState& _state, Method, OtherMethods... _other) { if (_state.i + Method::windowSize() <= _state.items.size() && Method::apply(_state.items.begin() + _state.i, _state.out)) _state.i += Method::windowSize(); else applyMethods(_state, _other...); } bool PeepholeOptimiser::optimise() { OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; while (state.i < m_items.size()) applyMethods(state, PushPop(), AddPop(), DoubleSwap(), JumpToNext(), TagConjunctions(), Identity()); if (m_optimisedItems.size() < m_items.size()) { m_items = std::move(m_optimisedItems); return true; } else return false; }