Reset Yul string repository before each compilation.

This commit is contained in:
Bhargava Shastry 2019-04-24 14:03:09 +02:00 committed by chriseth
parent b95eebee1c
commit dbae0fa939
19 changed files with 89 additions and 30 deletions

View File

@ -22,6 +22,7 @@ Compiler Features:
Bugfixes: Bugfixes:
* Code Generator: Explicitly turn uninitialized internal function pointers into invalid functions when loaded from storage. * Code Generator: Explicitly turn uninitialized internal function pointers into invalid functions when loaded from storage.
* Code Generator: Fix assertion failure when assigning structs containing array of mapping. * Code Generator: Fix assertion failure when assigning structs containing array of mapping.
* Compiler Internals: Reset the Yul string repository before each compilation, freeing up memory.
* SMTChecker: Fix bad cast in base constructor modifier. * SMTChecker: Fix bad cast in base constructor modifier.
* SMTChecker: Fix internal error when visiting state variable inherited from base class. * SMTChecker: Fix internal error when visiting state variable inherited from base class.
* SMTChecker: Fix internal error in fixed point operations. * SMTChecker: Fix internal error in fixed point operations.

View File

@ -21,10 +21,11 @@
*/ */
#include <libsolc/libsolc.h> #include <libsolc/libsolc.h>
#include <libdevcore/Common.h>
#include <libdevcore/JSON.h>
#include <libsolidity/interface/StandardCompiler.h> #include <libsolidity/interface/StandardCompiler.h>
#include <libsolidity/interface/Version.h> #include <libsolidity/interface/Version.h>
#include <libyul/YulString.h>
#include <libdevcore/Common.h>
#include <libdevcore/JSON.h>
#include <string> #include <string>
@ -100,6 +101,9 @@ extern char const* solidity_compile(char const* _input, CStyleReadFileCallback _
} }
extern void solidity_free() noexcept extern void solidity_free() noexcept
{ {
// This is called right before each compilation, but not at the end, so additional memory
// can be freed here.
yul::YulStringRepository::reset();
s_outputBuffer.clear(); s_outputBuffer.clear();
} }
} }

View File

@ -965,6 +965,8 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept
{ {
YulStringRepository::reset();
try try
{ {
auto parsed = parseInput(_input); auto parsed = parseInput(_input);

View File

@ -26,6 +26,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <string> #include <string>
#include <functional>
namespace yul namespace yul
{ {
@ -34,7 +35,7 @@ namespace yul
/// Owns the string data for all YulStrings, which can be referenced by a Handle. /// Owns the string data for all YulStrings, which can be referenced by a Handle.
/// A Handle consists of an ID (that depends on the insertion order of YulStrings and is potentially /// A Handle consists of an ID (that depends on the insertion order of YulStrings and is potentially
/// non-deterministic) and a deterministic string hash. /// non-deterministic) and a deterministic string hash.
class YulStringRepository: boost::noncopyable class YulStringRepository
{ {
public: public:
struct Handle struct Handle
@ -43,13 +44,12 @@ public:
std::uint64_t hash; std::uint64_t hash;
}; };
YulStringRepository() = default;
static YulStringRepository& instance() static YulStringRepository& instance()
{ {
static YulStringRepository inst; static YulStringRepository inst;
return inst; return inst;
} }
Handle stringToHandle(std::string const& _string) Handle stringToHandle(std::string const& _string)
{ {
if (_string.empty()) if (_string.empty())
@ -62,6 +62,7 @@ public:
m_strings.emplace_back(std::make_shared<std::string>(_string)); m_strings.emplace_back(std::make_shared<std::string>(_string));
size_t id = m_strings.size() - 1; size_t id = m_strings.size() - 1;
m_hashToID.emplace_hint(range.second, std::make_pair(h, id)); m_hashToID.emplace_hint(range.second, std::make_pair(h, id));
return Handle{id, h}; return Handle{id, h};
} }
std::string const& idToString(size_t _id) const { return *m_strings.at(_id); } std::string const& idToString(size_t _id) const { return *m_strings.at(_id); }
@ -79,8 +80,39 @@ public:
return hash; return hash;
} }
static constexpr std::uint64_t emptyHash() { return 14695981039346656037u; } static constexpr std::uint64_t emptyHash() { return 14695981039346656037u; }
/// Clear the repository.
/// Use with care - there cannot be any dangling YulString references.
/// If references need to be cleared manually, register the callback via
/// resetCallback.
static void reset()
{
for (auto const& cb: resetCallbacks())
cb();
instance() = YulStringRepository{};
}
/// Struct that registers a reset callback as a side-effect of its construction.
/// Useful as static local variable to register a reset callback once.
struct ResetCallback
{
ResetCallback(std::function<void()> _fun)
{
YulStringRepository::resetCallbacks().emplace_back(std::move(_fun));
}
};
private: private:
YulStringRepository() = default;
YulStringRepository(YulStringRepository const&) = delete;
YulStringRepository(YulStringRepository&&) = default;
YulStringRepository& operator=(YulStringRepository const& _rhs) = delete;
YulStringRepository& operator=(YulStringRepository&& _rhs) = default;
static std::vector<std::function<void()>>& resetCallbacks()
{
static std::vector<std::function<void()>> callbacks;
return callbacks;
}
std::vector<std::shared_ptr<std::string>> m_strings = {std::make_shared<std::string>()}; std::vector<std::shared_ptr<std::string>> m_strings = {std::make_shared<std::string>()};
std::unordered_multimap<std::uint64_t, size_t> m_hashToID = {{emptyHash(), 0}}; std::unordered_multimap<std::uint64_t, size_t> m_hashToID = {{emptyHash(), 0}};
}; };

View File

@ -182,6 +182,7 @@ BuiltinFunctionForEVM const* EVMDialect::builtin(YulString _name) const
EVMDialect const& EVMDialect::looseAssemblyForEVM(langutil::EVMVersion _version) EVMDialect const& EVMDialect::looseAssemblyForEVM(langutil::EVMVersion _version)
{ {
static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects; static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects;
static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }};
if (!dialects[_version]) if (!dialects[_version])
dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Loose, false, _version); dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Loose, false, _version);
return *dialects[_version]; return *dialects[_version];
@ -190,6 +191,7 @@ EVMDialect const& EVMDialect::looseAssemblyForEVM(langutil::EVMVersion _version)
EVMDialect const& EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version) EVMDialect const& EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version)
{ {
static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects; static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects;
static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }};
if (!dialects[_version]) if (!dialects[_version])
dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Strict, false, _version); dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Strict, false, _version);
return *dialects[_version]; return *dialects[_version];
@ -198,6 +200,7 @@ EVMDialect const& EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version
EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _version) EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _version)
{ {
static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects; static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects;
static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }};
if (!dialects[_version]) if (!dialects[_version])
dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Strict, true, _version); dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Strict, true, _version);
return *dialects[_version]; return *dialects[_version];
@ -206,6 +209,7 @@ EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _
EVMDialect const& EVMDialect::yulForEVM(langutil::EVMVersion _version) EVMDialect const& EVMDialect::yulForEVM(langutil::EVMVersion _version)
{ {
static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects; static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects;
static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }};
if (!dialects[_version]) if (!dialects[_version])
dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Yul, false, _version); dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Yul, false, _version);
return *dialects[_version]; return *dialects[_version];

View File

@ -63,6 +63,15 @@ BuiltinFunction const* WasmDialect::builtin(YulString _name) const
return nullptr; return nullptr;
} }
WasmDialect const& WasmDialect::instance()
{
static std::unique_ptr<WasmDialect> dialect;
static YulStringRepository::ResetCallback callback{[&] { dialect.reset(); }};
if (!dialect)
dialect = make_unique<WasmDialect>();
return *dialect;
}
void WasmDialect::addFunction(string _name, size_t _params, size_t _returns) void WasmDialect::addFunction(string _name, size_t _params, size_t _returns)
{ {
YulString name{move(_name)}; YulString name{move(_name)};

View File

@ -42,17 +42,13 @@ struct Object;
*/ */
struct WasmDialect: public Dialect struct WasmDialect: public Dialect
{ {
BuiltinFunction const* builtin(YulString _name) const override;
static WasmDialect const& instance()
{
static WasmDialect dialect;
return dialect;
}
protected:
WasmDialect(); WasmDialect();
BuiltinFunction const* builtin(YulString _name) const override;
static WasmDialect const& instance();
private:
void addFunction(std::string _name, size_t _params, size_t _returns); void addFunction(std::string _name, size_t _params, size_t _returns);
std::map<YulString, BuiltinFunction> m_functions; std::map<YulString, BuiltinFunction> m_functions;

View File

@ -142,7 +142,6 @@ void DataFlowAnalyzer::operator()(Block& _block)
void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expression* _value) void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expression* _value)
{ {
static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}};
clearValues(_variables); clearValues(_variables);
MovableChecker movableChecker{m_dialect}; MovableChecker movableChecker{m_dialect};
@ -150,7 +149,7 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
movableChecker.visit(*_value); movableChecker.visit(*_value);
else else
for (auto const& var: _variables) for (auto const& var: _variables)
m_value[var] = &zero; m_value[var] = &m_zero;
if (_value && _variables.size() == 1) if (_value && _variables.size() == 1)
{ {

View File

@ -24,6 +24,7 @@
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <libyul/AsmData.h>
#include <map> #include <map>
#include <set> #include <set>
@ -85,6 +86,9 @@ protected:
std::set<YulString> variables; std::set<YulString> variables;
bool isFunction; bool isFunction;
}; };
/// Special expression whose address will be used in m_value.
/// YulString does not need to be reset because DataFlowAnalyzer is short-lived.
Expression const m_zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}};
/// List of scopes. /// List of scopes.
std::vector<Scope> m_variableScopes; std::vector<Scope> m_variableScopes;
Dialect const& m_dialect; Dialect const& m_dialect;

View File

@ -179,8 +179,6 @@ vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionC
m_driver.tentativelyUpdateCodeSize(function->name, m_currentFunction); m_driver.tentativelyUpdateCodeSize(function->name, m_currentFunction);
static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}};
// helper function to create a new variable that is supposed to model // helper function to create a new variable that is supposed to model
// an existing variable. // an existing variable.
auto newVariable = [&](TypedName const& _existingVariable, Expression* _value) { auto newVariable = [&](TypedName const& _existingVariable, Expression* _value) {
@ -190,7 +188,7 @@ vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionC
if (_value) if (_value)
varDecl.value = make_unique<Expression>(std::move(*_value)); varDecl.value = make_unique<Expression>(std::move(*_value));
else else
varDecl.value = make_unique<Expression>(zero); varDecl.value = make_unique<Expression>(Literal{{}, LiteralKind::Number, YulString{"0"}, {}});
newStatements.emplace_back(std::move(varDecl)); newStatements.emplace_back(std::move(varDecl));
}; };

View File

@ -56,8 +56,7 @@ void SSAValueTracker::setValue(YulString _name, Expression const* _value)
OptimizerException, OptimizerException,
"Source needs to be disambiguated." "Source needs to be disambiguated."
); );
static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}};
if (!_value) if (!_value)
_value = &zero; _value = &m_zero;
m_values[_name] = _value; m_values[_name] = _value;
} }

View File

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/AsmData.h>
#include <map> #include <map>
#include <set> #include <set>
@ -51,6 +52,9 @@ public:
private: private:
void setValue(YulString _name, Expression const* _value); void setValue(YulString _name, Expression const* _value);
/// Special expression whose address will be used in m_values.
/// YulString does not need to be reset because SSAValueTracker is short-lived.
Expression const m_zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}};
std::map<YulString, Expression const*> m_values; std::map<YulString, Expression const*> m_values;
}; };

View File

@ -131,9 +131,8 @@ bool StructuralSimplifier::expressionAlwaysTrue(Expression const& _expression)
return false; return false;
}, },
[](Literal const& _literal) -> bool { [](Literal const& _literal) -> bool {
static YulString const trueString("true");
return return
(_literal.kind == LiteralKind::Boolean && _literal.value == trueString) || (_literal.kind == LiteralKind::Boolean && _literal.value == "true"_yulstring) ||
(_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) != u256(0)) (_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) != u256(0))
; ;
} }
@ -149,9 +148,8 @@ bool StructuralSimplifier::expressionAlwaysFalse(Expression const& _expression)
return false; return false;
}, },
[](Literal const& _literal) -> bool { [](Literal const& _literal) -> bool {
static YulString const falseString("false");
return return
(_literal.kind == LiteralKind::Boolean && _literal.value == falseString) || (_literal.kind == LiteralKind::Boolean && _literal.value == "false"_yulstring) ||
(_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) == u256(0)) (_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) == u256(0))
; ;
} }

View File

@ -29,17 +29,16 @@ void VarDeclInitializer::operator()(Block& _block)
{ {
ASTModifier::operator()(_block); ASTModifier::operator()(_block);
static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}};
using OptionalStatements = boost::optional<vector<Statement>>; using OptionalStatements = boost::optional<vector<Statement>>;
GenericFallbackReturnsVisitor<OptionalStatements, VariableDeclaration> visitor{ GenericFallbackReturnsVisitor<OptionalStatements, VariableDeclaration> visitor{
[](VariableDeclaration& _varDecl) -> OptionalStatements [](VariableDeclaration& _varDecl) -> OptionalStatements
{ {
if (_varDecl.value) if (_varDecl.value)
return {}; return {};
else if (_varDecl.variables.size() == 1) Literal zero{{}, LiteralKind::Number, YulString{"0"}, {}};
if (_varDecl.variables.size() == 1)
{ {
_varDecl.value = make_unique<Expression>(zero); _varDecl.value = make_unique<Expression>(std::move(zero));
return {}; return {};
} }
else else
@ -47,7 +46,7 @@ void VarDeclInitializer::operator()(Block& _block)
OptionalStatements ret{vector<Statement>{}}; OptionalStatements ret{vector<Statement>{}};
langutil::SourceLocation loc{std::move(_varDecl.location)}; langutil::SourceLocation loc{std::move(_varDecl.location)};
for (auto& var: _varDecl.variables) for (auto& var: _varDecl.variables)
ret->push_back(VariableDeclaration{loc, {std::move(var)}, make_unique<Expression>(zero)}); ret->emplace_back(VariableDeclaration{loc, {std::move(var)}, make_unique<Expression>(zero)});
return ret; return ret;
} }
} }

View File

@ -27,6 +27,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
if (_size > 600) if (_size > 600)
return 0; return 0;
YulStringRepository::reset();
string input(reinterpret_cast<char const*>(_data), _size); string input(reinterpret_cast<char const*>(_data), _size);
AssemblyStack stack( AssemblyStack stack(
langutil::EVMVersion(), langutil::EVMVersion(),

View File

@ -55,6 +55,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
})) }))
return 0; return 0;
YulStringRepository::reset();
AssemblyStack stack( AssemblyStack stack(
langutil::EVMVersion(), langutil::EVMVersion(),
AssemblyStack::Language::StrictAssembly, AssemblyStack::Language::StrictAssembly,

View File

@ -26,6 +26,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size)
if (_size > 600) if (_size > 600)
return 0; return 0;
YulStringRepository::reset();
string input(reinterpret_cast<char const*>(_data), _size); string input(reinterpret_cast<char const*>(_data), _size);
AssemblyStack stack( AssemblyStack stack(
langutil::EVMVersion(), langutil::EVMVersion(),

View File

@ -46,6 +46,8 @@ DEFINE_PROTO_FUZZER(Program const& _input)
if (yul_source.size() > 1200) if (yul_source.size() > 1200)
return; return;
YulStringRepository::reset();
// AssemblyStack entry point // AssemblyStack entry point
AssemblyStack stack( AssemblyStack stack(
langutil::EVMVersion(), langutil::EVMVersion(),

View File

@ -53,6 +53,8 @@ DEFINE_PROTO_FUZZER(Program const& _input)
if (yul_source.size() > 1200) if (yul_source.size() > 1200)
return; return;
YulStringRepository::reset();
// AssemblyStack entry point // AssemblyStack entry point
AssemblyStack stack( AssemblyStack stack(
langutil::EVMVersion(), langutil::EVMVersion(),