mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #5959 from ethereum/optimiser2
Introduce global optimiser settings.
This commit is contained in:
@@ -86,6 +86,7 @@ set(sources
|
||||
interface/GasEstimator.h
|
||||
interface/Natspec.cpp
|
||||
interface/Natspec.h
|
||||
interface/OptimiserSettings.h
|
||||
interface/ReadFile.h
|
||||
interface/StandardCompiler.cpp
|
||||
interface/StandardCompiler.h
|
||||
|
||||
@@ -35,16 +35,20 @@ void Compiler::compileContract(
|
||||
bytes const& _metadata
|
||||
)
|
||||
{
|
||||
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns);
|
||||
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimiserSettings);
|
||||
runtimeCompiler.compileContract(_contract, _otherCompilers);
|
||||
m_runtimeContext.appendAuxiliaryData(_metadata);
|
||||
|
||||
// This might modify m_runtimeContext because it can access runtime functions at
|
||||
// creation time.
|
||||
ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize, 1);
|
||||
OptimiserSettings creationSettings{m_optimiserSettings};
|
||||
// The creation code will be executed at most once, so we modify the optimizer
|
||||
// settings accordingly.
|
||||
creationSettings.expectedExecutionsPerDeployment = 1;
|
||||
ContractCompiler creationCompiler(&runtimeCompiler, m_context, creationSettings);
|
||||
m_runtimeSub = creationCompiler.compileConstructor(_contract, _otherCompilers);
|
||||
|
||||
m_context.optimise(m_optimize, m_optimizeRuns);
|
||||
m_context.optimise(m_optimiserSettings);
|
||||
}
|
||||
|
||||
std::shared_ptr<eth::Assembly> Compiler::runtimeAssemblyPtr() const
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/codegen/CompilerContext.h>
|
||||
#include <libsolidity/interface/OptimiserSettings.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
#include <libevmasm/Assembly.h>
|
||||
#include <functional>
|
||||
@@ -34,9 +35,8 @@ namespace solidity {
|
||||
class Compiler
|
||||
{
|
||||
public:
|
||||
explicit Compiler(langutil::EVMVersion _evmVersion = langutil::EVMVersion{}, bool _optimize = false, unsigned _runs = 200):
|
||||
m_optimize(_optimize),
|
||||
m_optimizeRuns(_runs),
|
||||
explicit Compiler(langutil::EVMVersion _evmVersion, OptimiserSettings _optimiserSettings):
|
||||
m_optimiserSettings(std::move(_optimiserSettings)),
|
||||
m_runtimeContext(_evmVersion),
|
||||
m_context(_evmVersion, &m_runtimeContext)
|
||||
{ }
|
||||
@@ -78,8 +78,7 @@ public:
|
||||
eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const;
|
||||
|
||||
private:
|
||||
bool const m_optimize;
|
||||
unsigned const m_optimizeRuns;
|
||||
OptimiserSettings const m_optimiserSettings;
|
||||
CompilerContext m_runtimeContext;
|
||||
size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present.
|
||||
CompilerContext m_context;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/backends/evm/AsmCodeGen.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <libyul/optimiser/Suite.h>
|
||||
#include <libyul/YulString.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
@@ -328,12 +329,19 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
|
||||
void CompilerContext::appendInlineAssembly(
|
||||
string const& _assembly,
|
||||
vector<string> const& _localVariables,
|
||||
set<string> const&,
|
||||
bool _system
|
||||
set<string> const& _externallyUsedFunctions,
|
||||
bool _system,
|
||||
bool _optimise
|
||||
)
|
||||
{
|
||||
int startStackHeight = stackHeight();
|
||||
|
||||
set<yul::YulString> externallyUsedIdentifiers;
|
||||
for (auto const& fun: _externallyUsedFunctions)
|
||||
externallyUsedIdentifiers.insert(yul::YulString(fun));
|
||||
for (auto const& var: _localVariables)
|
||||
externallyUsedIdentifiers.insert(yul::YulString(var));
|
||||
|
||||
yul::ExternalIdentifierAccess identifierAccess;
|
||||
identifierAccess.resolve = [&](
|
||||
yul::Identifier const& _identifier,
|
||||
@@ -380,20 +388,12 @@ void CompilerContext::appendInlineAssembly(
|
||||
#ifdef SOL_OUTPUT_ASM
|
||||
cout << yul::AsmPrinter()(*parserResult) << endl;
|
||||
#endif
|
||||
yul::AsmAnalysisInfo analysisInfo;
|
||||
bool analyzerResult = false;
|
||||
if (parserResult)
|
||||
analyzerResult = yul::AsmAnalyzer(
|
||||
analysisInfo,
|
||||
errorReporter,
|
||||
boost::none,
|
||||
yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
|
||||
identifierAccess.resolve
|
||||
).analyze(*parserResult);
|
||||
if (!parserResult || !errorReporter.errors().empty() || !analyzerResult)
|
||||
|
||||
auto reportError = [&](string const& _context)
|
||||
{
|
||||
string message =
|
||||
"Error parsing/analyzing inline assembly block:\n"
|
||||
"Error parsing/analyzing inline assembly block:\n" +
|
||||
_context + "\n"
|
||||
"------------------ Input: -----------------\n" +
|
||||
_assembly + "\n"
|
||||
"------------------ Errors: ----------------\n";
|
||||
@@ -405,10 +405,47 @@ void CompilerContext::appendInlineAssembly(
|
||||
message += "-------------------------------------------\n";
|
||||
|
||||
solAssert(false, message);
|
||||
};
|
||||
|
||||
yul::AsmAnalysisInfo analysisInfo;
|
||||
bool analyzerResult = false;
|
||||
if (parserResult)
|
||||
analyzerResult = yul::AsmAnalyzer(
|
||||
analysisInfo,
|
||||
errorReporter,
|
||||
boost::none,
|
||||
yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
|
||||
identifierAccess.resolve
|
||||
).analyze(*parserResult);
|
||||
if (!parserResult || !errorReporter.errors().empty() || !analyzerResult)
|
||||
reportError("Invalid assembly generated by code generator.");
|
||||
|
||||
// Several optimizer steps cannot handle externally supplied stack variables,
|
||||
// so we essentially only optimize the ABI functions.
|
||||
if (_optimise && _localVariables.empty())
|
||||
{
|
||||
yul::OptimiserSuite::run(
|
||||
yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
|
||||
*parserResult,
|
||||
analysisInfo,
|
||||
externallyUsedIdentifiers
|
||||
);
|
||||
analysisInfo = yul::AsmAnalysisInfo{};
|
||||
if (!yul::AsmAnalyzer(
|
||||
analysisInfo,
|
||||
errorReporter,
|
||||
boost::none,
|
||||
yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
|
||||
identifierAccess.resolve
|
||||
).analyze(*parserResult))
|
||||
reportError("Optimizer introduced error into inline assembly.");
|
||||
}
|
||||
|
||||
if (!errorReporter.errors().empty())
|
||||
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);
|
||||
yul::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, m_evmVersion, identifierAccess, _system, _optimise);
|
||||
|
||||
// Reset the source location to the one of the node (instead of the CODEGEN source location)
|
||||
updateSourceLocation();
|
||||
@@ -447,6 +484,21 @@ void CompilerContext::updateSourceLocation()
|
||||
m_asm->setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location());
|
||||
}
|
||||
|
||||
eth::Assembly::OptimiserSettings CompilerContext::translateOptimiserSettings(OptimiserSettings const& _settings)
|
||||
{
|
||||
// Constructing it this way so that we notice changes in the fields.
|
||||
eth::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, m_evmVersion, 0};
|
||||
asmSettings.isCreation = true;
|
||||
asmSettings.runJumpdestRemover = _settings.runJumpdestRemover;
|
||||
asmSettings.runPeephole = _settings.runPeephole;
|
||||
asmSettings.runDeduplicate = _settings.runDeduplicate;
|
||||
asmSettings.runCSE = _settings.runCSE;
|
||||
asmSettings.runConstantOptimiser = _settings.runConstantOptimiser;
|
||||
asmSettings.expectedExecutionsPerDeployment = _settings.expectedExecutionsPerDeployment;
|
||||
asmSettings.evmVersion = m_evmVersion;
|
||||
return asmSettings;
|
||||
}
|
||||
|
||||
eth::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabel(
|
||||
Declaration const& _declaration,
|
||||
CompilerContext& _context
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libsolidity/codegen/ABIFunctions.h>
|
||||
|
||||
#include <libsolidity/interface/OptimiserSettings.h>
|
||||
|
||||
#include <libevmasm/Assembly.h>
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
@@ -50,7 +52,7 @@ class Compiler;
|
||||
class CompilerContext
|
||||
{
|
||||
public:
|
||||
explicit CompilerContext(langutil::EVMVersion _evmVersion = langutil::EVMVersion{}, CompilerContext* _runtimeContext = nullptr):
|
||||
explicit CompilerContext(langutil::EVMVersion _evmVersion, CompilerContext* _runtimeContext = nullptr):
|
||||
m_asm(std::make_shared<eth::Assembly>()),
|
||||
m_evmVersion(_evmVersion),
|
||||
m_runtimeContext(_runtimeContext),
|
||||
@@ -214,14 +216,15 @@ public:
|
||||
std::string const& _assembly,
|
||||
std::vector<std::string> const& _localVariables = std::vector<std::string>(),
|
||||
std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>(),
|
||||
bool _system = false
|
||||
bool _system = false,
|
||||
bool _optimise = false
|
||||
);
|
||||
|
||||
/// Appends arbitrary data to the end of the bytecode.
|
||||
void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); }
|
||||
|
||||
/// Run optimisation step.
|
||||
void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, m_evmVersion, true, _runs); }
|
||||
void optimise(OptimiserSettings const& _settings) { m_asm->optimise(translateOptimiserSettings(_settings)); }
|
||||
|
||||
/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
|
||||
CompilerContext* runtimeContext() const { return m_runtimeContext; }
|
||||
@@ -271,6 +274,8 @@ private:
|
||||
/// Updates source location set in the assembly.
|
||||
void updateSourceLocation();
|
||||
|
||||
eth::Assembly::OptimiserSettings translateOptimiserSettings(OptimiserSettings const& _settings);
|
||||
|
||||
/**
|
||||
* Helper class that manages function labels and ensures that referenced functions are
|
||||
* compiled in a specific order.
|
||||
|
||||
@@ -391,7 +391,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
|
||||
sortedIDs.emplace_back(it.first);
|
||||
}
|
||||
std::sort(sortedIDs.begin(), sortedIDs.end());
|
||||
appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimise_runs);
|
||||
appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimiserSettings.expectedExecutionsPerDeployment);
|
||||
}
|
||||
|
||||
m_context << notFound;
|
||||
@@ -484,7 +484,7 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr
|
||||
solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library.");
|
||||
for (VariableDeclaration const* variable: _contract.stateVariables())
|
||||
if (variable->value() && !variable->isConstant())
|
||||
ExpressionCompiler(m_context, m_optimise).appendStateVariableInitialization(*variable);
|
||||
ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableInitialization(*variable);
|
||||
}
|
||||
|
||||
bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration)
|
||||
@@ -497,9 +497,9 @@ bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration)
|
||||
m_continueTags.clear();
|
||||
|
||||
if (_variableDeclaration.isConstant())
|
||||
ExpressionCompiler(m_context, m_optimise).appendConstStateVariableAccessor(_variableDeclaration);
|
||||
ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendConstStateVariableAccessor(_variableDeclaration);
|
||||
else
|
||||
ExpressionCompiler(m_context, m_optimise).appendStateVariableAccessor(_variableDeclaration);
|
||||
ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableAccessor(_variableDeclaration);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -978,7 +978,13 @@ void ContractCompiler::appendMissingFunctions()
|
||||
m_context.appendMissingLowLevelFunctions();
|
||||
auto abiFunctions = m_context.abiFunctions().requestedFunctions();
|
||||
if (!abiFunctions.first.empty())
|
||||
m_context.appendInlineAssembly("{" + move(abiFunctions.first) + "}", {}, abiFunctions.second, true);
|
||||
m_context.appendInlineAssembly(
|
||||
"{" + move(abiFunctions.first) + "}",
|
||||
{},
|
||||
abiFunctions.second,
|
||||
true,
|
||||
m_optimiserSettings.runYulOptimiser
|
||||
);
|
||||
}
|
||||
|
||||
void ContractCompiler::appendModifierOrFunctionCode()
|
||||
@@ -1053,7 +1059,7 @@ void ContractCompiler::appendStackVariableInitialisation(VariableDeclaration con
|
||||
|
||||
void ContractCompiler::compileExpression(Expression const& _expression, TypePointer const& _targetType)
|
||||
{
|
||||
ExpressionCompiler expressionCompiler(m_context, m_optimise);
|
||||
ExpressionCompiler expressionCompiler(m_context, m_optimiserSettings.runOrderLiterals);
|
||||
expressionCompiler.compile(_expression);
|
||||
if (_targetType)
|
||||
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
|
||||
|
||||
@@ -28,8 +28,10 @@
|
||||
#include <functional>
|
||||
#include <ostream>
|
||||
|
||||
namespace dev {
|
||||
namespace solidity {
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
/**
|
||||
* Code generator at the contract level. Can be used to generate code for exactly one contract
|
||||
@@ -38,9 +40,12 @@ namespace solidity {
|
||||
class ContractCompiler: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise, size_t _optimise_runs = 200):
|
||||
m_optimise(_optimise),
|
||||
m_optimise_runs(_optimise_runs),
|
||||
explicit ContractCompiler(
|
||||
ContractCompiler* _runtimeCompiler,
|
||||
CompilerContext& _context,
|
||||
OptimiserSettings _optimiserSettings
|
||||
):
|
||||
m_optimiserSettings(std::move(_optimiserSettings)),
|
||||
m_runtimeCompiler(_runtimeCompiler),
|
||||
m_context(_context)
|
||||
{
|
||||
@@ -129,8 +134,7 @@ private:
|
||||
/// Sets the stack height for the visited loop.
|
||||
void storeStackHeight(ASTNode const* _node);
|
||||
|
||||
bool const m_optimise;
|
||||
size_t const m_optimise_runs = 200;
|
||||
OptimiserSettings const m_optimiserSettings;
|
||||
/// Pointer to the runtime compiler in case this is a creation compiler.
|
||||
ContractCompiler* m_runtimeCompiler = nullptr;
|
||||
CompilerContext& m_context;
|
||||
|
||||
@@ -449,7 +449,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
|
||||
{
|
||||
return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::RationalNumber;
|
||||
};
|
||||
bool swap = m_optimize && TokenTraits::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression);
|
||||
bool swap = m_optimiseOrderLiterals && TokenTraits::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression);
|
||||
if (swap)
|
||||
{
|
||||
leftExpression.accept(*this);
|
||||
|
||||
@@ -55,11 +55,8 @@ class ArrayType;
|
||||
class ExpressionCompiler: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
/// Appends code for a State Variable accessor function
|
||||
static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false);
|
||||
|
||||
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
|
||||
m_optimize(_optimize), m_context(_compilerContext) {}
|
||||
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimiseOrderLiterals):
|
||||
m_optimiseOrderLiterals(_optimiseOrderLiterals), m_context(_compilerContext) {}
|
||||
|
||||
/// Compile the given @a _expression and leave its value on the stack.
|
||||
void compile(Expression const& _expression);
|
||||
@@ -127,7 +124,7 @@ private:
|
||||
/// @returns the CompilerUtils object containing the current context.
|
||||
CompilerUtils utils();
|
||||
|
||||
bool m_optimize;
|
||||
bool m_optimiseOrderLiterals;
|
||||
CompilerContext& m_context;
|
||||
std::unique_ptr<LValue> m_currentLValue;
|
||||
|
||||
|
||||
@@ -108,11 +108,17 @@ void CompilerStack::setLibraries(std::map<std::string, h160> const& _libraries)
|
||||
}
|
||||
|
||||
void CompilerStack::setOptimiserSettings(bool _optimize, unsigned _runs)
|
||||
{
|
||||
OptimiserSettings settings = _optimize ? OptimiserSettings::enabled() : OptimiserSettings::minimal();
|
||||
settings.expectedExecutionsPerDeployment = _runs;
|
||||
setOptimiserSettings(std::move(settings));
|
||||
}
|
||||
|
||||
void CompilerStack::setOptimiserSettings(OptimiserSettings _settings)
|
||||
{
|
||||
if (m_stackState >= ParsingSuccessful)
|
||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set optimiser settings before parsing."));
|
||||
m_optimize = _optimize;
|
||||
m_optimizeRuns = _runs;
|
||||
m_optimiserSettings = std::move(_settings);
|
||||
}
|
||||
|
||||
void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources)
|
||||
@@ -146,8 +152,7 @@ void CompilerStack::reset(bool _keepSources)
|
||||
m_unhandledSMTLib2Queries.clear();
|
||||
m_libraries.clear();
|
||||
m_evmVersion = langutil::EVMVersion();
|
||||
m_optimize = false;
|
||||
m_optimizeRuns = 200;
|
||||
m_optimiserSettings = OptimiserSettings::minimal();
|
||||
m_globalContext.reset();
|
||||
m_scopes.clear();
|
||||
m_sourceOrder.clear();
|
||||
@@ -840,7 +845,7 @@ void CompilerStack::compileContract(
|
||||
|
||||
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
|
||||
|
||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_optimize, m_optimizeRuns);
|
||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_optimiserSettings);
|
||||
compiledContract.compiler = compiler;
|
||||
|
||||
string metadata = createMetadata(compiledContract);
|
||||
@@ -953,8 +958,35 @@ string CompilerStack::createMetadata(Contract const& _contract) const
|
||||
meta["sources"][s.first]["urls"].append("bzzr://" + toHex(s.second.swarmHash().asBytes()));
|
||||
}
|
||||
}
|
||||
meta["settings"]["optimizer"]["enabled"] = m_optimize;
|
||||
meta["settings"]["optimizer"]["runs"] = m_optimizeRuns;
|
||||
|
||||
static_assert(sizeof(m_optimiserSettings.expectedExecutionsPerDeployment) <= sizeof(Json::LargestUInt), "Invalid word size.");
|
||||
solAssert(static_cast<Json::LargestUInt>(m_optimiserSettings.expectedExecutionsPerDeployment) < std::numeric_limits<Json::LargestUInt>::max(), "");
|
||||
meta["settings"]["optimizer"]["runs"] = Json::Value(Json::LargestUInt(m_optimiserSettings.expectedExecutionsPerDeployment));
|
||||
|
||||
/// Backwards compatibility: If set to one of the default settings, do not provide details.
|
||||
OptimiserSettings settingsWithoutRuns = m_optimiserSettings;
|
||||
// reset to default
|
||||
settingsWithoutRuns.expectedExecutionsPerDeployment = OptimiserSettings::minimal().expectedExecutionsPerDeployment;
|
||||
if (settingsWithoutRuns == OptimiserSettings::minimal())
|
||||
meta["settings"]["optimizer"]["enabled"] = false;
|
||||
else if (settingsWithoutRuns == OptimiserSettings::enabled())
|
||||
meta["settings"]["optimizer"]["enabled"] = true;
|
||||
else
|
||||
{
|
||||
Json::Value details{Json::objectValue};
|
||||
|
||||
details["orderLiterals"] = m_optimiserSettings.runOrderLiterals;
|
||||
details["jumpdestRemover"] = m_optimiserSettings.runJumpdestRemover;
|
||||
details["peephole"] = m_optimiserSettings.runPeephole;
|
||||
details["deduplicate"] = m_optimiserSettings.runDeduplicate;
|
||||
details["cse"] = m_optimiserSettings.runCSE;
|
||||
details["constantOptimizer"] = m_optimiserSettings.runConstantOptimiser;
|
||||
details["yul"] = m_optimiserSettings.runYulOptimiser;
|
||||
details["yulDetails"] = Json::objectValue;
|
||||
|
||||
meta["settings"]["optimizer"]["details"] = std::move(details);
|
||||
}
|
||||
|
||||
meta["settings"]["evmVersion"] = m_evmVersion.name();
|
||||
meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
|
||||
_contract.contract->annotation().canonicalName;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
#include <libsolidity/interface/OptimiserSettings.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
@@ -128,6 +129,10 @@ public:
|
||||
/// Must be set before parsing.
|
||||
void setOptimiserSettings(bool _optimize, unsigned _runs = 200);
|
||||
|
||||
/// Changes the optimiser settings.
|
||||
/// Must be set before parsing.
|
||||
void setOptimiserSettings(OptimiserSettings _settings);
|
||||
|
||||
/// Set the EVM version used before running compile.
|
||||
/// When called without an argument it will revert to the default version.
|
||||
/// Must be set before parsing.
|
||||
@@ -342,8 +347,7 @@ private:
|
||||
) const;
|
||||
|
||||
ReadCallback::Callback m_readFile;
|
||||
bool m_optimize = false;
|
||||
unsigned m_optimizeRuns = 200;
|
||||
OptimiserSettings m_optimiserSettings;
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
std::set<std::string> m_requestedContractNames;
|
||||
std::map<std::string, h160> m_libraries;
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @author Alex Beregszaszi
|
||||
* @date 2017
|
||||
* Helper class for optimiser settings.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
struct OptimiserSettings
|
||||
{
|
||||
/// No optimisations at all - not recommended.
|
||||
static OptimiserSettings none()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
/// Minimal optimisations: Peephole and jumpdest remover
|
||||
static OptimiserSettings minimal()
|
||||
{
|
||||
OptimiserSettings s = none();
|
||||
s.runJumpdestRemover = true;
|
||||
s.runPeephole = true;
|
||||
return s;
|
||||
}
|
||||
/// Standard optimisations.
|
||||
static OptimiserSettings enabled()
|
||||
{
|
||||
OptimiserSettings s;
|
||||
s.runOrderLiterals = true;
|
||||
s.runJumpdestRemover = true;
|
||||
s.runPeephole = true;
|
||||
s.runDeduplicate = true;
|
||||
s.runCSE = true;
|
||||
s.runConstantOptimiser = true;
|
||||
// The only disabled one
|
||||
s.runYulOptimiser = false;
|
||||
s.expectedExecutionsPerDeployment = 200;
|
||||
return s;
|
||||
}
|
||||
/// Standard optimisations plus yul optimiser.
|
||||
static OptimiserSettings full()
|
||||
{
|
||||
OptimiserSettings s = enabled();
|
||||
s.runYulOptimiser = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
bool operator==(OptimiserSettings const& _other) const
|
||||
{
|
||||
return
|
||||
runOrderLiterals == _other.runOrderLiterals &&
|
||||
runJumpdestRemover == _other.runJumpdestRemover &&
|
||||
runPeephole == _other.runPeephole &&
|
||||
runDeduplicate == _other.runDeduplicate &&
|
||||
runCSE == _other.runCSE &&
|
||||
runConstantOptimiser == _other.runConstantOptimiser &&
|
||||
runYulOptimiser == _other.runYulOptimiser &&
|
||||
expectedExecutionsPerDeployment == _other.expectedExecutionsPerDeployment;
|
||||
}
|
||||
|
||||
/// Move literals to the right of commutative binary operators during code generation.
|
||||
/// This helps exploiting associativity.
|
||||
bool runOrderLiterals = false;
|
||||
/// Non-referenced jump destination remover.
|
||||
bool runJumpdestRemover = false;
|
||||
/// Peephole optimizer
|
||||
bool runPeephole = false;
|
||||
/// Assembly block deduplicator
|
||||
bool runDeduplicate = false;
|
||||
/// Common subexpression eliminator based on assembly items.
|
||||
bool runCSE = false;
|
||||
/// Constant optimizer, which tries to find better representations that satisfy the given
|
||||
/// size/cost-trade-off.
|
||||
bool runConstantOptimiser = 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,
|
||||
/// i.e. use a small value to optimise for size and a large value to optimise for runtime gas usage.
|
||||
size_t expectedExecutionsPerDeployment = 200;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -292,10 +292,27 @@ boost::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
|
||||
|
||||
boost::optional<Json::Value> checkOptimizerKeys(Json::Value const& _input)
|
||||
{
|
||||
static set<string> keys{"enabled", "runs"};
|
||||
static set<string> keys{"details", "enabled", "runs"};
|
||||
return checkKeys(_input, keys, "settings.optimizer");
|
||||
}
|
||||
|
||||
boost::optional<Json::Value> checkOptimizerDetailsKeys(Json::Value const& _input)
|
||||
{
|
||||
static set<string> keys{"peephole", "jumpdestRemover", "orderLiterals", "deduplicate", "cse", "constantOptimizer", "yul", "yulDetails"};
|
||||
return checkKeys(_input, keys, "settings.optimizer.details");
|
||||
}
|
||||
|
||||
boost::optional<Json::Value> checkOptimizerDetail(Json::Value const& _details, std::string const& _name, bool& _setting)
|
||||
{
|
||||
if (_details.isMember(_name))
|
||||
{
|
||||
if (!_details[_name].isBool())
|
||||
return formatFatalError("JSONError", "\"settings.optimizer.details." + _name + "\" must be Boolean");
|
||||
_setting = _details[_name].asBool();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
boost::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
|
||||
{
|
||||
if (_input.isObject() && _input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool())
|
||||
@@ -351,6 +368,61 @@ boost::optional<Json::Value> checkOutputSelection(Json::Value const& _outputSele
|
||||
|
||||
}
|
||||
|
||||
boost::optional<Json::Value> StandardCompiler::parseOptimizerSettings(Json::Value const& _jsonInput)
|
||||
{
|
||||
if (auto result = checkOptimizerKeys(_jsonInput))
|
||||
return *result;
|
||||
|
||||
OptimiserSettings settings = OptimiserSettings::none();
|
||||
|
||||
if (_jsonInput.isMember("enabled"))
|
||||
{
|
||||
if (!_jsonInput["enabled"].isBool())
|
||||
return formatFatalError("JSONError", "The \"enabled\" setting must be a Boolean.");
|
||||
|
||||
settings = _jsonInput["enabled"].asBool() ? OptimiserSettings::enabled() : OptimiserSettings::minimal();
|
||||
}
|
||||
|
||||
if (_jsonInput.isMember("runs"))
|
||||
{
|
||||
if (!_jsonInput["runs"].isUInt())
|
||||
return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number.");
|
||||
settings.expectedExecutionsPerDeployment = _jsonInput["runs"].asUInt();
|
||||
}
|
||||
|
||||
if (_jsonInput.isMember("details"))
|
||||
{
|
||||
Json::Value const& details = _jsonInput["details"];
|
||||
if (auto result = checkOptimizerDetailsKeys(details))
|
||||
return *result;
|
||||
|
||||
if (auto error = checkOptimizerDetail(details, "peephole", settings.runPeephole))
|
||||
return *error;
|
||||
if (auto error = checkOptimizerDetail(details, "jumpdestRemover", settings.runJumpdestRemover))
|
||||
return *error;
|
||||
if (auto error = checkOptimizerDetail(details, "orderLiterals", settings.runOrderLiterals))
|
||||
return *error;
|
||||
if (auto error = checkOptimizerDetail(details, "deduplicate", settings.runDeduplicate))
|
||||
return *error;
|
||||
if (auto error = checkOptimizerDetail(details, "cse", settings.runCSE))
|
||||
return *error;
|
||||
if (auto error = checkOptimizerDetail(details, "constantOptimizer", settings.runConstantOptimiser))
|
||||
return *error;
|
||||
if (auto error = checkOptimizerDetail(details, "yul", settings.runYulOptimiser))
|
||||
return *error;
|
||||
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.");
|
||||
}
|
||||
}
|
||||
m_compilerStack.setOptimiserSettings(std::move(settings));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
{
|
||||
m_compilerStack.reset(false);
|
||||
@@ -512,29 +584,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
|
||||
m_compilerStack.setRemappings(remappings);
|
||||
|
||||
if (settings.isMember("optimizer"))
|
||||
{
|
||||
Json::Value optimizerSettings = settings["optimizer"];
|
||||
|
||||
if (auto result = checkOptimizerKeys(optimizerSettings))
|
||||
if (auto result = parseOptimizerSettings(settings["optimizer"]))
|
||||
return *result;
|
||||
|
||||
if (optimizerSettings.isMember("enabled"))
|
||||
{
|
||||
if (!optimizerSettings["enabled"].isBool())
|
||||
return formatFatalError("JSONError", "The \"enabled\" setting must be a boolean.");
|
||||
|
||||
bool const optimize = optimizerSettings["enabled"].asBool();
|
||||
unsigned optimizeRuns = 200;
|
||||
if (optimizerSettings.isMember("runs"))
|
||||
{
|
||||
if (!optimizerSettings["runs"].isUInt())
|
||||
return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number.");
|
||||
optimizeRuns = optimizerSettings["runs"].asUInt();
|
||||
}
|
||||
m_compilerStack.setOptimiserSettings(optimize, optimizeRuns);
|
||||
}
|
||||
}
|
||||
|
||||
map<string, h160> libraries;
|
||||
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
|
||||
if (!jsonLibraries.isObject())
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#include <libsolidity/interface/CompilerStack.h>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
||||
@@ -53,6 +55,10 @@ public:
|
||||
std::string compile(std::string const& _input) noexcept;
|
||||
|
||||
private:
|
||||
/// Validaes and applies the optimizer settings.
|
||||
/// On error returns the json-formatted error message.
|
||||
boost::optional<Json::Value> parseOptimizerSettings(Json::Value const& _settings);
|
||||
|
||||
Json::Value compileInternal(Json::Value const& _input);
|
||||
|
||||
CompilerStack m_compilerStack;
|
||||
|
||||
Reference in New Issue
Block a user