diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index f1f386300..8c1099855 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -423,18 +423,12 @@ void CompilerContext::appendInlineAssembly( // so we essentially only optimize the ABI functions. if (_optimiserSettings.runYulOptimiser && _localVariables.empty()) { - bool const isCreation = m_runtimeContext != nullptr; - yul::GasMeter meter(dialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment); yul::Object obj; obj.code = parserResult; obj.analysisInfo = make_shared(analysisInfo); - yul::OptimiserSuite::run( - dialect, - &meter, - obj, - _optimiserSettings.optimizeStackAllocation, - externallyUsedIdentifiers - ); + + optimizeYul(obj, dialect, _optimiserSettings, externallyUsedIdentifiers); + analysisInfo = std::move(*obj.analysisInfo); parserResult = std::move(obj.code); @@ -462,6 +456,29 @@ void CompilerContext::appendInlineAssembly( updateSourceLocation(); } + +void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _dialect, OptimiserSettings const& _optimiserSettings, std::set const& _externalIdentifiers) +{ +#ifdef SOL_OUTPUT_ASM + cout << yul::AsmPrinter(*dialect)(*_object.code) << endl; +#endif + + bool const isCreation = runtimeContext() != nullptr; + yul::GasMeter meter(_dialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment); + yul::OptimiserSuite::run( + _dialect, + &meter, + _object, + _optimiserSettings.optimizeStackAllocation, + _externalIdentifiers + ); + +#ifdef SOL_OUTPUT_ASM + cout << "After optimizer:" << endl; + cout << yul::AsmPrinter(*dialect)(*object.code) << endl; +#endif +} + FunctionDefinition const& CompilerContext::resolveVirtualFunction( FunctionDefinition const& _function, vector::const_iterator _searchStart diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 28aefa036..0ee379f5a 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -32,9 +32,13 @@ #include #include +#include #include #include +#include +#include + #include #include #include @@ -232,6 +236,8 @@ public: /// Otherwise returns "revert(0, 0)". std::string revertReasonIfDebug(std::string const& _message = ""); + void optimizeYul(yul::Object& _object, yul::EVMDialect const& _dialect, OptimiserSettings const& _optimiserSetting, std::set const& _externalIdentifiers = {}); + /// Appends arbitrary data to the end of the bytecode. void appendAuxiliaryData(bytes const& _data) { m_asm->appendAuxiliaryDataToEnd(_data); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index bbf6472de..298437b1d 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -20,6 +20,7 @@ * Solidity compiler. */ + #include #include #include @@ -27,7 +28,16 @@ #include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include #include #include @@ -38,6 +48,7 @@ #include #include + #include using namespace std; @@ -788,10 +799,36 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) _assembly.appendInstruction(Instruction::POP); } }; - solAssert(_inlineAssembly.annotation().analysisInfo, ""); + + yul::Block const* code = &_inlineAssembly.operations(); + yul::AsmAnalysisInfo* analysisInfo = _inlineAssembly.annotation().analysisInfo.get(); + + // Only used in the scope below, but required to live outside to keep the + // shared_ptr's alive + yul::Object object = {}; + + // The optimiser cannot handle external references + if ( + m_optimiserSettings.runYulOptimiser && + _inlineAssembly.annotation().externalReferences.empty() + ) + { + yul::EVMDialect const* dialect = dynamic_cast(&_inlineAssembly.dialect()); + solAssert(dialect, ""); + + // Create a modifiable copy of the code and analysis + object.code = make_shared(yul::ASTCopier().translate(*code)); + object.analysisInfo = make_shared(yul::AsmAnalyzer::analyzeStrictAssertCorrect(*dialect, object)); + + m_context.optimizeYul(object, *dialect, m_optimiserSettings); + + code = object.code.get(); + analysisInfo = object.analysisInfo.get(); + } + yul::CodeGenerator::assemble( - _inlineAssembly.operations(), - *_inlineAssembly.annotation().analysisInfo, + *code, + *analysisInfo, *m_context.assemblyPtr(), m_context.evmVersion(), identifierAccess, diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index 45cce07c2..0ac0b5f82 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -84,6 +84,7 @@ public: virtual Expression translate(Expression const& _expression); virtual Statement translate(Statement const& _statement); + Block translate(Block const& _block); protected: template std::vector translateVector(std::vector const& _values); @@ -94,7 +95,6 @@ protected: return _v ? std::make_unique(translate(*_v)) : nullptr; } - Block translate(Block const& _block); Case translate(Case const& _case); virtual Identifier translate(Identifier const& _identifier); Literal translate(Literal const& _literal); diff --git a/scripts/check_style.sh b/scripts/check_style.sh index 340aba47e..0bd6f6bc0 100755 --- a/scripts/check_style.sh +++ b/scripts/check_style.sh @@ -17,6 +17,7 @@ fi FORMATERROR=$( ( + git grep -nIE "#include \"" -- '*.h' '*.cpp' | egrep -v -e "license.h" -e "BuildInfo.h" # Use include with <> characters git grep -nIE "\<(if|for|while|switch)\(" -- '*.h' '*.cpp' # no space after "if", "for", "while" or "switch" git grep -nIE "\\s*\([^=]*\>\s:\s.*\)" -- '*.h' '*.cpp' # no space before range based for-loop git grep -nIE "\\s*\(.*\)\s*\{\s*$" -- '*.h' '*.cpp' # "{\n" on same line as "if" / "for" diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 754cd0bac..cf832e614 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -155,6 +155,7 @@ function test_solc_behaviour() rm "$stdout_path.bak" else sed -i.bak -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" + sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1AUXDATA REMOVED/' "$stdout_path" sed -i.bak -e 's/ Consider adding "pragma .*$//' "$stderr_path" # Remove trailing empty lines. Needs a line break to make OSX sed happy. sed -i.bak -e '1{/^$/d diff --git a/test/cmdlineTests/optimizer_user_yul/args b/test/cmdlineTests/optimizer_user_yul/args new file mode 100644 index 000000000..8942fcc35 --- /dev/null +++ b/test/cmdlineTests/optimizer_user_yul/args @@ -0,0 +1 @@ +--optimize --asm --metadata-hash none diff --git a/test/cmdlineTests/optimizer_user_yul/input.sol b/test/cmdlineTests/optimizer_user_yul/input.sol new file mode 100644 index 000000000..74dc84cbd --- /dev/null +++ b/test/cmdlineTests/optimizer_user_yul/input.sol @@ -0,0 +1,37 @@ +pragma solidity >=0.0; + +contract C +{ + constructor() public payable + { + int a; + + // Can't be optimized due to external reference "a" + assembly + { + let x,y,z + + sstore(0, 1) + + for { } sload(4) { } { + z := exp(x, y) + } + + a := 2 + } + + // Can be optimized due to no external references + assembly + { + let x,y,z + + sstore(2, 3) + + for { } sload(5) { } { + // Expected to be optimized out for yulOptimizer, but not for + // old optimizer + z := exp(x, y) + } + } + } +} diff --git a/test/cmdlineTests/optimizer_user_yul/output b/test/cmdlineTests/optimizer_user_yul/output new file mode 100644 index 000000000..b95f2e0fa --- /dev/null +++ b/test/cmdlineTests/optimizer_user_yul/output @@ -0,0 +1,87 @@ + +======= optimizer_user_yul/input.sol:C ======= +EVM assembly: + /* "optimizer_user_yul/input.sol":24:489 contract C... */ + mstore(0x40, 0x80) + /* "optimizer_user_yul/input.sol":72:77 int a */ + 0x00 + /* "optimizer_user_yul/input.sol":38:487 constructor() public payable... */ + dup1 + 0x00 + dup1 + /* "optimizer_user_yul/input.sol":176:177 1 */ + 0x01 + /* "optimizer_user_yul/input.sol":173:174 0 */ + 0x00 + /* "optimizer_user_yul/input.sol":166:178 sstore(0, 1) */ + sstore + /* "optimizer_user_yul/input.sol":183:229 for { } sload(4) { } {... */ +tag_3: + /* "optimizer_user_yul/input.sol":197:198 4 */ + 0x04 + /* "optimizer_user_yul/input.sol":191:199 sload(4) */ + sload + /* "optimizer_user_yul/input.sol":183:229 for { } sload(4) { } {... */ + iszero + tag_5 + jumpi + pop + /* "optimizer_user_yul/input.sol":215:224 exp(x, y) */ + dup1 + dup3 + exp + /* "optimizer_user_yul/input.sol":183:229 for { } sload(4) { } {... */ + jump(tag_3) +tag_5: + /* "optimizer_user_yul/input.sol":187:190 { } */ + pop + pop + pop + /* "optimizer_user_yul/input.sol":239:240 2 */ + 0x02 + /* "optimizer_user_yul/input.sol":234:240 a := 2 */ + swap1 + pop + /* "optimizer_user_yul/input.sol":340:341 3 */ + 0x03 + /* "optimizer_user_yul/input.sol":337:338 2 */ + 0x02 + /* "optimizer_user_yul/input.sol":330:342 sstore(2, 3) */ + sstore + /* "optimizer_user_yul/input.sol":347:480 for { } sload(5) { } {... */ +tag_6: + /* "optimizer_user_yul/input.sol":361:362 5 */ + 0x05 + /* "optimizer_user_yul/input.sol":355:363 sload(5) */ + sload + tag_9 + jumpi + jump(tag_8) +tag_9: + /* "optimizer_user_yul/input.sol":347:480 for { } sload(5) { } {... */ + jump(tag_6) +tag_8: + /* "optimizer_user_yul/input.sol":311:484 {... */ + pop + /* "optimizer_user_yul/input.sol":24:489 contract C... */ + dataSize(sub_0) + dup1 + dataOffset(sub_0) + 0x00 + codecopy + 0x00 + return +stop + +sub_0: assembly { + /* "optimizer_user_yul/input.sol":24:489 contract C... */ + mstore(0x40, 0x80) + /* "--CODEGEN--":12:13 */ + 0x00 + /* "--CODEGEN--":9:10 */ + dup1 + /* "--CODEGEN--":2:14 */ + revert + + auxdata: AUXDATA REMOVED +}