Merge pull request #4983 from ethereum/optimizerEmscriptenBug

Fix weird Optimizer Emscripten Bug
This commit is contained in:
chriseth 2018-09-18 15:39:46 +02:00 committed by GitHub
commit 4b656420ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 74 additions and 14 deletions

View File

@ -102,6 +102,7 @@ Bugfixes:
* Commandline Interface: Correctly handle paths with backslashes on windows. * Commandline Interface: Correctly handle paths with backslashes on windows.
* Fix NatSpec json output for `@notice` and `@dev` tags on contract definitions. * Fix NatSpec json output for `@notice` and `@dev` tags on contract definitions.
* Optimizer: Correctly estimate gas costs of constants for special cases. * Optimizer: Correctly estimate gas costs of constants for special cases.
* Optimizer: Fix simplification rule initialization bug that appeared on some emscripten platforms.
* References Resolver: Do not crash on using ``_slot`` and ``_offset`` suffixes on their own. * References Resolver: Do not crash on using ``_slot`` and ``_offset`` suffixes on their own.
* References Resolver: Enforce ``storage`` as data location for mappings. * References Resolver: Enforce ``storage`` as data location for mappings.
* References Resolver: Properly handle invalid references used together with ``_slot`` and ``_offset``. * References Resolver: Properly handle invalid references used together with ``_slot`` and ``_offset``.

View File

@ -184,6 +184,7 @@ string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const
ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr) ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr)
{ {
static Rules rules; static Rules rules;
assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized.");
if ( if (
!_expr.item || !_expr.item ||

View File

@ -44,12 +44,11 @@ template <class S> S modWorkaround(S const& _a, S const& _b)
return (S)(bigint(_a) % bigint(_b)); return (S)(bigint(_a) % bigint(_b));
} }
/// @returns a list of simplification rules given certain match placeholders. // This part of simplificationRuleList below was split out to prevent
/// A, B and C should represent constants, X and Y arbitrary expressions. // stack overflows in the JavaScript optimizer for emscripten builds
/// The simplifications should never change the order of evaluation of // that affected certain browser versions.
/// arbitrary operations.
template <class Pattern> template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleList( std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
Pattern A, Pattern A,
Pattern B, Pattern B,
Pattern C, Pattern C,
@ -57,8 +56,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
Pattern Y Pattern Y
) )
{ {
std::vector<SimplificationRule<Pattern>> rules; return std::vector<SimplificationRule<Pattern>> {
rules += std::vector<SimplificationRule<Pattern>>{
// arithmetic on constants // arithmetic on constants
{{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false}, {{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false},
{{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false}, {{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false},
@ -162,6 +160,22 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
{{Instruction::OR, {X, {Instruction::NOT, {X}}}}, [=]{ return ~u256(0); }, true}, {{Instruction::OR, {X, {Instruction::NOT, {X}}}}, [=]{ return ~u256(0); }, true},
{{Instruction::OR, {{Instruction::NOT, {X}}, X}}, [=]{ return ~u256(0); }, true}, {{Instruction::OR, {{Instruction::NOT, {X}}, X}}, [=]{ return ~u256(0); }, true},
}; };
}
// This part of simplificationRuleList below was split out to prevent
// stack overflows in the JavaScript optimizer for emscripten builds
// that affected certain browser versions.
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
Pattern A,
Pattern B,
Pattern,
Pattern X,
Pattern Y
)
{
std::vector<SimplificationRule<Pattern>> rules;
// Replace MOD X, <power-of-two> with AND X, <power-of-two> - 1 // Replace MOD X, <power-of-two> with AND X, <power-of-two> - 1
for (size_t i = 0; i < 256; ++i) for (size_t i = 0; i < 256; ++i)
@ -292,5 +306,24 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
return rules; return rules;
} }
/// @returns a list of simplification rules given certain match placeholders.
/// A, B and C should represent constants, X and Y arbitrary expressions.
/// The simplifications should never change the order of evaluation of
/// arbitrary operations.
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleList(
Pattern A,
Pattern B,
Pattern C,
Pattern X,
Pattern Y
)
{
std::vector<SimplificationRule<Pattern>> rules;
rules += simplificationRuleListPart1(A, B, C, X, Y);
rules += simplificationRuleListPart2(A, B, C, X, Y);
return rules;
}
} }
} }

View File

@ -21,16 +21,19 @@
* Container for equivalence classes of expressions for use in common subexpression elimination. * Container for equivalence classes of expressions for use in common subexpression elimination.
*/ */
#include <libevmasm/ExpressionClasses.h>
#include <utility>
#include <functional>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/noncopyable.hpp>
#include <libevmasm/Assembly.h>
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/SimplificationRules.h> #include <libevmasm/SimplificationRules.h>
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/RuleList.h> #include <libevmasm/RuleList.h>
#include <libdevcore/Assertions.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/noncopyable.hpp>
#include <utility>
#include <functional>
using namespace std; using namespace std;
using namespace dev; using namespace dev;
@ -54,6 +57,11 @@ SimplificationRule<Pattern> const* Rules::findFirstMatch(
return nullptr; return nullptr;
} }
bool Rules::isInitialized() const
{
return !m_rules[byte(Instruction::ADD)].empty();
}
void Rules::addRules(std::vector<SimplificationRule<Pattern>> const& _rules) void Rules::addRules(std::vector<SimplificationRule<Pattern>> const& _rules)
{ {
for (auto const& r: _rules) for (auto const& r: _rules)
@ -82,6 +90,7 @@ Rules::Rules()
Y.setMatchGroup(5, m_matchGroups); Y.setMatchGroup(5, m_matchGroups);
addRules(simplificationRuleList(A, B, C, X, Y)); addRules(simplificationRuleList(A, B, C, X, Y));
assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized.");
} }
Pattern::Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments): Pattern::Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments):

View File

@ -26,6 +26,8 @@
#include <libevmasm/ExpressionClasses.h> #include <libevmasm/ExpressionClasses.h>
#include <libevmasm/SimplificationRule.h> #include <libevmasm/SimplificationRule.h>
#include <boost/noncopyable.hpp>
#include <functional> #include <functional>
#include <vector> #include <vector>
@ -53,6 +55,10 @@ public:
ExpressionClasses const& _classes ExpressionClasses const& _classes
); );
/// Checks whether the rulelist is non-empty. This is usually enforced
/// by the constructor, but we had some issues with static initialization.
bool isInitialized() const;
private: private:
void addRules(std::vector<SimplificationRule<Pattern>> const& _rules); void addRules(std::vector<SimplificationRule<Pattern>> const& _rules);
void addRule(SimplificationRule<Pattern> const& _rule); void addRule(SimplificationRule<Pattern> const& _rule);

View File

@ -40,6 +40,7 @@ SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch(Expressio
return nullptr; return nullptr;
static SimplificationRules rules; static SimplificationRules rules;
assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized.");
FunctionalInstruction const& instruction = boost::get<FunctionalInstruction>(_expr); FunctionalInstruction const& instruction = boost::get<FunctionalInstruction>(_expr);
for (auto const& rule: rules.m_rules[byte(instruction.instruction)]) for (auto const& rule: rules.m_rules[byte(instruction.instruction)])
@ -51,6 +52,11 @@ SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch(Expressio
return nullptr; return nullptr;
} }
bool SimplificationRules::isInitialized() const
{
return !m_rules[byte(solidity::Instruction::ADD)].empty();
}
void SimplificationRules::addRules(vector<SimplificationRule<Pattern>> const& _rules) void SimplificationRules::addRules(vector<SimplificationRule<Pattern>> const& _rules)
{ {
for (auto const& r: _rules) for (auto const& r: _rules)
@ -79,6 +85,7 @@ SimplificationRules::SimplificationRules()
Y.setMatchGroup(5, m_matchGroups); Y.setMatchGroup(5, m_matchGroups);
addRules(simplificationRuleList(A, B, C, X, Y)); addRules(simplificationRuleList(A, B, C, X, Y));
assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized.");
} }
Pattern::Pattern(solidity::Instruction _instruction, vector<Pattern> const& _arguments): Pattern::Pattern(solidity::Instruction _instruction, vector<Pattern> const& _arguments):

View File

@ -51,6 +51,9 @@ public:
/// groups accordingly. /// groups accordingly.
static SimplificationRule<Pattern> const* findFirstMatch(Expression const& _expr); static SimplificationRule<Pattern> const* findFirstMatch(Expression const& _expr);
/// Checks whether the rulelist is non-empty. This is usually enforced
/// by the constructor, but we had some issues with static initialization.
bool isInitialized() const;
private: private:
void addRules(std::vector<SimplificationRule<Pattern>> const& _rules); void addRules(std::vector<SimplificationRule<Pattern>> const& _rules);
void addRule(SimplificationRule<Pattern> const& _rule); void addRule(SimplificationRule<Pattern> const& _rule);