Use stack optimizations.

This commit is contained in:
chriseth 2019-02-26 19:55:13 +01:00
parent ca34335d07
commit 6d1ed93247
16 changed files with 70 additions and 20 deletions

View File

@ -4,6 +4,7 @@ Language Features:
Compiler Features: Compiler Features:
* Yul Optimizer: Enable stack allocation optimization by default if yul optimizer is active (disable in yulDetails).
Bugfixes: Bugfixes:

View File

@ -216,8 +216,12 @@ Input Description
// It can only be activated through the details here. // It can only be activated through the details here.
// This feature is still considered experimental. // This feature is still considered experimental.
"yul": false, "yul": false,
// Future tuning options, currently unused. // Tuning options for the Yul optimizer.
"yulDetails": {} "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 "evmVersion": "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople or petersburg

View File

@ -331,7 +331,7 @@ void CompilerContext::appendInlineAssembly(
vector<string> const& _localVariables, vector<string> const& _localVariables,
set<string> const& _externallyUsedFunctions, set<string> const& _externallyUsedFunctions,
bool _system, bool _system,
bool _optimise OptimiserSettings const& _optimiserSettings
) )
{ {
int startStackHeight = stackHeight(); int startStackHeight = stackHeight();
@ -422,7 +422,7 @@ void CompilerContext::appendInlineAssembly(
// Several optimizer steps cannot handle externally supplied stack variables, // Several optimizer steps cannot handle externally supplied stack variables,
// so we essentially only optimize the ABI functions. // so we essentially only optimize the ABI functions.
if (_optimise && _localVariables.empty()) if (_optimiserSettings.runYulOptimiser && _localVariables.empty())
{ {
yul::OptimiserSuite::run( yul::OptimiserSuite::run(
yul::EVMDialect::strictAssemblyForEVM(m_evmVersion), yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
@ -445,7 +445,15 @@ void CompilerContext::appendInlineAssembly(
reportError("Failed to analyze inline assembly block."); reportError("Failed to analyze inline assembly block.");
solAssert(errorReporter.errors().empty(), "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) // Reset the source location to the one of the node (instead of the CODEGEN source location)
updateSourceLocation(); updateSourceLocation();

View File

@ -217,7 +217,7 @@ public:
std::vector<std::string> const& _localVariables = std::vector<std::string>(), std::vector<std::string> const& _localVariables = std::vector<std::string>(),
std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>(), std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>(),
bool _system = false, bool _system = false,
bool _optimise = false OptimiserSettings const& _optimiserSettings = OptimiserSettings::none()
); );
/// Appends arbitrary data to the end of the bytecode. /// Appends arbitrary data to the end of the bytecode.

View File

@ -720,7 +720,9 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
*_inlineAssembly.annotation().analysisInfo, *_inlineAssembly.annotation().analysisInfo,
*m_context.assemblyPtr(), *m_context.assemblyPtr(),
m_context.evmVersion(), m_context.evmVersion(),
identifierAccess identifierAccess,
false,
m_optimiserSettings.optimizeStackAllocation
); );
m_context.setStackOffset(startStackHeight); m_context.setStackOffset(startStackHeight);
return false; return false;
@ -983,7 +985,7 @@ void ContractCompiler::appendMissingFunctions()
{}, {},
abiFunctions.second, abiFunctions.second,
true, true,
m_optimiserSettings.runYulOptimiser m_optimiserSettings
); );
} }

View File

@ -993,7 +993,11 @@ string CompilerStack::createMetadata(Contract const& _contract) const
details["cse"] = m_optimiserSettings.runCSE; details["cse"] = m_optimiserSettings.runCSE;
details["constantOptimizer"] = m_optimiserSettings.runConstantOptimiser; details["constantOptimizer"] = m_optimiserSettings.runConstantOptimiser;
details["yul"] = m_optimiserSettings.runYulOptimiser; 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); meta["settings"]["optimizer"]["details"] = std::move(details);
} }

View File

@ -54,15 +54,17 @@ struct OptimiserSettings
s.runDeduplicate = true; s.runDeduplicate = true;
s.runCSE = true; s.runCSE = true;
s.runConstantOptimiser = true; s.runConstantOptimiser = true;
// The only disabled one // The only disabled ones
s.optimizeStackAllocation = false;
s.runYulOptimiser = false; s.runYulOptimiser = false;
s.expectedExecutionsPerDeployment = 200; s.expectedExecutionsPerDeployment = 200;
return s; return s;
} }
/// Standard optimisations plus yul optimiser. /// Standard optimisations plus yul and stack optimiser.
static OptimiserSettings full() static OptimiserSettings full()
{ {
OptimiserSettings s = enabled(); OptimiserSettings s = enabled();
s.optimizeStackAllocation = true;
s.runYulOptimiser = true; s.runYulOptimiser = true;
return s; return s;
} }
@ -76,6 +78,7 @@ struct OptimiserSettings
runDeduplicate == _other.runDeduplicate && runDeduplicate == _other.runDeduplicate &&
runCSE == _other.runCSE && runCSE == _other.runCSE &&
runConstantOptimiser == _other.runConstantOptimiser && runConstantOptimiser == _other.runConstantOptimiser &&
optimizeStackAllocation == _other.optimizeStackAllocation &&
runYulOptimiser == _other.runYulOptimiser && runYulOptimiser == _other.runYulOptimiser &&
expectedExecutionsPerDeployment == _other.expectedExecutionsPerDeployment; expectedExecutionsPerDeployment == _other.expectedExecutionsPerDeployment;
} }
@ -94,6 +97,8 @@ struct OptimiserSettings
/// Constant optimizer, which tries to find better representations that satisfy the given /// Constant optimizer, which tries to find better representations that satisfy the given
/// size/cost-trade-off. /// size/cost-trade-off.
bool runConstantOptimiser = false; 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. /// Yul optimiser with default settings. Will only run on certain parts of the code for now.
bool runYulOptimiser = false; bool runYulOptimiser = false;
/// This specifies an estimate on how often each opcode in this assembly will be executed, /// This specifies an estimate on how often each opcode in this assembly will be executed,

View File

@ -399,12 +399,17 @@ boost::variant<OptimiserSettings, Json::Value> parseOptimizerSettings(Json::Valu
return *error; return *error;
if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser)) if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser))
return *error; return *error;
if (settings.runYulOptimiser)
settings.optimizeStackAllocation = true;
if (details.isMember("yulDetails")) if (details.isMember("yulDetails"))
{ {
if (!_jsonInput["yulDetails"].isObject()) if (!settings.runYulOptimiser)
return formatFatalError("JSONError", "The \"yulDetails\" optimizer setting has to be a JSON object."); return formatFatalError("JSONError", "\"Providing yulDetails requires Yul optimizer to be enabled.");
if (!_jsonInput["yulDetails"].getMemberNames().empty())
return formatFatalError("JSONError", "The \"yulDetails\" optimizer setting cannot have any settings yet."); 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); return std::move(settings);

View File

@ -180,7 +180,7 @@ void CodeGenerator::assemble(
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
ExternalIdentifierAccess const& _identifierAccess, ExternalIdentifierAccess const& _identifierAccess,
bool _useNamedLabelsForFunctions, bool _useNamedLabelsForFunctions,
bool _optimize bool _optimizeStackAllocation
) )
{ {
EthAssemblyAdapter assemblyAdapter(_assembly); EthAssemblyAdapter assemblyAdapter(_assembly);
@ -190,7 +190,7 @@ void CodeGenerator::assemble(
_analysisInfo, _analysisInfo,
_parsedData, _parsedData,
*dialect, *dialect,
_optimize, _optimizeStackAllocation,
false, false,
_identifierAccess, _identifierAccess,
_useNamedLabelsForFunctions _useNamedLabelsForFunctions

View File

@ -82,7 +82,7 @@ public:
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(), ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(),
bool _useNamedLabelsForFunctions = false, bool _useNamedLabelsForFunctions = false,
bool _optimize = false bool _optimizeStackAllocation = false
); );
}; };

View File

@ -907,6 +907,7 @@ bool CommandLineInterface::processInput()
OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::enabled() : OptimiserSettings::minimal(); OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::enabled() : OptimiserSettings::minimal();
settings.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as<unsigned>(); settings.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as<unsigned>();
settings.runYulOptimiser = m_args.count(g_strOptimizeYul); settings.runYulOptimiser = m_args.count(g_strOptimizeYul);
settings.optimizeStackAllocation = settings.runYulOptimiser;
m_compiler->setOptimiserSettings(settings); m_compiler->setOptimiserSettings(settings);
bool successful = m_compiler->compile(); bool successful = m_compiler->compile();

View File

@ -10,7 +10,7 @@
"settings": "settings":
{ {
"optimizer": { "optimizer": {
"details": { "yulDetails": 7 } "details": { "yul": true, "yulDetails": 7 }
} }
} }
} }

View File

@ -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"}]}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources":
{
"A":
{
"content": "pragma solidity >=0.0; contract C { function f() public pure {} }"
}
},
"settings":
{
"optimizer": {
"details": { "yulDetails": 7 }
}
}
}

View File

@ -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"}]}

View File

@ -1010,7 +1010,10 @@ BOOST_AUTO_TEST_CASE(optimizer_settings_details_different)
BOOST_CHECK(optimizer["details"]["jumpdestRemover"].asBool() == true); BOOST_CHECK(optimizer["details"]["jumpdestRemover"].asBool() == true);
BOOST_CHECK(optimizer["details"]["orderLiterals"].asBool() == false); BOOST_CHECK(optimizer["details"]["orderLiterals"].asBool() == false);
BOOST_CHECK(optimizer["details"]["peephole"].asBool() == true); 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"].isObject());
BOOST_CHECK(optimizer["details"]["yulDetails"].getMemberNames() == vector<string>{"stackAllocation"});
BOOST_CHECK(optimizer["details"]["yulDetails"]["stackAllocation"].asBool() == true);
BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 8); BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 8);
BOOST_CHECK(optimizer["runs"].asUInt() == 600); BOOST_CHECK(optimizer["runs"].asUInt() == 600);
} }