mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Simple peephole optimizer that is activated even if not requested.
This commit is contained in:
parent
22b4d1b29a
commit
0335ed4cb4
@ -20,13 +20,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Assembly.h"
|
#include "Assembly.h"
|
||||||
#include <fstream>
|
|
||||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||||
#include <libevmasm/ControlFlowGraph.h>
|
#include <libevmasm/ControlFlowGraph.h>
|
||||||
|
#include <libevmasm/PeepholeOptimiser.h>
|
||||||
#include <libevmasm/BlockDeduplicator.h>
|
#include <libevmasm/BlockDeduplicator.h>
|
||||||
#include <libevmasm/ConstantOptimiser.h>
|
#include <libevmasm/ConstantOptimiser.h>
|
||||||
#include <libevmasm/GasMeter.h>
|
#include <libevmasm/GasMeter.h>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::eth;
|
using namespace dev::eth;
|
||||||
@ -314,16 +318,15 @@ void Assembly::injectStart(AssemblyItem const& _i)
|
|||||||
|
|
||||||
Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
|
Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
|
||||||
{
|
{
|
||||||
if (_enable)
|
optimiseInternal(_enable, _isCreation, _runs);
|
||||||
optimiseInternal(_isCreation, _runs);
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
map<u256, u256> Assembly::optimiseInternal(bool _isCreation, size_t _runs)
|
map<u256, u256> Assembly::optimiseInternal(bool _enable, bool _isCreation, size_t _runs)
|
||||||
{
|
{
|
||||||
for (size_t subId = 0; subId < m_subs.size(); ++subId)
|
for (size_t subId = 0; subId < m_subs.size(); ++subId)
|
||||||
{
|
{
|
||||||
map<u256, u256> subTagReplacements = m_subs[subId]->optimiseInternal(false, _runs);
|
map<u256, u256> subTagReplacements = m_subs[subId]->optimiseInternal(_enable, false, _runs);
|
||||||
BlockDeduplicator::applyTagReplacement(m_items, subTagReplacements, subId);
|
BlockDeduplicator::applyTagReplacement(m_items, subTagReplacements, subId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,6 +336,13 @@ map<u256, u256> Assembly::optimiseInternal(bool _isCreation, size_t _runs)
|
|||||||
{
|
{
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
|
PeepholeOptimiser peepOpt(m_items);
|
||||||
|
if (peepOpt.optimise())
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (!_enable)
|
||||||
|
continue;
|
||||||
|
|
||||||
// This only modifies PushTags, we have to run again to actually remove code.
|
// This only modifies PushTags, we have to run again to actually remove code.
|
||||||
BlockDeduplicator dedup(m_items);
|
BlockDeduplicator dedup(m_items);
|
||||||
if (dedup.deduplicate())
|
if (dedup.deduplicate())
|
||||||
@ -399,6 +409,7 @@ map<u256, u256> Assembly::optimiseInternal(bool _isCreation, size_t _runs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_enable)
|
||||||
total += ConstantOptimisationMethod::optimiseConstants(
|
total += ConstantOptimisationMethod::optimiseConstants(
|
||||||
_isCreation,
|
_isCreation,
|
||||||
_isCreation ? 1 : _runs,
|
_isCreation ? 1 : _runs,
|
||||||
|
@ -101,6 +101,7 @@ public:
|
|||||||
/// execution gas usage is optimised. @a _isCreation should be true for the top-level assembly.
|
/// execution gas usage is optimised. @a _isCreation should be true for the top-level assembly.
|
||||||
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
|
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
|
||||||
/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
|
/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
|
||||||
|
/// If @a _enable is not set, will perform some simple peephole optimizations.
|
||||||
Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200);
|
Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200);
|
||||||
Json::Value stream(
|
Json::Value stream(
|
||||||
std::ostream& _out,
|
std::ostream& _out,
|
||||||
@ -112,7 +113,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
/// Does the same operations as @a optimise, but should only be applied to a sub and
|
/// Does the same operations as @a optimise, but should only be applied to a sub and
|
||||||
/// returns the replaced tags.
|
/// returns the replaced tags.
|
||||||
std::map<u256, u256> optimiseInternal(bool _isCreation, size_t _runs);
|
std::map<u256, u256> optimiseInternal(bool _enable, bool _isCreation, size_t _runs);
|
||||||
|
|
||||||
std::string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const;
|
std::string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const;
|
||||||
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
|
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
|
||||||
|
146
libevmasm/PeepholeOptimiser.cpp
Normal file
146
libevmasm/PeepholeOptimiser.cpp
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
This file is part of cpp-ethereum.
|
||||||
|
|
||||||
|
cpp-ethereum 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.
|
||||||
|
|
||||||
|
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file PeepholeOptimiser.h
|
||||||
|
* Performs local optimising code changes to assembly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PeepholeOptimiser.h"
|
||||||
|
|
||||||
|
#include <libevmasm/AssemblyItem.h>
|
||||||
|
#include <libevmasm/SemanticInformation.h>
|
||||||
|
|
||||||
|
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<AssemblyItems> _out)
|
||||||
|
{
|
||||||
|
*_out = *_in;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PushPop
|
||||||
|
{
|
||||||
|
static size_t windowSize() { return 2; }
|
||||||
|
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems>)
|
||||||
|
{
|
||||||
|
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 DoubleSwap
|
||||||
|
{
|
||||||
|
static size_t windowSize() { return 2; }
|
||||||
|
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems>)
|
||||||
|
{
|
||||||
|
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<AssemblyItems> _out)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
_in[0].type() == PushTag &&
|
||||||
|
(_in[1] == Instruction::JUMP || _in[1] == Instruction::JUMPI) &&
|
||||||
|
_in[2].type() == Tag &&
|
||||||
|
_in[0].data() == _in[2].data()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
*_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<AssemblyItems> _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<AssemblyItems> out;
|
||||||
|
};
|
||||||
|
|
||||||
|
void applyMethods(OptimiserState&)
|
||||||
|
{
|
||||||
|
assertThrow(false, OptimizerException, "Peephole optimizer failed to apply identity.");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Method, typename... OtherMethods>
|
||||||
|
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(), DoubleSwap(), JumpToNext(), TagConjunctions(), Identity());
|
||||||
|
if (m_optimisedItems.size() < m_items.size())
|
||||||
|
{
|
||||||
|
m_items = std::move(m_optimisedItems);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
53
libevmasm/PeepholeOptimiser.h
Normal file
53
libevmasm/PeepholeOptimiser.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
This file is part of cpp-ethereum.
|
||||||
|
|
||||||
|
cpp-ethereum 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.
|
||||||
|
|
||||||
|
cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file PeepholeOptimiser.h
|
||||||
|
* Performs local optimising code changes to assembly.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace eth
|
||||||
|
{
|
||||||
|
class AssemblyItem;
|
||||||
|
using AssemblyItems = std::vector<AssemblyItem>;
|
||||||
|
|
||||||
|
class PeepholeOptimisationMethod
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual size_t windowSize() const;
|
||||||
|
virtual bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PeepholeOptimiser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PeepholeOptimiser(AssemblyItems& _items): m_items(_items) {}
|
||||||
|
|
||||||
|
bool optimise();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AssemblyItems& m_items;
|
||||||
|
AssemblyItems m_optimisedItems;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -41,8 +41,7 @@ void Compiler::compileContract(
|
|||||||
ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize);
|
ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize);
|
||||||
m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
|
m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
|
||||||
|
|
||||||
if (m_optimize)
|
m_context.optimise(m_optimize, m_optimizeRuns);
|
||||||
m_context.optimise(m_optimizeRuns);
|
|
||||||
|
|
||||||
if (_contract.isLibrary())
|
if (_contract.isLibrary())
|
||||||
{
|
{
|
||||||
@ -60,8 +59,7 @@ void Compiler::compileClone(
|
|||||||
ContractCompiler cloneCompiler(&runtimeCompiler, m_context, m_optimize);
|
ContractCompiler cloneCompiler(&runtimeCompiler, m_context, m_optimize);
|
||||||
m_runtimeSub = cloneCompiler.compileClone(_contract, _contracts);
|
m_runtimeSub = cloneCompiler.compileClone(_contract, _contracts);
|
||||||
|
|
||||||
if (m_optimize)
|
m_context.optimise(m_optimize, m_optimizeRuns);
|
||||||
m_context.optimise(m_optimizeRuns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const
|
eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const
|
||||||
|
@ -155,7 +155,7 @@ public:
|
|||||||
/// Prepends "PUSH <compiler version number> POP"
|
/// Prepends "PUSH <compiler version number> POP"
|
||||||
void injectVersionStampIntoSub(size_t _subIndex);
|
void injectVersionStampIntoSub(size_t _subIndex);
|
||||||
|
|
||||||
void optimise(unsigned _runs = 200) { m_asm->optimise(true, true, _runs); }
|
void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); }
|
||||||
|
|
||||||
/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
|
/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
|
||||||
CompilerContext* runtimeContext() { return m_runtimeContext; }
|
CompilerContext* runtimeContext() { return m_runtimeContext; }
|
||||||
|
Loading…
Reference in New Issue
Block a user