From 6d1ed9324723da8b68d8b8ae739923339a896439 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 26 Feb 2019 19:55:13 +0100 Subject: [PATCH] Use stack optimizations. --- Changelog.md | 1 + docs/using-the-compiler.rst | 8 ++++++-- libsolidity/codegen/CompilerContext.cpp | 14 +++++++++++--- libsolidity/codegen/CompilerContext.h | 2 +- libsolidity/codegen/ContractCompiler.cpp | 6 ++++-- libsolidity/interface/CompilerStack.cpp | 6 +++++- libsolidity/interface/OptimiserSettings.h | 9 +++++++-- libsolidity/interface/StandardCompiler.cpp | 13 +++++++++---- libyul/backends/evm/AsmCodeGen.cpp | 4 ++-- libyul/backends/evm/AsmCodeGen.h | 2 +- solc/CommandLineInterface.cpp | 1 + .../input.json | 2 +- .../output.json | 2 +- .../input.json | 16 ++++++++++++++++ .../output.json | 1 + test/libsolidity/StandardCompiler.cpp | 3 +++ 16 files changed, 70 insertions(+), 20 deletions(-) create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_without_yul/input.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json diff --git a/Changelog.md b/Changelog.md index 30d1bf2e1..7e422b8e7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: + * Yul Optimizer: Enable stack allocation optimization by default if yul optimizer is active (disable in yulDetails). Bugfixes: diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index f4b5aceec..ca773fa72 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -216,8 +216,12 @@ Input Description // It can only be activated through the details here. // This feature is still considered experimental. "yul": false, - // Future tuning options, currently unused. - "yulDetails": {} + // Tuning options for the Yul optimizer. + "yulDetails": { + // Improve allocation of stack slots for variables, can free up stack slots early. + // Activated by default if the Yul optimizer is activated. + "stackAllocation": true + } } }, "evmVersion": "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople or petersburg diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 3053e3f7c..e106a4839 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -331,7 +331,7 @@ void CompilerContext::appendInlineAssembly( vector const& _localVariables, set const& _externallyUsedFunctions, bool _system, - bool _optimise + OptimiserSettings const& _optimiserSettings ) { int startStackHeight = stackHeight(); @@ -422,7 +422,7 @@ void CompilerContext::appendInlineAssembly( // Several optimizer steps cannot handle externally supplied stack variables, // so we essentially only optimize the ABI functions. - if (_optimise && _localVariables.empty()) + if (_optimiserSettings.runYulOptimiser && _localVariables.empty()) { yul::OptimiserSuite::run( yul::EVMDialect::strictAssemblyForEVM(m_evmVersion), @@ -445,7 +445,15 @@ void CompilerContext::appendInlineAssembly( reportError("Failed to analyze inline assembly block."); solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block."); - yul::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, m_evmVersion, identifierAccess, _system, _optimise); + yul::CodeGenerator::assemble( + *parserResult, + analysisInfo, + *m_asm, + m_evmVersion, + identifierAccess, + _system, + _optimiserSettings.optimizeStackAllocation + ); // Reset the source location to the one of the node (instead of the CODEGEN source location) updateSourceLocation(); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 44f96f5dd..3744913c3 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -217,7 +217,7 @@ public: std::vector const& _localVariables = std::vector(), std::set const& _externallyUsedFunctions = std::set(), bool _system = false, - bool _optimise = false + OptimiserSettings const& _optimiserSettings = OptimiserSettings::none() ); /// Appends arbitrary data to the end of the bytecode. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index b10882749..2e5338942 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -720,7 +720,9 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) *_inlineAssembly.annotation().analysisInfo, *m_context.assemblyPtr(), m_context.evmVersion(), - identifierAccess + identifierAccess, + false, + m_optimiserSettings.optimizeStackAllocation ); m_context.setStackOffset(startStackHeight); return false; @@ -983,7 +985,7 @@ void ContractCompiler::appendMissingFunctions() {}, abiFunctions.second, true, - m_optimiserSettings.runYulOptimiser + m_optimiserSettings ); } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index c0dc2ffd7..3c753c25b 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -993,7 +993,11 @@ string CompilerStack::createMetadata(Contract const& _contract) const details["cse"] = m_optimiserSettings.runCSE; details["constantOptimizer"] = m_optimiserSettings.runConstantOptimiser; details["yul"] = m_optimiserSettings.runYulOptimiser; - details["yulDetails"] = Json::objectValue; + if (m_optimiserSettings.runYulOptimiser) + { + details["yulDetails"] = Json::objectValue; + details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation; + } meta["settings"]["optimizer"]["details"] = std::move(details); } diff --git a/libsolidity/interface/OptimiserSettings.h b/libsolidity/interface/OptimiserSettings.h index aae5fa1ce..c88778949 100644 --- a/libsolidity/interface/OptimiserSettings.h +++ b/libsolidity/interface/OptimiserSettings.h @@ -54,15 +54,17 @@ struct OptimiserSettings s.runDeduplicate = true; s.runCSE = true; s.runConstantOptimiser = true; - // The only disabled one + // The only disabled ones + s.optimizeStackAllocation = false; s.runYulOptimiser = false; s.expectedExecutionsPerDeployment = 200; return s; } - /// Standard optimisations plus yul optimiser. + /// Standard optimisations plus yul and stack optimiser. static OptimiserSettings full() { OptimiserSettings s = enabled(); + s.optimizeStackAllocation = true; s.runYulOptimiser = true; return s; } @@ -76,6 +78,7 @@ struct OptimiserSettings runDeduplicate == _other.runDeduplicate && runCSE == _other.runCSE && runConstantOptimiser == _other.runConstantOptimiser && + optimizeStackAllocation == _other.optimizeStackAllocation && runYulOptimiser == _other.runYulOptimiser && expectedExecutionsPerDeployment == _other.expectedExecutionsPerDeployment; } @@ -94,6 +97,8 @@ struct OptimiserSettings /// Constant optimizer, which tries to find better representations that satisfy the given /// size/cost-trade-off. bool runConstantOptimiser = false; + /// Perform more efficient stack allocation for variables during code generation from Yul to bytecode. + bool optimizeStackAllocation = false; /// Yul optimiser with default settings. Will only run on certain parts of the code for now. bool runYulOptimiser = false; /// This specifies an estimate on how often each opcode in this assembly will be executed, diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index c17fdf39a..326dac601 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -399,12 +399,17 @@ boost::variant parseOptimizerSettings(Json::Valu return *error; if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser)) return *error; + if (settings.runYulOptimiser) + settings.optimizeStackAllocation = true; if (details.isMember("yulDetails")) { - if (!_jsonInput["yulDetails"].isObject()) - return formatFatalError("JSONError", "The \"yulDetails\" optimizer setting has to be a JSON object."); - if (!_jsonInput["yulDetails"].getMemberNames().empty()) - return formatFatalError("JSONError", "The \"yulDetails\" optimizer setting cannot have any settings yet."); + if (!settings.runYulOptimiser) + return formatFatalError("JSONError", "\"Providing yulDetails requires Yul optimizer to be enabled."); + + if (auto result = checkKeys(details["yulDetails"], {"stackAllocation"}, "settings.optimizer.details.yulDetails")) + return *result; + if (auto error = checkOptimizerDetail(details["yulDetails"], "stackAllocation", settings.optimizeStackAllocation)) + return *error; } } return std::move(settings); diff --git a/libyul/backends/evm/AsmCodeGen.cpp b/libyul/backends/evm/AsmCodeGen.cpp index 19c87b77d..489bfdc19 100644 --- a/libyul/backends/evm/AsmCodeGen.cpp +++ b/libyul/backends/evm/AsmCodeGen.cpp @@ -180,7 +180,7 @@ void CodeGenerator::assemble( langutil::EVMVersion _evmVersion, ExternalIdentifierAccess const& _identifierAccess, bool _useNamedLabelsForFunctions, - bool _optimize + bool _optimizeStackAllocation ) { EthAssemblyAdapter assemblyAdapter(_assembly); @@ -190,7 +190,7 @@ void CodeGenerator::assemble( _analysisInfo, _parsedData, *dialect, - _optimize, + _optimizeStackAllocation, false, _identifierAccess, _useNamedLabelsForFunctions diff --git a/libyul/backends/evm/AsmCodeGen.h b/libyul/backends/evm/AsmCodeGen.h index 2c09e2556..9647014ce 100644 --- a/libyul/backends/evm/AsmCodeGen.h +++ b/libyul/backends/evm/AsmCodeGen.h @@ -82,7 +82,7 @@ public: langutil::EVMVersion _evmVersion, ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(), bool _useNamedLabelsForFunctions = false, - bool _optimize = false + bool _optimizeStackAllocation = false ); }; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 2eb01c47b..2b762e646 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -907,6 +907,7 @@ bool CommandLineInterface::processInput() OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::enabled() : OptimiserSettings::minimal(); settings.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as(); settings.runYulOptimiser = m_args.count(g_strOptimizeYul); + settings.optimizeStackAllocation = settings.runYulOptimiser; m_compiler->setOptimiserSettings(settings); bool successful = m_compiler->compile(); diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_no_object/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_no_object/input.json index 056aee91b..18d3852db 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_no_object/input.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_no_object/input.json @@ -10,7 +10,7 @@ "settings": { "optimizer": { - "details": { "yulDetails": 7 } + "details": { "yul": true, "yulDetails": 7 } } } } diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_no_object/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_no_object/output.json index b71a8a61a..35638adf0 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_no_object/output.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_no_object/output.json @@ -1 +1 @@ -{"errors":[{"component":"general","formattedMessage":"The \"yulDetails\" optimizer setting has to be a JSON object.","message":"The \"yulDetails\" optimizer setting has to be a JSON object.","severity":"error","type":"JSONError"}]} +{"errors":[{"component":"general","formattedMessage":"\"settings.optimizer.details.yulDetails\" must be an object","message":"\"settings.optimizer.details.yulDetails\" must be an object","severity":"error","type":"JSONError"}]} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/input.json new file mode 100644 index 000000000..056aee91b --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": + { + "A": + { + "content": "pragma solidity >=0.0; contract C { function f() public pure {} }" + } + }, + "settings": + { + "optimizer": { + "details": { "yulDetails": 7 } + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json new file mode 100644 index 000000000..c44794cc1 --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json @@ -0,0 +1 @@ +{"errors":[{"component":"general","formattedMessage":"\"Providing yulDetails requires Yul optimizer to be enabled.","message":"\"Providing yulDetails requires Yul optimizer to be enabled.","severity":"error","type":"JSONError"}]} diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index f2c8f9582..f7e04a6f1 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -1010,7 +1010,10 @@ BOOST_AUTO_TEST_CASE(optimizer_settings_details_different) BOOST_CHECK(optimizer["details"]["jumpdestRemover"].asBool() == true); BOOST_CHECK(optimizer["details"]["orderLiterals"].asBool() == false); BOOST_CHECK(optimizer["details"]["peephole"].asBool() == true); + BOOST_CHECK(optimizer["details"]["yul"].asBool() == true); BOOST_CHECK(optimizer["details"]["yulDetails"].isObject()); + BOOST_CHECK(optimizer["details"]["yulDetails"].getMemberNames() == vector{"stackAllocation"}); + BOOST_CHECK(optimizer["details"]["yulDetails"]["stackAllocation"].asBool() == true); BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 8); BOOST_CHECK(optimizer["runs"].asUInt() == 600); }