diff --git a/Changelog.md b/Changelog.md index 12b2e848d..5d07fef31 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,7 @@ Compiler Features: Bugfixes: * 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. + * Compiler Internals: Reset the Yul string repository before each compilation, freeing up memory. * SMTChecker: Fix bad cast in base constructor modifier. * SMTChecker: Fix internal error when visiting state variable inherited from base class. * SMTChecker: Fix internal error in fixed point operations. diff --git a/libsolc/libsolc.cpp b/libsolc/libsolc.cpp index 89859458c..060baaa2a 100644 --- a/libsolc/libsolc.cpp +++ b/libsolc/libsolc.cpp @@ -21,10 +21,11 @@ */ #include -#include -#include #include #include +#include +#include +#include #include @@ -100,6 +101,9 @@ extern char const* solidity_compile(char const* _input, CStyleReadFileCallback _ } 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(); } } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 6ab9789fc..8cd8dea8a 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -965,6 +965,8 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept { + YulStringRepository::reset(); + try { auto parsed = parseInput(_input); diff --git a/libyul/YulString.h b/libyul/YulString.h index 47fb960a9..ae35d3ecd 100644 --- a/libyul/YulString.h +++ b/libyul/YulString.h @@ -26,6 +26,7 @@ #include #include #include +#include namespace yul { @@ -34,7 +35,7 @@ namespace yul /// 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 /// non-deterministic) and a deterministic string hash. -class YulStringRepository: boost::noncopyable +class YulStringRepository { public: struct Handle @@ -43,13 +44,12 @@ public: std::uint64_t hash; }; - YulStringRepository() = default; - static YulStringRepository& instance() { static YulStringRepository inst; return inst; } + Handle stringToHandle(std::string const& _string) { if (_string.empty()) @@ -62,6 +62,7 @@ public: m_strings.emplace_back(std::make_shared(_string)); size_t id = m_strings.size() - 1; m_hashToID.emplace_hint(range.second, std::make_pair(h, id)); + return Handle{id, h}; } std::string const& idToString(size_t _id) const { return *m_strings.at(_id); } @@ -79,8 +80,39 @@ public: return hash; } 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 _fun) + { + YulStringRepository::resetCallbacks().emplace_back(std::move(_fun)); + } + }; private: + YulStringRepository() = default; + YulStringRepository(YulStringRepository const&) = delete; + YulStringRepository(YulStringRepository&&) = default; + YulStringRepository& operator=(YulStringRepository const& _rhs) = delete; + YulStringRepository& operator=(YulStringRepository&& _rhs) = default; + + static std::vector>& resetCallbacks() + { + static std::vector> callbacks; + return callbacks; + } + std::vector> m_strings = {std::make_shared()}; std::unordered_multimap m_hashToID = {{emptyHash(), 0}}; }; diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 36244d576..ffc96eb83 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -182,6 +182,7 @@ BuiltinFunctionForEVM const* EVMDialect::builtin(YulString _name) const EVMDialect const& EVMDialect::looseAssemblyForEVM(langutil::EVMVersion _version) { static map> dialects; + static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }}; if (!dialects[_version]) dialects[_version] = make_unique(AsmFlavour::Loose, false, _version); return *dialects[_version]; @@ -190,6 +191,7 @@ EVMDialect const& EVMDialect::looseAssemblyForEVM(langutil::EVMVersion _version) EVMDialect const& EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version) { static map> dialects; + static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }}; if (!dialects[_version]) dialects[_version] = make_unique(AsmFlavour::Strict, false, _version); return *dialects[_version]; @@ -198,6 +200,7 @@ EVMDialect const& EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _version) { static map> dialects; + static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }}; if (!dialects[_version]) dialects[_version] = make_unique(AsmFlavour::Strict, true, _version); return *dialects[_version]; @@ -206,6 +209,7 @@ EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _ EVMDialect const& EVMDialect::yulForEVM(langutil::EVMVersion _version) { static map> dialects; + static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }}; if (!dialects[_version]) dialects[_version] = make_unique(AsmFlavour::Yul, false, _version); return *dialects[_version]; diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index 8b6ff0e73..4a8b077cb 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -63,6 +63,15 @@ BuiltinFunction const* WasmDialect::builtin(YulString _name) const return nullptr; } +WasmDialect const& WasmDialect::instance() +{ + static std::unique_ptr dialect; + static YulStringRepository::ResetCallback callback{[&] { dialect.reset(); }}; + if (!dialect) + dialect = make_unique(); + return *dialect; +} + void WasmDialect::addFunction(string _name, size_t _params, size_t _returns) { YulString name{move(_name)}; diff --git a/libyul/backends/wasm/WasmDialect.h b/libyul/backends/wasm/WasmDialect.h index 54bb76874..0948a8049 100644 --- a/libyul/backends/wasm/WasmDialect.h +++ b/libyul/backends/wasm/WasmDialect.h @@ -42,17 +42,13 @@ struct Object; */ struct WasmDialect: public Dialect { - BuiltinFunction const* builtin(YulString _name) const override; - - static WasmDialect const& instance() - { - static WasmDialect dialect; - return dialect; - } - -protected: WasmDialect(); + BuiltinFunction const* builtin(YulString _name) const override; + + static WasmDialect const& instance(); + +private: void addFunction(std::string _name, size_t _params, size_t _returns); std::map m_functions; diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 86011ffd1..63b1f9e8a 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -142,7 +142,6 @@ void DataFlowAnalyzer::operator()(Block& _block) void DataFlowAnalyzer::handleAssignment(set const& _variables, Expression* _value) { - static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}}; clearValues(_variables); MovableChecker movableChecker{m_dialect}; @@ -150,7 +149,7 @@ void DataFlowAnalyzer::handleAssignment(set const& _variables, Expres movableChecker.visit(*_value); else for (auto const& var: _variables) - m_value[var] = &zero; + m_value[var] = &m_zero; if (_value && _variables.size() == 1) { diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index 100a38665..78d8fa7e2 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -85,6 +86,9 @@ protected: std::set variables; 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. std::vector m_variableScopes; Dialect const& m_dialect; diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 4ab22544f..f764b0b1e 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -179,8 +179,6 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC 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 // an existing variable. auto newVariable = [&](TypedName const& _existingVariable, Expression* _value) { @@ -190,7 +188,7 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC if (_value) varDecl.value = make_unique(std::move(*_value)); else - varDecl.value = make_unique(zero); + varDecl.value = make_unique(Literal{{}, LiteralKind::Number, YulString{"0"}, {}}); newStatements.emplace_back(std::move(varDecl)); }; diff --git a/libyul/optimiser/SSAValueTracker.cpp b/libyul/optimiser/SSAValueTracker.cpp index 23eb9ec28..d4feacbd9 100644 --- a/libyul/optimiser/SSAValueTracker.cpp +++ b/libyul/optimiser/SSAValueTracker.cpp @@ -56,8 +56,7 @@ void SSAValueTracker::setValue(YulString _name, Expression const* _value) OptimizerException, "Source needs to be disambiguated." ); - static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}}; if (!_value) - _value = &zero; + _value = &m_zero; m_values[_name] = _value; } diff --git a/libyul/optimiser/SSAValueTracker.h b/libyul/optimiser/SSAValueTracker.h index 0680485f8..1062ca8e7 100644 --- a/libyul/optimiser/SSAValueTracker.h +++ b/libyul/optimiser/SSAValueTracker.h @@ -22,6 +22,7 @@ #pragma once #include +#include #include #include @@ -51,6 +52,9 @@ public: private: 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 m_values; }; diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index 867f670ca..98e70a3ee 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -131,9 +131,8 @@ bool StructuralSimplifier::expressionAlwaysTrue(Expression const& _expression) return false; }, [](Literal const& _literal) -> bool { - static YulString const trueString("true"); return - (_literal.kind == LiteralKind::Boolean && _literal.value == trueString) || + (_literal.kind == LiteralKind::Boolean && _literal.value == "true"_yulstring) || (_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) != u256(0)) ; } @@ -149,9 +148,8 @@ bool StructuralSimplifier::expressionAlwaysFalse(Expression const& _expression) return false; }, [](Literal const& _literal) -> bool { - static YulString const falseString("false"); return - (_literal.kind == LiteralKind::Boolean && _literal.value == falseString) || + (_literal.kind == LiteralKind::Boolean && _literal.value == "false"_yulstring) || (_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) == u256(0)) ; } diff --git a/libyul/optimiser/VarDeclInitializer.cpp b/libyul/optimiser/VarDeclInitializer.cpp index 7009cc9b2..5a18dfa32 100644 --- a/libyul/optimiser/VarDeclInitializer.cpp +++ b/libyul/optimiser/VarDeclInitializer.cpp @@ -29,17 +29,16 @@ void VarDeclInitializer::operator()(Block& _block) { ASTModifier::operator()(_block); - static Expression const zero{Literal{{}, LiteralKind::Number, YulString{"0"}, {}}}; - using OptionalStatements = boost::optional>; GenericFallbackReturnsVisitor visitor{ [](VariableDeclaration& _varDecl) -> OptionalStatements { if (_varDecl.value) return {}; - else if (_varDecl.variables.size() == 1) + Literal zero{{}, LiteralKind::Number, YulString{"0"}, {}}; + if (_varDecl.variables.size() == 1) { - _varDecl.value = make_unique(zero); + _varDecl.value = make_unique(std::move(zero)); return {}; } else @@ -47,7 +46,7 @@ void VarDeclInitializer::operator()(Block& _block) OptionalStatements ret{vector{}}; langutil::SourceLocation loc{std::move(_varDecl.location)}; for (auto& var: _varDecl.variables) - ret->push_back(VariableDeclaration{loc, {std::move(var)}, make_unique(zero)}); + ret->emplace_back(VariableDeclaration{loc, {std::move(var)}, make_unique(zero)}); return ret; } } diff --git a/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp index fe9f9609e..e795d047d 100644 --- a/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_assembly_ossfuzz.cpp @@ -27,6 +27,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) if (_size > 600) return 0; + YulStringRepository::reset(); + string input(reinterpret_cast(_data), _size); AssemblyStack stack( langutil::EVMVersion(), diff --git a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp index f7f077bd5..c61bf9817 100644 --- a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp @@ -55,6 +55,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) })) return 0; + YulStringRepository::reset(); + AssemblyStack stack( langutil::EVMVersion(), AssemblyStack::Language::StrictAssembly, diff --git a/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp index 28e1984b2..70c385847 100644 --- a/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_opt_ossfuzz.cpp @@ -26,6 +26,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) if (_size > 600) return 0; + YulStringRepository::reset(); + string input(reinterpret_cast(_data), _size); AssemblyStack stack( langutil::EVMVersion(), diff --git a/test/tools/ossfuzz/yulProtoFuzzer.cpp b/test/tools/ossfuzz/yulProtoFuzzer.cpp index 1cdfc124e..604fc88d6 100644 --- a/test/tools/ossfuzz/yulProtoFuzzer.cpp +++ b/test/tools/ossfuzz/yulProtoFuzzer.cpp @@ -46,6 +46,8 @@ DEFINE_PROTO_FUZZER(Program const& _input) if (yul_source.size() > 1200) return; + YulStringRepository::reset(); + // AssemblyStack entry point AssemblyStack stack( langutil::EVMVersion(), diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 9cafeaff6..4102bb4b0 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -53,6 +53,8 @@ DEFINE_PROTO_FUZZER(Program const& _input) if (yul_source.size() > 1200) return; + YulStringRepository::reset(); + // AssemblyStack entry point AssemblyStack stack( langutil::EVMVersion(),