diff --git a/Changelog.md b/Changelog.md index 45ec293a15fe..7d9af1cdbfcc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: * Code Generator: Transient storage value type state variables are now supported by the legacy pipeline. + * General: Generate JSON representations of Yul ASTs only on demand to reduce memory usage. Bugfixes: diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 9848b6b59ce8..bac358a939f4 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -782,6 +782,28 @@ void CompilerStack::link() } } +YulStack CompilerStack::loadGeneratedIR(std::string const& _ir) const +{ + YulStack stack( + m_evmVersion, + m_eofVersion, + YulStack::Language::StrictAssembly, + m_optimiserSettings, + m_debugInfoSelection, + this, // _soliditySourceProvider + m_objectOptimizer + ); + bool yulAnalysisSuccessful = stack.parseAndAnalyze("", _ir); + solAssert( + yulAnalysisSuccessful, + _ir + "\n\n" + "Invalid IR generated:\n" + + SourceReferenceFormatter::formatErrorInformation(stack.errors(), stack) + "\n" + ); + + return stack; +} + std::vector CompilerStack::contractNames() const { solAssert(m_stackState >= Parsed, "Parsing was not successful."); @@ -934,21 +956,24 @@ std::string const& CompilerStack::yulIR(std::string const& _contractName) const return contract(_contractName).yulIR; } -Json const& CompilerStack::yulIRAst(std::string const& _contractName) const +Json CompilerStack::yulIRAst(std::string const& _contractName) const { solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful."); solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).yulIRAst; + + // NOTE: Intentionally not using LazyInit. The artifact can get very large and we don't want to + // keep it around when compiling a large project containing many contracts. + return loadGeneratedIR(contract(_contractName).yulIR).astJson(); } -Json const& CompilerStack::yulCFGJson(std::string const& _contractName) const +Json CompilerStack::yulCFGJson(std::string const& _contractName) const { - if (m_stackState != CompilationSuccessful) - solThrow(CompilerError, "Compilation was not successful."); - + solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful."); solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).yulCFGJson; + // NOTE: Intentionally not using LazyInit. The artifact can get very large and we don't want to + // keep it around when compiling a large project containing many contracts. + return loadGeneratedIR(contract(_contractName).yulIR).cfgJson(); } std::string const& CompilerStack::yulIROptimized(std::string const& _contractName) const @@ -957,11 +982,14 @@ std::string const& CompilerStack::yulIROptimized(std::string const& _contractNam return contract(_contractName).yulIROptimized; } -Json const& CompilerStack::yulIROptimizedAst(std::string const& _contractName) const +Json CompilerStack::yulIROptimizedAst(std::string const& _contractName) const { solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful."); solUnimplementedAssert(!isExperimentalSolidity()); - return contract(_contractName).yulIROptimizedAst; + + // NOTE: Intentionally not using LazyInit. The artifact can get very large and we don't want to + // keep it around when compiling a large project containing many contracts. + return loadGeneratedIR(contract(_contractName).yulIROptimized).astJson(); } evmasm::LinkerObject const& CompilerStack::object(std::string const& _contractName) const @@ -1516,30 +1544,11 @@ void CompilerStack::generateIR(ContractDefinition const& _contract, bool _unopti ); } - YulStack stack( - m_evmVersion, - m_eofVersion, - YulStack::Language::StrictAssembly, - m_optimiserSettings, - m_debugInfoSelection, - this, // _soliditySourceProvider - m_objectOptimizer - ); - bool yulAnalysisSuccessful = stack.parseAndAnalyze("", compiledContract.yulIR); - solAssert( - yulAnalysisSuccessful, - compiledContract.yulIR + "\n\n" - "Invalid IR generated:\n" + - SourceReferenceFormatter::formatErrorInformation(stack.errors(), stack) + "\n" - ); - - compiledContract.yulIRAst = stack.astJson(); - compiledContract.yulCFGJson = stack.cfgJson(); + YulStack stack = loadGeneratedIR(compiledContract.yulIR); if (!_unoptimizedOnly) { stack.optimize(); compiledContract.yulIROptimized = stack.print(); - compiledContract.yulIROptimizedAst = stack.astJson(); } } @@ -1556,17 +1565,7 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract) return; // Re-parse the Yul IR in EVM dialect - yul::YulStack stack( - m_evmVersion, - m_eofVersion, - yul::YulStack::Language::StrictAssembly, - m_optimiserSettings, - m_debugInfoSelection, - this, // _soliditySourceProvider - m_objectOptimizer - ); - bool analysisSuccessful = stack.parseAndAnalyze("", compiledContract.yulIROptimized); - solAssert(analysisSuccessful); + YulStack stack = loadGeneratedIR(compiledContract.yulIROptimized); //cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl; diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 0a62cbf7efaf..c0166af96d5b 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -71,6 +71,11 @@ class AssemblyItem; using AssemblyItems = std::vector; } +namespace solidity::yul +{ +class YulStack; +} + namespace solidity::frontend { @@ -290,15 +295,15 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac std::string const& yulIR(std::string const& _contractName) const; /// @returns the IR representation of a contract AST in format. - Json const& yulIRAst(std::string const& _contractName) const; + Json yulIRAst(std::string const& _contractName) const; /// @returns the optimized IR representation of a contract. std::string const& yulIROptimized(std::string const& _contractName) const; /// @returns the optimized IR representation of a contract AST in JSON format. - Json const& yulIROptimizedAst(std::string const& _contractName) const; + Json yulIROptimizedAst(std::string const& _contractName) const; - Json const& yulCFGJson(std::string const& _contractName) const; + Json yulCFGJson(std::string const& _contractName) const; /// @returns the assembled object for a contract. virtual evmasm::LinkerObject const& object(std::string const& _contractName) const override; @@ -411,9 +416,6 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac evmasm::LinkerObject runtimeObject; ///< Runtime object. std::string yulIR; ///< Yul IR code straight from the code generator. std::string yulIROptimized; ///< Reparsed and possibly optimized Yul IR code. - Json yulIRAst; ///< JSON AST of Yul IR code. - Json yulIROptimizedAst; ///< JSON AST of optimized Yul IR code. - Json yulCFGJson; ///< JSON CFG of Yul IR code. util::LazyInit metadata; ///< The metadata json that will be hashed into the chain. util::LazyInit abi; util::LazyInit storageLayout; @@ -490,6 +492,11 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac /// library will still be kept as an unlinked placeholder in the objects. void link(); + /// Parses and analyzes specified Yul source and returns the YulStack that can be used to manipulate it. + /// Assumes that the IR was generated from sources loaded currently into CompilerStack, which + /// means that it is error-free and uses the same settings. + yul::YulStack loadGeneratedIR(std::string const& _ir) const; + /// @returns the contract object for the given @a _contractName. /// Can only be called after state is CompilationSuccessful. Contract const& contract(std::string const& _contractName) const;