Merge pull request #1429 from ethereum/unreachablepeephole

Some dead code elimination
This commit is contained in:
chriseth 2016-11-25 16:57:59 +01:00 committed by GitHub
commit 0933a4ff1a
4 changed files with 179 additions and 67 deletions

View File

@ -1,5 +1,8 @@
### 0.4.7 (unreleased) ### 0.4.7 (unreleased)
Features:
* Optimizer: Some dead code elimination.
Bugfixes: Bugfixes:
* Type checker: string literals that are not valid UTF-8 cannot be converted to string type * Type checker: string literals that are not valid UTF-8 cannot be converted to string type

View File

@ -337,7 +337,7 @@ map<u256, u256> Assembly::optimiseInternal(bool _enable, bool _isCreation, size_
count = 0; count = 0;
PeepholeOptimiser peepOpt(m_items); PeepholeOptimiser peepOpt(m_items);
if (peepOpt.optimise()) while (peepOpt.optimise())
count++; count++;
if (!_enable) if (!_enable)

View File

@ -30,51 +30,96 @@ using namespace dev;
// TODO: Extend this to use the tools from ExpressionClasses.cpp // TODO: Extend this to use the tools from ExpressionClasses.cpp
struct Identity struct OptimiserState
{ {
static size_t windowSize() { return 1; } AssemblyItems const& items;
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out) size_t i;
std::back_insert_iterator<AssemblyItems> out;
};
template <class Method, size_t Arguments>
struct ApplyRule
{
};
template <class Method>
struct ApplyRule<Method, 3>
{
static bool applyRule(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out)
{ {
*_out = *_in; return Method::applySimple(_in[0], _in[1], _in[2], _out);
return true; }
};
template <class Method>
struct ApplyRule<Method, 2>
{
static bool applyRule(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out)
{
return Method::applySimple(_in[0], _in[1], _out);
}
};
template <class Method>
struct ApplyRule<Method, 1>
{
static bool applyRule(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out)
{
return Method::applySimple(_in[0], _out);
} }
}; };
struct PushPop template <class Method, size_t WindowSize>
struct SimplePeepholeOptimizerMethod
{ {
static size_t windowSize() { return 2; } static bool apply(OptimiserState& _state)
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems>)
{ {
auto t = _in[0].type(); if (
if (_in[1] == Instruction::POP && ( _state.i + WindowSize <= _state.items.size() &&
SemanticInformation::isDupInstruction(_in[0]) || ApplyRule<Method, WindowSize>::applyRule(_state.items.begin() + _state.i, _state.out)
t == Push || t == PushString || t == PushTag || t == PushSub || )
t == PushSubSize || t == PushProgramSize || t == PushData || t == PushLibraryAddress {
)) _state.i += WindowSize;
return true; return true;
}
else else
return false; return false;
} }
}; };
struct AddPop struct Identity: SimplePeepholeOptimizerMethod<Identity, 1>
{ {
static size_t windowSize() { return 2; } static bool applySimple(AssemblyItem const& _item, std::back_insert_iterator<AssemblyItems> _out)
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out)
{ {
if (_in[1] == Instruction::POP && *_out = _item;
_in[0].type() == Operation return true;
) }
};
struct PushPop: SimplePeepholeOptimizerMethod<PushPop, 2>
{
static bool applySimple(AssemblyItem const& _push, AssemblyItem const& _pop, std::back_insert_iterator<AssemblyItems>)
{
auto t = _push.type();
return _pop == Instruction::POP && (
SemanticInformation::isDupInstruction(_push) ||
t == Push || t == PushString || t == PushTag || t == PushSub ||
t == PushSubSize || t == PushProgramSize || t == PushData || t == PushLibraryAddress
);
}
};
struct OpPop: SimplePeepholeOptimizerMethod<OpPop, 2>
{
static bool applySimple(
AssemblyItem const& _op,
AssemblyItem const& _pop,
std::back_insert_iterator<AssemblyItems> _out
)
{
if (_pop == Instruction::POP && _op.type() == Operation)
{ {
Instruction i0 = _in[0].instruction(); Instruction instr = _op.instruction();
if (instructionInfo(i0).ret == 1 && if (instructionInfo(instr).ret == 1 && !instructionInfo(instr).sideEffects)
!SemanticInformation::invalidatesMemory(i0) &&
!SemanticInformation::invalidatesStorage(i0) &&
!SemanticInformation::altersControlFlow(i0) &&
!instructionInfo(i0).sideEffects
)
{ {
for (int j = 0; j < instructionInfo(i0).args; j++) for (int j = 0; j < instructionInfo(instr).args; j++)
*_out = Instruction::POP; *_out = Instruction::POP;
return true; return true;
} }
@ -83,33 +128,33 @@ struct AddPop
} }
}; };
struct DoubleSwap struct DoubleSwap: SimplePeepholeOptimizerMethod<DoubleSwap, 2>
{ {
static size_t windowSize() { return 2; } static size_t applySimple(AssemblyItem const& _s1, AssemblyItem const& _s2, std::back_insert_iterator<AssemblyItems>)
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems>)
{ {
if (_in[0] == _in[1] && SemanticInformation::isSwapInstruction(_in[0])) return _s1 == _s2 && SemanticInformation::isSwapInstruction(_s1);
return true;
else
return false;
} }
}; };
struct JumpToNext struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext, 3>
{ {
static size_t windowSize() { return 3; } static size_t applySimple(
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out) AssemblyItem const& _pushTag,
AssemblyItem const& _jump,
AssemblyItem const& _tag,
std::back_insert_iterator<AssemblyItems> _out
)
{ {
if ( if (
_in[0].type() == PushTag && _pushTag.type() == PushTag &&
(_in[1] == Instruction::JUMP || _in[1] == Instruction::JUMPI) && (_jump == Instruction::JUMP || _jump == Instruction::JUMPI) &&
_in[2].type() == Tag && _tag.type() == Tag &&
_in[0].data() == _in[2].data() _pushTag.data() == _tag.data()
) )
{ {
if (_in[1] == Instruction::JUMPI) if (_jump == Instruction::JUMPI)
*_out = AssemblyItem(Instruction::POP, _in[1].location()); *_out = AssemblyItem(Instruction::POP, _jump.location());
*_out = _in[2]; *_out = _tag;
return true; return true;
} }
else else
@ -117,19 +162,23 @@ struct JumpToNext
} }
}; };
struct TagConjunctions struct TagConjunctions: SimplePeepholeOptimizerMethod<TagConjunctions, 3>
{ {
static size_t windowSize() { return 3; } static bool applySimple(
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out) AssemblyItem const& _pushTag,
AssemblyItem const& _pushConstant,
AssemblyItem const& _and,
std::back_insert_iterator<AssemblyItems> _out
)
{ {
if ( if (
_in[0].type() == PushTag && _pushTag.type() == PushTag &&
_in[2] == Instruction::AND && _and == Instruction::AND &&
_in[1].type() == Push && _pushConstant.type() == Push &&
(_in[1].data() & u256(0xFFFFFFFF)) == u256(0xFFFFFFFF) (_pushConstant.data() & u256(0xFFFFFFFF)) == u256(0xFFFFFFFF)
) )
{ {
*_out = _in[0]; *_out = _pushTag;
return true; return true;
} }
else else
@ -137,11 +186,35 @@ struct TagConjunctions
} }
}; };
struct OptimiserState /// Removes everything after a JUMP (or similar) until the next JUMPDEST.
struct UnreachableCode
{ {
AssemblyItems const& items; static bool apply(OptimiserState& _state)
size_t i; {
std::back_insert_iterator<AssemblyItems> out; auto it = _state.items.begin() + _state.i;
auto end = _state.items.end();
if (it == end)
return false;
if (
it[0] != Instruction::JUMP &&
it[0] != Instruction::RETURN &&
it[0] != Instruction::STOP &&
it[0] != Instruction::SUICIDE
)
return false;
size_t i = 1;
while (it + i != end && it[i].type() != Tag)
i++;
if (i > 1)
{
*_state.out = it[0];
_state.i += i;
return true;
}
else
return false;
}
}; };
void applyMethods(OptimiserState&) void applyMethods(OptimiserState&)
@ -152,9 +225,7 @@ void applyMethods(OptimiserState&)
template <typename Method, typename... OtherMethods> template <typename Method, typename... OtherMethods>
void applyMethods(OptimiserState& _state, Method, OtherMethods... _other) void applyMethods(OptimiserState& _state, Method, OtherMethods... _other)
{ {
if (_state.i + Method::windowSize() <= _state.items.size() && Method::apply(_state.items.begin() + _state.i, _state.out)) if (!Method::apply(_state))
_state.i += Method::windowSize();
else
applyMethods(_state, _other...); applyMethods(_state, _other...);
} }
@ -162,7 +233,7 @@ bool PeepholeOptimiser::optimise()
{ {
OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
while (state.i < m_items.size()) while (state.i < m_items.size())
applyMethods(state, PushPop(), AddPop(), DoubleSwap(), JumpToNext(), TagConjunctions(), Identity()); applyMethods(state, PushPop(), OpPop(), DoubleSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity());
if (m_optimisedItems.size() < m_items.size()) if (m_optimisedItems.size() < m_items.size())
{ {
m_items = std::move(m_optimisedItems); m_items = std::move(m_optimisedItems);

View File

@ -20,17 +20,21 @@
* Tests for the Solidity optimizer. * Tests for the Solidity optimizer.
*/ */
#include <string>
#include <tuple>
#include <memory>
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
#include <test/libsolidity/SolidityExecutionFramework.h> #include <test/libsolidity/SolidityExecutionFramework.h>
#include <libevmasm/CommonSubexpressionEliminator.h> #include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/PeepholeOptimiser.h>
#include <libevmasm/ControlFlowGraph.h> #include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libevmasm/BlockDeduplicator.h> #include <libevmasm/BlockDeduplicator.h>
#include <boost/test/unit_test.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
#include <tuple>
#include <memory>
using namespace std; using namespace std;
using namespace dev::eth; using namespace dev::eth;
@ -1121,6 +1125,40 @@ BOOST_AUTO_TEST_CASE(block_deduplicator_loops)
BOOST_CHECK_EQUAL(pushTags.size(), 1); BOOST_CHECK_EQUAL(pushTags.size(), 1);
} }
BOOST_AUTO_TEST_CASE(clear_unreachable_code)
{
AssemblyItems items{
AssemblyItem(PushTag, 1),
Instruction::JUMP,
u256(0),
Instruction::SLOAD,
AssemblyItem(Tag, 2),
u256(5),
u256(6),
Instruction::SSTORE,
AssemblyItem(PushTag, 1),
Instruction::JUMP,
u256(5),
u256(6)
};
AssemblyItems expectation{
AssemblyItem(PushTag, 1),
Instruction::JUMP,
AssemblyItem(Tag, 2),
u256(5),
u256(6),
Instruction::SSTORE,
AssemblyItem(PushTag, 1),
Instruction::JUMP
};
PeepholeOptimiser peepOpt(items);
BOOST_REQUIRE(peepOpt.optimise());
BOOST_CHECK_EQUAL_COLLECTIONS(
items.begin(), items.end(),
expectation.begin(), expectation.end()
);
}
BOOST_AUTO_TEST_CASE(computing_constants) BOOST_AUTO_TEST_CASE(computing_constants)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(