Merge pull request #5959 from ethereum/optimiser2

Introduce global optimiser settings.
This commit is contained in:
chriseth 2019-03-04 12:54:59 +01:00 committed by GitHub
commit 2e0ea16a0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 905 additions and 219 deletions

View File

@ -66,10 +66,23 @@ explanatory purposes.
{ {
// Required for Solidity: Sorted list of remappings // Required for Solidity: Sorted list of remappings
remappings: [ ":g/dir" ], remappings: [ ":g/dir" ],
// Optional: Optimizer settings (enabled defaults to false) // Optional: Optimizer settings. The fields "enabled" and "runs" are deprecated
// and are only given for backwards-compatibility.
optimizer: { optimizer: {
enabled: true, enabled: true,
runs: 500 runs: 500,
details: {
// peephole defaults to "true"
peephole: true,
// jumpdestRemover defaults to "true"
jumpdestRemover: true,
orderLiterals: false,
deduplicate: false,
cse: false,
constantOptimizer: false,
yul: false,
yulDetails: {}
}
}, },
// Required for Solidity: File and name of the contract or library this // Required for Solidity: File and name of the contract or library this
// metadata is created for. // metadata is created for.

View File

@ -191,7 +191,30 @@ Input Description
"enabled": true, "enabled": true,
// Optimize for how many times you intend to run the code. // Optimize for how many times you intend to run the code.
// Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage. // Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage.
"runs": 200 "runs": 200,
// Switch optimizer components on or off in detail.
// The "enabled" switch above provides two defaults which can be
// tweaked here. If "details" is given, "enabled" can be omitted.
"details": {
// The peephole optimizer is always on if no details are given, use details to switch it off.
"peephole": true,
// The unused jumpdest remover is always on if no details are given, use details to switch it off.
"jumpdestRemover": true,
// Sometimes re-orders literals in commutative operations.
"orderLiterals": false,
// Removes duplicate code blocks
"deduplicate": false,
// Common subexpression elimination, this is the most complicated step but
// can also provide the largest gain.
"cse": false,
// Optimize representation of literal numbers and strings in code.
"constantOptimizer": false,
// The new Yul optimizer. Mostly operates on the code of ABIEncoderV2.
// It can only be activated through the details here.
"yul": false,
// Future tuning options, currently unused.
"yulDetails": {}
}
}, },
"evmVersion": "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople "evmVersion": "byzantium", // Version of the EVM to compile for. Affects type checking and code generation. Can be homestead, tangerineWhistle, spuriousDragon, byzantium or constantinople
// Metadata settings (optional) // Metadata settings (optional)

View File

@ -113,7 +113,8 @@ public:
size_t expectedExecutionsPerDeployment = 200; size_t expectedExecutionsPerDeployment = 200;
}; };
/// Execute optimisation passes as defined by @a _settings and return the optimised assembly. /// Modify and return the current assembly such that creation and execution gas usage
/// is optimised according to the settings in @a _settings.
Assembly& optimise(OptimiserSettings const& _settings); Assembly& optimise(OptimiserSettings const& _settings);
/// Modify (if @a _enable is set) and return the current assembly such that creation and /// Modify (if @a _enable is set) and return the current assembly such that creation and
@ -121,7 +122,7 @@ public:
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed, /// @a _runs specifes 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. /// i.e. use a small value to optimise for size and a large value to optimise for runtime.
/// If @a _enable is not set, will perform some simple peephole optimizations. /// If @a _enable is not set, will perform some simple peephole optimizations.
Assembly& optimise(bool _enable, langutil::EVMVersion _evmVersion, bool _isCreation = true, size_t _runs = 200); Assembly& optimise(bool _enable, langutil::EVMVersion _evmVersion, bool _isCreation, size_t _runs);
/// Create a text representation of the assembly. /// Create a text representation of the assembly.
std::string assemblyString( std::string assemblyString(

View File

@ -36,7 +36,7 @@ bytes dev::lll::compileLLL(string const& _src, langutil::EVMVersion _evmVersion,
cs.populateStandard(); cs.populateStandard();
auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs);
if (_opt) if (_opt)
assembly = assembly.optimise(true, _evmVersion); assembly = assembly.optimise(true, _evmVersion, true, 200);
bytes ret = assembly.assemble().bytecode; bytes ret = assembly.assemble().bytecode;
for (auto i: cs.treesToKill) for (auto i: cs.treesToKill)
killBigints(i); killBigints(i);
@ -74,7 +74,7 @@ std::string dev::lll::compileLLLToAsm(std::string const& _src, langutil::EVMVers
cs.populateStandard(); cs.populateStandard();
auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs); auto assembly = CodeFragment::compile(_src, cs, _readFile).assembly(cs);
if (_opt) if (_opt)
assembly = assembly.optimise(true, _evmVersion); assembly = assembly.optimise(true, _evmVersion, true, 200);
string ret = assembly.assemblyString(); string ret = assembly.assemblyString();
for (auto i: cs.treesToKill) for (auto i: cs.treesToKill)
killBigints(i); killBigints(i);

View File

@ -86,6 +86,7 @@ set(sources
interface/GasEstimator.h interface/GasEstimator.h
interface/Natspec.cpp interface/Natspec.cpp
interface/Natspec.h interface/Natspec.h
interface/OptimiserSettings.h
interface/ReadFile.h interface/ReadFile.h
interface/StandardCompiler.cpp interface/StandardCompiler.cpp
interface/StandardCompiler.h interface/StandardCompiler.h

View File

@ -35,16 +35,20 @@ void Compiler::compileContract(
bytes const& _metadata bytes const& _metadata
) )
{ {
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize, m_optimizeRuns); ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimiserSettings);
runtimeCompiler.compileContract(_contract, _otherCompilers); runtimeCompiler.compileContract(_contract, _otherCompilers);
m_runtimeContext.appendAuxiliaryData(_metadata); m_runtimeContext.appendAuxiliaryData(_metadata);
// This might modify m_runtimeContext because it can access runtime functions at // This might modify m_runtimeContext because it can access runtime functions at
// creation time. // 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_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 std::shared_ptr<eth::Assembly> Compiler::runtimeAssemblyPtr() const

View File

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <libsolidity/codegen/CompilerContext.h> #include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/interface/OptimiserSettings.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <functional> #include <functional>
@ -34,9 +35,8 @@ namespace solidity {
class Compiler class Compiler
{ {
public: public:
explicit Compiler(langutil::EVMVersion _evmVersion = langutil::EVMVersion{}, bool _optimize = false, unsigned _runs = 200): explicit Compiler(langutil::EVMVersion _evmVersion, OptimiserSettings _optimiserSettings):
m_optimize(_optimize), m_optimiserSettings(std::move(_optimiserSettings)),
m_optimizeRuns(_runs),
m_runtimeContext(_evmVersion), m_runtimeContext(_evmVersion),
m_context(_evmVersion, &m_runtimeContext) m_context(_evmVersion, &m_runtimeContext)
{ } { }
@ -78,8 +78,7 @@ public:
eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const; eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const;
private: private:
bool const m_optimize; OptimiserSettings const m_optimiserSettings;
unsigned const m_optimizeRuns;
CompilerContext m_runtimeContext; CompilerContext m_runtimeContext;
size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present. size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present.
CompilerContext m_context; CompilerContext m_context;

View File

@ -32,6 +32,7 @@
#include <libyul/AsmAnalysisInfo.h> #include <libyul/AsmAnalysisInfo.h>
#include <libyul/backends/evm/AsmCodeGen.h> #include <libyul/backends/evm/AsmCodeGen.h>
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
#include <libyul/optimiser/Suite.h>
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
@ -328,12 +329,19 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node)
void CompilerContext::appendInlineAssembly( void CompilerContext::appendInlineAssembly(
string const& _assembly, string const& _assembly,
vector<string> const& _localVariables, vector<string> const& _localVariables,
set<string> const&, set<string> const& _externallyUsedFunctions,
bool _system bool _system,
bool _optimise
) )
{ {
int startStackHeight = stackHeight(); 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; yul::ExternalIdentifierAccess identifierAccess;
identifierAccess.resolve = [&]( identifierAccess.resolve = [&](
yul::Identifier const& _identifier, yul::Identifier const& _identifier,
@ -380,20 +388,12 @@ void CompilerContext::appendInlineAssembly(
#ifdef SOL_OUTPUT_ASM #ifdef SOL_OUTPUT_ASM
cout << yul::AsmPrinter()(*parserResult) << endl; cout << yul::AsmPrinter()(*parserResult) << endl;
#endif #endif
yul::AsmAnalysisInfo analysisInfo;
bool analyzerResult = false; auto reportError = [&](string const& _context)
if (parserResult)
analyzerResult = yul::AsmAnalyzer(
analysisInfo,
errorReporter,
boost::none,
yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
identifierAccess.resolve
).analyze(*parserResult);
if (!parserResult || !errorReporter.errors().empty() || !analyzerResult)
{ {
string message = string message =
"Error parsing/analyzing inline assembly block:\n" "Error parsing/analyzing inline assembly block:\n" +
_context + "\n"
"------------------ Input: -----------------\n" + "------------------ Input: -----------------\n" +
_assembly + "\n" _assembly + "\n"
"------------------ Errors: ----------------\n"; "------------------ Errors: ----------------\n";
@ -405,10 +405,47 @@ void CompilerContext::appendInlineAssembly(
message += "-------------------------------------------\n"; message += "-------------------------------------------\n";
solAssert(false, message); 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."); 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) // Reset the source location to the one of the node (instead of the CODEGEN source location)
updateSourceLocation(); updateSourceLocation();
@ -447,6 +484,21 @@ void CompilerContext::updateSourceLocation()
m_asm->setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location()); 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( eth::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabel(
Declaration const& _declaration, Declaration const& _declaration,
CompilerContext& _context CompilerContext& _context

View File

@ -27,6 +27,8 @@
#include <libsolidity/ast/Types.h> #include <libsolidity/ast/Types.h>
#include <libsolidity/codegen/ABIFunctions.h> #include <libsolidity/codegen/ABIFunctions.h>
#include <libsolidity/interface/OptimiserSettings.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
@ -50,7 +52,7 @@ class Compiler;
class CompilerContext class CompilerContext
{ {
public: 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_asm(std::make_shared<eth::Assembly>()),
m_evmVersion(_evmVersion), m_evmVersion(_evmVersion),
m_runtimeContext(_runtimeContext), m_runtimeContext(_runtimeContext),
@ -214,14 +216,15 @@ public:
std::string const& _assembly, std::string const& _assembly,
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
); );
/// Appends arbitrary data to the end of the bytecode. /// Appends arbitrary data to the end of the bytecode.
void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); } void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); }
/// Run optimisation step. /// 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. /// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
CompilerContext* runtimeContext() const { return m_runtimeContext; } CompilerContext* runtimeContext() const { return m_runtimeContext; }
@ -271,6 +274,8 @@ private:
/// Updates source location set in the assembly. /// Updates source location set in the assembly.
void updateSourceLocation(); void updateSourceLocation();
eth::Assembly::OptimiserSettings translateOptimiserSettings(OptimiserSettings const& _settings);
/** /**
* Helper class that manages function labels and ensures that referenced functions are * Helper class that manages function labels and ensures that referenced functions are
* compiled in a specific order. * compiled in a specific order.

View File

@ -391,7 +391,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
sortedIDs.emplace_back(it.first); sortedIDs.emplace_back(it.first);
} }
std::sort(sortedIDs.begin(), sortedIDs.end()); std::sort(sortedIDs.begin(), sortedIDs.end());
appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimise_runs); appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimiserSettings.expectedExecutionsPerDeployment);
} }
m_context << notFound; m_context << notFound;
@ -484,7 +484,7 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr
solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library."); solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library.");
for (VariableDeclaration const* variable: _contract.stateVariables()) for (VariableDeclaration const* variable: _contract.stateVariables())
if (variable->value() && !variable->isConstant()) 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) bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration)
@ -497,9 +497,9 @@ bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration)
m_continueTags.clear(); m_continueTags.clear();
if (_variableDeclaration.isConstant()) if (_variableDeclaration.isConstant())
ExpressionCompiler(m_context, m_optimise).appendConstStateVariableAccessor(_variableDeclaration); ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendConstStateVariableAccessor(_variableDeclaration);
else else
ExpressionCompiler(m_context, m_optimise).appendStateVariableAccessor(_variableDeclaration); ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableAccessor(_variableDeclaration);
return false; return false;
} }
@ -978,7 +978,13 @@ void ContractCompiler::appendMissingFunctions()
m_context.appendMissingLowLevelFunctions(); m_context.appendMissingLowLevelFunctions();
auto abiFunctions = m_context.abiFunctions().requestedFunctions(); auto abiFunctions = m_context.abiFunctions().requestedFunctions();
if (!abiFunctions.first.empty()) 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() void ContractCompiler::appendModifierOrFunctionCode()
@ -1053,7 +1059,7 @@ void ContractCompiler::appendStackVariableInitialisation(VariableDeclaration con
void ContractCompiler::compileExpression(Expression const& _expression, TypePointer const& _targetType) 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); expressionCompiler.compile(_expression);
if (_targetType) if (_targetType)
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType); CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);

View File

@ -28,8 +28,10 @@
#include <functional> #include <functional>
#include <ostream> #include <ostream>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
/** /**
* Code generator at the contract level. Can be used to generate code for exactly one contract * 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 class ContractCompiler: private ASTConstVisitor
{ {
public: public:
explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise, size_t _optimise_runs = 200): explicit ContractCompiler(
m_optimise(_optimise), ContractCompiler* _runtimeCompiler,
m_optimise_runs(_optimise_runs), CompilerContext& _context,
OptimiserSettings _optimiserSettings
):
m_optimiserSettings(std::move(_optimiserSettings)),
m_runtimeCompiler(_runtimeCompiler), m_runtimeCompiler(_runtimeCompiler),
m_context(_context) m_context(_context)
{ {
@ -129,8 +134,7 @@ private:
/// Sets the stack height for the visited loop. /// Sets the stack height for the visited loop.
void storeStackHeight(ASTNode const* _node); void storeStackHeight(ASTNode const* _node);
bool const m_optimise; OptimiserSettings const m_optimiserSettings;
size_t const m_optimise_runs = 200;
/// Pointer to the runtime compiler in case this is a creation compiler. /// Pointer to the runtime compiler in case this is a creation compiler.
ContractCompiler* m_runtimeCompiler = nullptr; ContractCompiler* m_runtimeCompiler = nullptr;
CompilerContext& m_context; CompilerContext& m_context;

View File

@ -449,7 +449,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
{ {
return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::RationalNumber; 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) if (swap)
{ {
leftExpression.accept(*this); leftExpression.accept(*this);

View File

@ -55,11 +55,8 @@ class ArrayType;
class ExpressionCompiler: private ASTConstVisitor class ExpressionCompiler: private ASTConstVisitor
{ {
public: public:
/// Appends code for a State Variable accessor function explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimiseOrderLiterals):
static void appendStateVariableAccessor(CompilerContext& _context, VariableDeclaration const& _varDecl, bool _optimize = false); m_optimiseOrderLiterals(_optimiseOrderLiterals), m_context(_compilerContext) {}
explicit ExpressionCompiler(CompilerContext& _compilerContext, bool _optimize = false):
m_optimize(_optimize), m_context(_compilerContext) {}
/// Compile the given @a _expression and leave its value on the stack. /// Compile the given @a _expression and leave its value on the stack.
void compile(Expression const& _expression); void compile(Expression const& _expression);
@ -127,7 +124,7 @@ private:
/// @returns the CompilerUtils object containing the current context. /// @returns the CompilerUtils object containing the current context.
CompilerUtils utils(); CompilerUtils utils();
bool m_optimize; bool m_optimiseOrderLiterals;
CompilerContext& m_context; CompilerContext& m_context;
std::unique_ptr<LValue> m_currentLValue; std::unique_ptr<LValue> m_currentLValue;

View File

@ -108,11 +108,17 @@ void CompilerStack::setLibraries(std::map<std::string, h160> const& _libraries)
} }
void CompilerStack::setOptimiserSettings(bool _optimize, unsigned _runs) 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) if (m_stackState >= ParsingSuccessful)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set optimiser settings before parsing.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must set optimiser settings before parsing."));
m_optimize = _optimize; m_optimiserSettings = std::move(_settings);
m_optimizeRuns = _runs;
} }
void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources) void CompilerStack::useMetadataLiteralSources(bool _metadataLiteralSources)
@ -146,8 +152,7 @@ void CompilerStack::reset(bool _keepSources)
m_unhandledSMTLib2Queries.clear(); m_unhandledSMTLib2Queries.clear();
m_libraries.clear(); m_libraries.clear();
m_evmVersion = langutil::EVMVersion(); m_evmVersion = langutil::EVMVersion();
m_optimize = false; m_optimiserSettings = OptimiserSettings::minimal();
m_optimizeRuns = 200;
m_globalContext.reset(); m_globalContext.reset();
m_scopes.clear(); m_scopes.clear();
m_sourceOrder.clear(); m_sourceOrder.clear();
@ -840,7 +845,7 @@ void CompilerStack::compileContract(
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); 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; compiledContract.compiler = compiler;
string metadata = createMetadata(compiledContract); 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["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"]["evmVersion"] = m_evmVersion.name();
meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] =
_contract.contract->annotation().canonicalName; _contract.contract->annotation().canonicalName;

View File

@ -24,6 +24,7 @@
#pragma once #pragma once
#include <libsolidity/interface/ReadFile.h> #include <libsolidity/interface/ReadFile.h>
#include <libsolidity/interface/OptimiserSettings.h>
#include <liblangutil/ErrorReporter.h> #include <liblangutil/ErrorReporter.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
@ -128,6 +129,10 @@ public:
/// Must be set before parsing. /// Must be set before parsing.
void setOptimiserSettings(bool _optimize, unsigned _runs = 200); 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. /// Set the EVM version used before running compile.
/// When called without an argument it will revert to the default version. /// When called without an argument it will revert to the default version.
/// Must be set before parsing. /// Must be set before parsing.
@ -342,8 +347,7 @@ private:
) const; ) const;
ReadCallback::Callback m_readFile; ReadCallback::Callback m_readFile;
bool m_optimize = false; OptimiserSettings m_optimiserSettings;
unsigned m_optimizeRuns = 200;
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
std::set<std::string> m_requestedContractNames; std::set<std::string> m_requestedContractNames;
std::map<std::string, h160> m_libraries; std::map<std::string, h160> m_libraries;

View File

@ -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;
};
}
}

View File

@ -292,10 +292,27 @@ boost::optional<Json::Value> checkSettingsKeys(Json::Value const& _input)
boost::optional<Json::Value> checkOptimizerKeys(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"); 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) boost::optional<Json::Value> checkMetadataKeys(Json::Value const& _input)
{ {
if (_input.isObject() && _input.isMember("useLiteralContent") && !_input["useLiteralContent"].isBool()) 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) Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
{ {
m_compilerStack.reset(false); m_compilerStack.reset(false);
@ -512,29 +584,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.setRemappings(remappings); m_compilerStack.setRemappings(remappings);
if (settings.isMember("optimizer")) if (settings.isMember("optimizer"))
{ if (auto result = parseOptimizerSettings(settings["optimizer"]))
Json::Value optimizerSettings = settings["optimizer"];
if (auto result = checkOptimizerKeys(optimizerSettings))
return *result; 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; map<string, h160> libraries;
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue)); Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
if (!jsonLibraries.isObject()) if (!jsonLibraries.isObject())

View File

@ -24,6 +24,8 @@
#include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/CompilerStack.h>
#include <boost/optional.hpp>
namespace dev namespace dev
{ {
@ -53,6 +55,10 @@ public:
std::string compile(std::string const& _input) noexcept; std::string compile(std::string const& _input) noexcept;
private: 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); Json::Value compileInternal(Json::Value const& _input);
CompilerStack m_compilerStack; CompilerStack m_compilerStack;

View File

@ -132,6 +132,7 @@ static string const g_strNatspecUser = "userdoc";
static string const g_strOpcodes = "opcodes"; static string const g_strOpcodes = "opcodes";
static string const g_strOptimize = "optimize"; static string const g_strOptimize = "optimize";
static string const g_strOptimizeRuns = "optimize-runs"; static string const g_strOptimizeRuns = "optimize-runs";
static string const g_strOptimizeYul = "optimize-yul";
static string const g_strOutputDir = "output-dir"; static string const g_strOutputDir = "output-dir";
static string const g_strOverwrite = "overwrite"; static string const g_strOverwrite = "overwrite";
static string const g_strSignatureHashes = "hashes"; static string const g_strSignatureHashes = "hashes";
@ -615,6 +616,7 @@ Allowed options)",
"Set for how many contract runs to optimize." "Set for how many contract runs to optimize."
"Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage." "Lower values will optimize more for initial deployment cost, higher values will optimize more for high-frequency usage."
) )
(g_strOptimizeYul.c_str(), "Enable Yul optimizer in Solidity, mostly for ABIEncoderV2.")
(g_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.") (g_argPrettyJson.c_str(), "Output JSON in pretty format. Currently it only works with the combined JSON output.")
( (
g_argLibraries.c_str(), g_argLibraries.c_str(),
@ -847,7 +849,7 @@ bool CommandLineInterface::processInput()
using Machine = yul::AssemblyStack::Machine; using Machine = yul::AssemblyStack::Machine;
Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly);
Machine targetMachine = Machine::EVM; Machine targetMachine = Machine::EVM;
bool optimize = m_args.count(g_argOptimize); bool optimize = m_args.count(g_argOptimize) || m_args.count(g_strOptimizeYul);
if (m_args.count(g_argMachine)) if (m_args.count(g_argMachine))
{ {
string machine = m_args[g_argMachine].as<string>(); string machine = m_args[g_argMachine].as<string>();
@ -901,9 +903,11 @@ bool CommandLineInterface::processInput()
m_compiler->setLibraries(m_libraries); m_compiler->setLibraries(m_libraries);
m_compiler->setEVMVersion(m_evmVersion); m_compiler->setEVMVersion(m_evmVersion);
// TODO: Perhaps we should not compile unless requested // TODO: Perhaps we should not compile unless requested
bool optimize = m_args.count(g_argOptimize) > 0;
unsigned runs = m_args[g_argOptimizeRuns].as<unsigned>(); OptimiserSettings settings = m_args.count(g_argOptimize) ? OptimiserSettings::enabled() : OptimiserSettings::minimal();
m_compiler->setOptimiserSettings(optimize, runs); settings.expectedExecutionsPerDeployment = m_args[g_argOptimizeRuns].as<unsigned>();
settings.runYulOptimiser = m_args.count(g_strOptimizeYul);
m_compiler->setOptimiserSettings(settings);
bool successful = m_compiler->compile(); bool successful = m_compiler->compile();

View File

@ -0,0 +1 @@
--gas

View File

@ -0,0 +1,3 @@
gas_test_abiv2/input.sol:2:1: Warning: Experimental features are turned on. Do not use experimental features on live deployments.
pragma experimental ABIEncoderV2;
^-------------------------------^

View File

@ -0,0 +1,15 @@
pragma solidity >=0.0;
pragma experimental ABIEncoderV2;
contract C {
uint public a;
uint[] public b;
function f1(uint) public pure returns (uint) { }
function f2(uint[] memory, string[] memory, uint16, address) public returns (uint[] memory, uint16[] memory) {}
function f3(uint16[] memory, string[] memory, uint16, address) public returns (uint[] memory, uint16[] memory) {}
function f4(uint32[] memory, string[12] memory, bytes[2][] memory, address) public returns (uint[] memory, uint16[] memory) {}
function f5(address[] memory, string[] memory, bytes memory, address) public returns (uint[] memory, uint16[] memory) {}
function f6(uint[30] memory, string[] memory, uint16, address) public returns (uint16[200] memory, uint16[] memory) {}
function f7(uint[31] memory, string[20] memory, C, address) public returns (bytes[] memory, uint16[] memory) {}
function f8(uint[32] memory, string[] memory, uint32, address) public returns (uint[] memory, uint16[] memory) {}
}

View File

@ -0,0 +1,16 @@
======= gas_test_abiv2/input.sol:C =======
Gas estimation:
construction:
1154 + 1109000 = 1110154
external:
a(): 535
b(uint256): 1129
f1(uint256): 591
f2(uint256[],string[],uint16,address): infinite
f3(uint16[],string[],uint16,address): infinite
f4(uint32[],string[12],bytes[2][],address): infinite
f5(address[],string[],bytes,address): infinite
f6(uint256[30],string[],uint16,address): infinite
f7(uint256[31],string[20],address,address): infinite
f8(uint256[32],string[],uint32,address): infinite

View File

@ -0,0 +1 @@
--gas --optimize --optimize-yul

View File

@ -0,0 +1,3 @@
gas_test_abiv2_optimize_yul/input.sol:2:1: Warning: Experimental features are turned on. Do not use experimental features on live deployments.
pragma experimental ABIEncoderV2;
^-------------------------------^

View File

@ -0,0 +1,15 @@
pragma solidity >=0.0;
pragma experimental ABIEncoderV2;
contract C {
uint public a;
uint[] public b;
function f1(uint) public pure returns (uint) { }
function f2(uint[] memory, string[] memory, uint16, address) public returns (uint[] memory, uint16[] memory) {}
function f3(uint16[] memory, string[] memory, uint16, address) public returns (uint[] memory, uint16[] memory) {}
function f4(uint32[] memory, string[12] memory, bytes[2][] memory, address) public returns (uint[] memory, uint16[] memory) {}
function f5(address[] memory, string[] memory, bytes memory, address) public returns (uint[] memory, uint16[] memory) {}
function f6(uint[30] memory, string[] memory, uint16, address) public returns (uint16[200] memory, uint16[] memory) {}
function f7(uint[31] memory, string[20] memory, C, address) public returns (bytes[] memory, uint16[] memory) {}
function f8(uint[32] memory, string[] memory, uint32, address) public returns (uint[] memory, uint16[] memory) {}
}

View File

@ -0,0 +1,16 @@
======= gas_test_abiv2_optimize_yul/input.sol:C =======
Gas estimation:
construction:
676 + 641600 = 642276
external:
a(): 434
b(uint256): 892
f1(uint256): 356
f2(uint256[],string[],uint16,address): infinite
f3(uint16[],string[],uint16,address): infinite
f4(uint32[],string[12],bytes[2][],address): infinite
f5(address[],string[],bytes,address): infinite
f6(uint256[30],string[],uint16,address): infinite
f7(uint256[31],string[20],address,address): infinite
f8(uint256[32],string[],uint32,address): infinite

View File

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

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"\"settings.optimizer.details.peephole\" must be Boolean","message":"\"settings.optimizer.details.peephole\" must be Boolean","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": { "notThere": true }
}
}
}

View File

@ -0,0 +1 @@
{"errors":[{"component":"general","formattedMessage":"Unknown key \"notThere\"","message":"Unknown key \"notThere\"","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":"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"}]}

View File

@ -1053,7 +1053,7 @@ BOOST_AUTO_TEST_CASE(jumpdest_removal_subassemblies)
main.append(t1.toSubAssemblyTag(subId)); main.append(t1.toSubAssemblyTag(subId));
main.append(u256(8)); main.append(u256(8));
main.optimise(true, dev::test::Options::get().evmVersion()); main.optimise(true, dev::test::Options::get().evmVersion(), false, 200);
AssemblyItems expectationMain{ AssemblyItems expectationMain{
AssemblyItem(PushSubSize, 0), AssemblyItem(PushSubSize, 0),

View File

@ -84,7 +84,10 @@ eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{ {
Compiler compiler(dev::test::Options::get().evmVersion()); Compiler compiler(
dev::test::Options::get().evmVersion(),
dev::test::Options::get().optimize ? OptimiserSettings::enabled() : OptimiserSettings::minimal()
);
compiler.compileContract(*contract, map<ContractDefinition const*, shared_ptr<Compiler const>>{}, bytes()); compiler.compileContract(*contract, map<ContractDefinition const*, shared_ptr<Compiler const>>{}, bytes());
return compiler.runtimeAssemblyItems(); return compiler.runtimeAssemblyItems();
@ -165,20 +168,35 @@ BOOST_AUTO_TEST_CASE(location_test)
auto codegenCharStream = make_shared<CharStream>("", "--CODEGEN--"); auto codegenCharStream = make_shared<CharStream>("", "--CODEGEN--");
vector<SourceLocation> locations = vector<SourceLocation> locations;
vector<SourceLocation>(4, SourceLocation{2, 82, sourceCode}) + if (dev::test::Options::get().optimize)
vector<SourceLocation>(1, SourceLocation{8, 17, codegenCharStream}) + locations =
vector<SourceLocation>(3, SourceLocation{5, 7, codegenCharStream}) + vector<SourceLocation>(4, SourceLocation{2, 82, sourceCode}) +
vector<SourceLocation>(1, SourceLocation{30, 31, codegenCharStream}) + vector<SourceLocation>(1, SourceLocation{8, 17, codegenCharStream}) +
vector<SourceLocation>(1, SourceLocation{27, 28, codegenCharStream}) + vector<SourceLocation>(3, SourceLocation{5, 7, codegenCharStream}) +
vector<SourceLocation>(1, SourceLocation{20, 32, codegenCharStream}) + vector<SourceLocation>(1, SourceLocation{30, 31, codegenCharStream}) +
vector<SourceLocation>(1, SourceLocation{5, 7, codegenCharStream}) + vector<SourceLocation>(1, SourceLocation{27, 28, codegenCharStream}) +
vector<SourceLocation>(hasShifts ? 19 : 20, SourceLocation{2, 82, sourceCode}) + vector<SourceLocation>(1, SourceLocation{20, 32, codegenCharStream}) +
vector<SourceLocation>(24, SourceLocation{20, 79, sourceCode}) + vector<SourceLocation>(1, SourceLocation{5, 7, codegenCharStream}) +
vector<SourceLocation>(1, SourceLocation{49, 58, sourceCode}) + vector<SourceLocation>(19, SourceLocation{2, 82, sourceCode}) +
vector<SourceLocation>(1, SourceLocation{72, 74, sourceCode}) + vector<SourceLocation>(21, SourceLocation{20, 79, sourceCode}) +
vector<SourceLocation>(2, SourceLocation{65, 74, sourceCode}) + vector<SourceLocation>(1, SourceLocation{72, 74, sourceCode}) +
vector<SourceLocation>(2, SourceLocation{20, 79, sourceCode}); vector<SourceLocation>(2, SourceLocation{20, 79, sourceCode});
else
locations =
vector<SourceLocation>(4, SourceLocation{2, 82, sourceCode}) +
vector<SourceLocation>(1, SourceLocation{8, 17, codegenCharStream}) +
vector<SourceLocation>(3, SourceLocation{5, 7, codegenCharStream}) +
vector<SourceLocation>(1, SourceLocation{30, 31, codegenCharStream}) +
vector<SourceLocation>(1, SourceLocation{27, 28, codegenCharStream}) +
vector<SourceLocation>(1, SourceLocation{20, 32, codegenCharStream}) +
vector<SourceLocation>(1, SourceLocation{5, 7, codegenCharStream}) +
vector<SourceLocation>(hasShifts ? 19 : 20, SourceLocation{2, 82, sourceCode}) +
vector<SourceLocation>(24, SourceLocation{20, 79, sourceCode}) +
vector<SourceLocation>(1, SourceLocation{49, 58, sourceCode}) +
vector<SourceLocation>(1, SourceLocation{72, 74, sourceCode}) +
vector<SourceLocation>(2, SourceLocation{65, 74, sourceCode}) +
vector<SourceLocation>(2, SourceLocation{20, 79, sourceCode});
checkAssemblyLocations(items, locations); checkAssemblyLocations(items, locations);
} }

View File

@ -153,7 +153,7 @@ bytes compileFirstExpression(
parametersSize-- parametersSize--
); );
ExpressionCompiler(context).compile(*extractor.expression()); ExpressionCompiler(context, dev::test::Options::get().optimize).compile(*extractor.expression());
for (vector<string> const& function: _functions) for (vector<string> const& function: _functions)
context << context.functionEntryLabel(dynamic_cast<FunctionDefinition const&>( context << context.functionEntryLabel(dynamic_cast<FunctionDefinition const&>(
@ -282,12 +282,26 @@ BOOST_AUTO_TEST_CASE(comparison)
)"; )";
bytes code = compileFirstExpression(sourceCode); bytes code = compileFirstExpression(sourceCode);
bytes expectation({uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), bytes expectation;
uint8_t(Instruction::PUSH2), 0x11, 0xaa, if (dev::test::Options::get().optimize)
uint8_t(Instruction::PUSH2), 0x10, 0xaa, expectation = {
uint8_t(Instruction::LT), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), uint8_t(Instruction::PUSH2), 0x11, 0xaa,
uint8_t(Instruction::EQ), uint8_t(Instruction::PUSH2), 0x10, 0xaa,
uint8_t(Instruction::ISZERO)}); uint8_t(Instruction::LT), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO),
uint8_t(Instruction::PUSH1), 0x1,
uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO),
uint8_t(Instruction::EQ),
uint8_t(Instruction::ISZERO)
};
else
expectation = {
uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO),
uint8_t(Instruction::PUSH2), 0x11, 0xaa,
uint8_t(Instruction::PUSH2), 0x10, 0xaa,
uint8_t(Instruction::LT), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO),
uint8_t(Instruction::EQ),
uint8_t(Instruction::ISZERO)
};
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
} }
@ -300,23 +314,25 @@ BOOST_AUTO_TEST_CASE(short_circuiting)
)"; )";
bytes code = compileFirstExpression(sourceCode); bytes code = compileFirstExpression(sourceCode);
bytes expectation({uint8_t(Instruction::PUSH1), 0x12, // 8 + 10 bytes expectation{
uint8_t(Instruction::PUSH1), 0x4, uint8_t(Instruction::PUSH1), 0x12, // 8 + 10
uint8_t(Instruction::GT), uint8_t(Instruction::PUSH1), 0x4,
uint8_t(Instruction::ISZERO), // after this we have 4 <= 8 + 10 uint8_t(Instruction::GT),
uint8_t(Instruction::DUP1), uint8_t(Instruction::ISZERO), // after this we have 4 <= 8 + 10
uint8_t(Instruction::PUSH1), 0x11, uint8_t(Instruction::DUP1),
uint8_t(Instruction::JUMPI), // short-circuit if it is true uint8_t(Instruction::PUSH1), 0x11,
uint8_t(Instruction::POP), uint8_t(Instruction::JUMPI), // short-circuit if it is true
uint8_t(Instruction::PUSH1), 0x2, uint8_t(Instruction::POP),
uint8_t(Instruction::PUSH1), 0x9, uint8_t(Instruction::PUSH1), 0x2,
uint8_t(Instruction::EQ), uint8_t(Instruction::PUSH1), 0x9,
uint8_t(Instruction::ISZERO), // after this we have 9 != 2 uint8_t(Instruction::EQ),
uint8_t(Instruction::JUMPDEST), uint8_t(Instruction::ISZERO), // after this we have 9 != 2
uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), uint8_t(Instruction::JUMPDEST),
uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO),
uint8_t(Instruction::EQ), uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO),
uint8_t(Instruction::ISZERO)}); uint8_t(Instruction::EQ),
uint8_t(Instruction::ISZERO)
};
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
} }
@ -328,37 +344,76 @@ BOOST_AUTO_TEST_CASE(arithmetic)
} }
)"; )";
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
bytes expectation({uint8_t(Instruction::PUSH1), 0x1,
uint8_t(Instruction::PUSH1), 0x2, bytes expectation;
uint8_t(Instruction::PUSH1), 0x3, if (dev::test::Options::get().optimize)
uint8_t(Instruction::PUSH1), 0x4, expectation = {
uint8_t(Instruction::PUSH1), 0x5, uint8_t(Instruction::PUSH1), 0x2,
uint8_t(Instruction::PUSH1), 0x6, uint8_t(Instruction::PUSH1), 0x3,
uint8_t(Instruction::PUSH1), 0x7, uint8_t(Instruction::PUSH1), 0x5,
uint8_t(Instruction::PUSH1), 0x8, uint8_t(Instruction::DUP4),
uint8_t(Instruction::DUP9), uint8_t(Instruction::PUSH1), 0x8,
uint8_t(Instruction::XOR), uint8_t(Instruction::XOR),
uint8_t(Instruction::AND), uint8_t(Instruction::PUSH1), 0x7,
uint8_t(Instruction::OR), uint8_t(Instruction::AND),
uint8_t(Instruction::SUB), uint8_t(Instruction::PUSH1), 0x6,
uint8_t(Instruction::ADD), uint8_t(Instruction::OR),
uint8_t(Instruction::DUP2), uint8_t(Instruction::SUB),
uint8_t(Instruction::ISZERO), uint8_t(Instruction::PUSH1), 0x4,
uint8_t(Instruction::ISZERO), uint8_t(Instruction::ADD),
uint8_t(Instruction::PUSH1), 0x1d, uint8_t(Instruction::DUP2),
uint8_t(Instruction::JUMPI), uint8_t(Instruction::ISZERO),
uint8_t(Instruction::INVALID), uint8_t(Instruction::ISZERO),
uint8_t(Instruction::JUMPDEST), uint8_t(Instruction::PUSH1), 0x1b,
uint8_t(Instruction::MOD), uint8_t(Instruction::JUMPI),
uint8_t(Instruction::DUP2), uint8_t(Instruction::INVALID),
uint8_t(Instruction::ISZERO), uint8_t(Instruction::JUMPDEST),
uint8_t(Instruction::ISZERO), uint8_t(Instruction::MOD),
uint8_t(Instruction::PUSH1), 0x26, uint8_t(Instruction::DUP2),
uint8_t(Instruction::JUMPI), uint8_t(Instruction::ISZERO),
uint8_t(Instruction::INVALID), uint8_t(Instruction::ISZERO),
uint8_t(Instruction::JUMPDEST), uint8_t(Instruction::PUSH1), 0x24,
uint8_t(Instruction::DIV), uint8_t(Instruction::JUMPI),
uint8_t(Instruction::MUL)}); uint8_t(Instruction::INVALID),
uint8_t(Instruction::JUMPDEST),
uint8_t(Instruction::DIV),
uint8_t(Instruction::PUSH1), 0x1,
uint8_t(Instruction::MUL)
};
else
expectation = {
uint8_t(Instruction::PUSH1), 0x1,
uint8_t(Instruction::PUSH1), 0x2,
uint8_t(Instruction::PUSH1), 0x3,
uint8_t(Instruction::PUSH1), 0x4,
uint8_t(Instruction::PUSH1), 0x5,
uint8_t(Instruction::PUSH1), 0x6,
uint8_t(Instruction::PUSH1), 0x7,
uint8_t(Instruction::PUSH1), 0x8,
uint8_t(Instruction::DUP9),
uint8_t(Instruction::XOR),
uint8_t(Instruction::AND),
uint8_t(Instruction::OR),
uint8_t(Instruction::SUB),
uint8_t(Instruction::ADD),
uint8_t(Instruction::DUP2),
uint8_t(Instruction::ISZERO),
uint8_t(Instruction::ISZERO),
uint8_t(Instruction::PUSH1), 0x1d,
uint8_t(Instruction::JUMPI),
uint8_t(Instruction::INVALID),
uint8_t(Instruction::JUMPDEST),
uint8_t(Instruction::MOD),
uint8_t(Instruction::DUP2),
uint8_t(Instruction::ISZERO),
uint8_t(Instruction::ISZERO),
uint8_t(Instruction::PUSH1), 0x26,
uint8_t(Instruction::JUMPI),
uint8_t(Instruction::INVALID),
uint8_t(Instruction::JUMPDEST),
uint8_t(Instruction::DIV),
uint8_t(Instruction::MUL)
};
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
} }
@ -371,13 +426,27 @@ BOOST_AUTO_TEST_CASE(unary_operators)
)"; )";
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
bytes expectation({uint8_t(Instruction::PUSH1), 0x2, bytes expectation;
uint8_t(Instruction::DUP2), if (dev::test::Options::get().optimize)
uint8_t(Instruction::PUSH1), 0x0, expectation = {
uint8_t(Instruction::SUB), uint8_t(Instruction::DUP1),
uint8_t(Instruction::NOT), uint8_t(Instruction::PUSH1), 0x0,
uint8_t(Instruction::EQ), uint8_t(Instruction::SUB),
uint8_t(Instruction::ISZERO)}); uint8_t(Instruction::NOT),
uint8_t(Instruction::PUSH1), 0x2,
uint8_t(Instruction::EQ),
uint8_t(Instruction::ISZERO)
};
else
expectation = {
uint8_t(Instruction::PUSH1), 0x2,
uint8_t(Instruction::DUP2),
uint8_t(Instruction::PUSH1), 0x0,
uint8_t(Instruction::SUB),
uint8_t(Instruction::NOT),
uint8_t(Instruction::EQ),
uint8_t(Instruction::ISZERO)
};
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
} }
@ -391,48 +460,50 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec)
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}}); bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}});
// Stack: a, x // Stack: a, x
bytes expectation({uint8_t(Instruction::DUP2), bytes expectation{
uint8_t(Instruction::DUP1), uint8_t(Instruction::DUP2),
uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::DUP1),
uint8_t(Instruction::ADD), uint8_t(Instruction::PUSH1), 0x1,
// Stack here: a x a (a+1) uint8_t(Instruction::ADD),
uint8_t(Instruction::SWAP3), // Stack here: a x a (a+1)
uint8_t(Instruction::POP), // first ++ uint8_t(Instruction::SWAP3),
// Stack here: (a+1) x a uint8_t(Instruction::POP), // first ++
uint8_t(Instruction::DUP3), // Stack here: (a+1) x a
uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::DUP3),
uint8_t(Instruction::ADD), uint8_t(Instruction::PUSH1), 0x1,
// Stack here: (a+1) x a (a+2) uint8_t(Instruction::ADD),
uint8_t(Instruction::SWAP3), // Stack here: (a+1) x a (a+2)
uint8_t(Instruction::POP), uint8_t(Instruction::SWAP3),
// Stack here: (a+2) x a uint8_t(Instruction::POP),
uint8_t(Instruction::DUP3), // second ++ // Stack here: (a+2) x a
uint8_t(Instruction::XOR), uint8_t(Instruction::DUP3), // second ++
// Stack here: (a+2) x a^(a+2) uint8_t(Instruction::XOR),
uint8_t(Instruction::DUP3), // Stack here: (a+2) x a^(a+2)
uint8_t(Instruction::DUP1), uint8_t(Instruction::DUP3),
uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::DUP1),
uint8_t(Instruction::SWAP1), uint8_t(Instruction::PUSH1), 0x1,
uint8_t(Instruction::SUB), uint8_t(Instruction::SWAP1),
// Stack here: (a+2) x a^(a+2) (a+2) (a+1) uint8_t(Instruction::SUB),
uint8_t(Instruction::SWAP4), // Stack here: (a+2) x a^(a+2) (a+2) (a+1)
uint8_t(Instruction::POP), // first -- uint8_t(Instruction::SWAP4),
uint8_t(Instruction::XOR), uint8_t(Instruction::POP), // first --
// Stack here: (a+1) x a^(a+2)^(a+2) uint8_t(Instruction::XOR),
uint8_t(Instruction::DUP3), // Stack here: (a+1) x a^(a+2)^(a+2)
uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::DUP3),
uint8_t(Instruction::SWAP1), uint8_t(Instruction::PUSH1), 0x1,
uint8_t(Instruction::SUB), uint8_t(Instruction::SWAP1),
// Stack here: (a+1) x a^(a+2)^(a+2) a uint8_t(Instruction::SUB),
uint8_t(Instruction::SWAP3), // Stack here: (a+1) x a^(a+2)^(a+2) a
uint8_t(Instruction::POP), // second ++ uint8_t(Instruction::SWAP3),
// Stack here: a x a^(a+2)^(a+2) uint8_t(Instruction::POP), // second ++
uint8_t(Instruction::DUP3), // will change // Stack here: a x a^(a+2)^(a+2)
uint8_t(Instruction::XOR), uint8_t(Instruction::DUP3), // will change
uint8_t(Instruction::SWAP1), uint8_t(Instruction::XOR),
uint8_t(Instruction::POP), uint8_t(Instruction::SWAP1),
uint8_t(Instruction::DUP1)}); uint8_t(Instruction::POP),
// Stack here: a x a^(a+2)^(a+2)^a uint8_t(Instruction::DUP1)
};
// Stack here: a x a^(a+2)^(a+2)^a
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
} }
@ -446,16 +517,31 @@ BOOST_AUTO_TEST_CASE(assignment)
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}}); bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}});
// Stack: a, b // Stack: a, b
bytes expectation({uint8_t(Instruction::PUSH1), 0x2, bytes expectation;
uint8_t(Instruction::DUP2), if (dev::test::Options::get().optimize)
uint8_t(Instruction::DUP4), expectation = {
uint8_t(Instruction::ADD), uint8_t(Instruction::DUP1),
// Stack here: a b 2 a+b uint8_t(Instruction::DUP3),
uint8_t(Instruction::SWAP3), uint8_t(Instruction::ADD),
uint8_t(Instruction::POP), uint8_t(Instruction::SWAP2),
uint8_t(Instruction::DUP3), uint8_t(Instruction::POP),
// Stack here: a+b b 2 a+b uint8_t(Instruction::DUP2),
uint8_t(Instruction::MUL)}); uint8_t(Instruction::PUSH1), 0x2,
uint8_t(Instruction::MUL)
};
else
expectation = {
uint8_t(Instruction::PUSH1), 0x2,
uint8_t(Instruction::DUP2),
uint8_t(Instruction::DUP4),
uint8_t(Instruction::ADD),
// Stack here: a b 2 a+b
uint8_t(Instruction::SWAP3),
uint8_t(Instruction::POP),
uint8_t(Instruction::DUP3),
// Stack here: a+b b 2 a+b
uint8_t(Instruction::MUL)
};
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
} }

View File

@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE(optimizer_enabled_not_boolean)
} }
)"; )";
Json::Value result = compile(input); Json::Value result = compile(input);
BOOST_CHECK(containsError(result, "JSONError", "The \"enabled\" setting must be a boolean.")); BOOST_CHECK(containsError(result, "JSONError", "The \"enabled\" setting must be a Boolean."));
} }
BOOST_AUTO_TEST_CASE(optimizer_runs_not_a_number) BOOST_AUTO_TEST_CASE(optimizer_runs_not_a_number)
@ -859,6 +859,159 @@ BOOST_AUTO_TEST_CASE(evm_version)
BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested."); BOOST_CHECK(result["errors"][0]["message"].asString() == "Invalid EVM version requested.");
} }
BOOST_AUTO_TEST_CASE(optimizer_settings_default_disabled)
{
char const* input = R"(
{
"language": "Solidity",
"settings": {
"outputSelection": {
"fileA": { "A": [ "metadata" ] }
}
},
"sources": {
"fileA": {
"content": "contract A { }"
}
}
}
)";
Json::Value result = compile(input);
BOOST_CHECK(containsAtMostWarnings(result));
Json::Value contract = getContractResult(result, "fileA", "A");
BOOST_CHECK(contract.isObject());
BOOST_CHECK(contract["metadata"].isString());
Json::Value metadata;
BOOST_CHECK(jsonParseStrict(contract["metadata"].asString(), metadata));
Json::Value const& optimizer = metadata["settings"]["optimizer"];
BOOST_CHECK(optimizer.isMember("enabled"));
BOOST_CHECK(optimizer["enabled"].asBool() == false);
BOOST_CHECK(!optimizer.isMember("details"));
BOOST_CHECK(optimizer["runs"].asUInt() == 200);
}
BOOST_AUTO_TEST_CASE(optimizer_settings_default_enabled)
{
char const* input = R"(
{
"language": "Solidity",
"settings": {
"outputSelection": {
"fileA": { "A": [ "metadata" ] }
},
"optimizer": { "enabled": true }
},
"sources": {
"fileA": {
"content": "contract A { }"
}
}
}
)";
Json::Value result = compile(input);
BOOST_CHECK(containsAtMostWarnings(result));
Json::Value contract = getContractResult(result, "fileA", "A");
BOOST_CHECK(contract.isObject());
BOOST_CHECK(contract["metadata"].isString());
Json::Value metadata;
BOOST_CHECK(jsonParseStrict(contract["metadata"].asString(), metadata));
Json::Value const& optimizer = metadata["settings"]["optimizer"];
BOOST_CHECK(optimizer.isMember("enabled"));
BOOST_CHECK(optimizer["enabled"].asBool() == true);
BOOST_CHECK(!optimizer.isMember("details"));
BOOST_CHECK(optimizer["runs"].asUInt() == 200);
}
BOOST_AUTO_TEST_CASE(optimizer_settings_details_exactly_as_default_disabled)
{
char const* input = R"(
{
"language": "Solidity",
"settings": {
"outputSelection": {
"fileA": { "A": [ "metadata" ] }
},
"optimizer": { "details": {
"constantOptimizer" : false,
"cse" : false,
"deduplicate" : false,
"jumpdestRemover" : true,
"orderLiterals" : false,
"peephole" : true
} }
},
"sources": {
"fileA": {
"content": "contract A { }"
}
}
}
)";
Json::Value result = compile(input);
BOOST_CHECK(containsAtMostWarnings(result));
Json::Value contract = getContractResult(result, "fileA", "A");
BOOST_CHECK(contract.isObject());
BOOST_CHECK(contract["metadata"].isString());
Json::Value metadata;
BOOST_CHECK(jsonParseStrict(contract["metadata"].asString(), metadata));
Json::Value const& optimizer = metadata["settings"]["optimizer"];
BOOST_CHECK(optimizer.isMember("enabled"));
// enabled is switched to false instead!
BOOST_CHECK(optimizer["enabled"].asBool() == false);
BOOST_CHECK(!optimizer.isMember("details"));
BOOST_CHECK(optimizer["runs"].asUInt() == 200);
}
BOOST_AUTO_TEST_CASE(optimizer_settings_details_different)
{
char const* input = R"(
{
"language": "Solidity",
"settings": {
"outputSelection": {
"fileA": { "A": [ "metadata" ] }
},
"optimizer": { "runs": 600, "details": {
"constantOptimizer" : true,
"cse" : false,
"deduplicate" : true,
"jumpdestRemover" : true,
"orderLiterals" : false,
"peephole" : true,
"yul": true
} }
},
"sources": {
"fileA": {
"content": "contract A { }"
}
}
}
)";
Json::Value result = compile(input);
BOOST_CHECK(containsAtMostWarnings(result));
Json::Value contract = getContractResult(result, "fileA", "A");
BOOST_CHECK(contract.isObject());
BOOST_CHECK(contract["metadata"].isString());
Json::Value metadata;
BOOST_CHECK(jsonParseStrict(contract["metadata"].asString(), metadata));
Json::Value const& optimizer = metadata["settings"]["optimizer"];
BOOST_CHECK(!optimizer.isMember("enabled"));
BOOST_CHECK(optimizer.isMember("details"));
BOOST_CHECK(optimizer["details"]["constantOptimizer"].asBool() == true);
BOOST_CHECK(optimizer["details"]["cse"].asBool() == false);
BOOST_CHECK(optimizer["details"]["deduplicate"].asBool() == true);
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"]["yulDetails"].isObject());
BOOST_CHECK_EQUAL(optimizer["details"].getMemberNames().size(), 8);
BOOST_CHECK(optimizer["runs"].asUInt() == 600);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()