From da38149f5780f8c29fc81c290d71b6e472d4b55c Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Thu, 19 Nov 2020 16:32:35 -0500 Subject: [PATCH 001/235] [test] Add support for --enforce-compile-to-ewasm. --- test/Common.cpp | 1 + test/Common.h | 1 + test/TestCase.h | 1 + test/boostTest.cpp | 6 +- test/libsolidity/SemanticTest.cpp | 154 +++++++++++++++++++----------- test/libsolidity/SemanticTest.h | 14 ++- test/tools/isoltest.cpp | 1 + 7 files changed, 115 insertions(+), 63 deletions(-) diff --git a/test/Common.cpp b/test/Common.cpp index 8406727d3..10883e872 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -103,6 +103,7 @@ CommonOptions::CommonOptions(std::string _caption): ("no-smt", po::bool_switch(&disableSMT), "disable SMT checker") ("optimize", po::bool_switch(&optimize), "enables optimization") ("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.") + ("enforce-compile-to-ewasm", po::bool_switch(&enforceCompileToEwasm), "Enforce compiling all tests to Ewasm to see if additional tests can be activated.") ("enforce-gas-cost", po::bool_switch(&enforceGasTest), "Enforce checking gas cost in semantic tests.") ("enforce-gas-cost-min-value", po::value(&enforceGasTestMinValue), "Threshold value to enforce adding gas checks to a test.") ("abiencoderv1", po::bool_switch(&useABIEncoderV1), "enables abi encoder v1") diff --git a/test/Common.h b/test/Common.h index 2434e49ce..047d0b8ae 100644 --- a/test/Common.h +++ b/test/Common.h @@ -59,6 +59,7 @@ struct CommonOptions bool ewasm = false; bool optimize = false; bool enforceViaYul = false; + bool enforceCompileToEwasm = false; bool enforceGasTest = false; u256 enforceGasTestMinValue = 100000; bool disableSMT = false; diff --git a/test/TestCase.h b/test/TestCase.h index f4699db39..5d4394b11 100644 --- a/test/TestCase.h +++ b/test/TestCase.h @@ -41,6 +41,7 @@ public: langutil::EVMVersion evmVersion; std::vector vmPaths; bool enforceCompileViaYul = false; + bool enforceCompileToEwasm = false; bool enforceGasCost = false; u256 enforceGasCostMinValue; }; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 0162cd593..86d2e0a66 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -65,6 +65,7 @@ int registerTests( boost::filesystem::path const& _basepath, boost::filesystem::path const& _path, bool _enforceViaYul, + bool _enforceCompileToEwasm, vector const& _labels, TestCase::TestCaseCreator _testCaseCreator ) @@ -76,8 +77,9 @@ int registerTests( solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().vmPaths, _enforceViaYul, + _enforceCompileToEwasm, solidity::test::CommonOptions::get().enforceGasTest, - solidity::test::CommonOptions::get().enforceGasTestMinValue + solidity::test::CommonOptions::get().enforceGasTestMinValue, }; if (fs::is_directory(fullpath)) { @@ -91,6 +93,7 @@ int registerTests( *sub_suite, _basepath, _path / entry.path().filename(), _enforceViaYul, + _enforceCompileToEwasm, _labels, _testCaseCreator ); @@ -195,6 +198,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) options.testPath / ts.path, ts.subpath, options.enforceViaYul, + options.enforceCompileToEwasm, ts.labels, ts.testCaseCreator ) > 0, std::string("no ") + ts.title + " tests found"); diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 7a06f0ba1..0307abedc 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -50,6 +50,7 @@ SemanticTest::SemanticTest( langutil::EVMVersion _evmVersion, vector const& _vmPaths, bool _enforceViaYul, + bool _enforceCompileToEwasm, bool _enforceGasCost, u256 _enforceGasCostMinValue ): @@ -58,51 +59,43 @@ SemanticTest::SemanticTest( m_sources(m_reader.sources()), m_lineOffset(m_reader.lineNumber()), m_enforceViaYul(_enforceViaYul), + m_enforceCompileToEwasm(_enforceCompileToEwasm), m_enforceGasCost(_enforceGasCost), - m_enforceGasCostMinValue(_enforceGasCostMinValue) + m_enforceGasCostMinValue(std::move(_enforceGasCostMinValue)) { initializeBuiltins(); - string choice = m_reader.stringSetting("compileViaYul", "default"); - if (choice == "also") + static set const compileViaYulAllowedValues{"also", "true", "false", "default"}; + static set const yulRunTriggers{"also", "true"}; + static set const legacyRunTriggers{"also", "false", "default"}; + + string compileViaYul = m_reader.stringSetting("compileViaYul", "default"); + if (!contains(compileViaYulAllowedValues, compileViaYul)) + BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + compileViaYul + ".")); + m_testCaseWantsYulRun = contains(yulRunTriggers, compileViaYul); + m_testCaseWantsLegacyRun = contains(legacyRunTriggers, compileViaYul); + + // Do not enforce via yul and ewasm, if via yul was explicitly denied. + if (compileViaYul == "false") { - m_runWithYul = true; - m_runWithoutYul = true; - } - else if (choice == "true") - { - m_runWithYul = true; - m_runWithoutYul = false; - } - else if (choice == "false") - { - m_runWithYul = false; - m_runWithoutYul = true; - // Do not try to run via yul if explicitly denied. m_enforceViaYul = false; + m_enforceCompileToEwasm = false; } - else if (choice == "default") - { - m_runWithYul = false; - m_runWithoutYul = true; - } - else - BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + choice + ".")); string compileToEwasm = m_reader.stringSetting("compileToEwasm", "false"); if (compileToEwasm == "also") - m_runWithEwasm = true; + m_testCaseWantsEwasmRun = true; else if (compileToEwasm == "false") - m_runWithEwasm = false; + m_testCaseWantsEwasmRun = false; else BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + ".")); - if (m_runWithEwasm && !m_runWithYul) + if (m_testCaseWantsEwasmRun && !m_testCaseWantsYulRun) BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + ", compileViaYul need to be enabled.")); - // run ewasm tests only, if an ewasm evmc vm was defined - if (m_runWithEwasm && !m_supportsEwasm) - m_runWithEwasm = false; + // run ewasm tests only if an ewasm evmc vm was defined + if (m_testCaseWantsEwasmRun && !m_supportsEwasm) + m_testCaseWantsEwasmRun = false; m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false); if (m_runWithABIEncoderV1Only && !solidity::test::CommonOptions::get().useABIEncoderV1) @@ -136,17 +129,26 @@ void SemanticTest::initializeBuiltins() TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) { TestResult result = TestResult::Success; - bool compileViaYul = m_runWithYul || m_enforceViaYul; - if (m_runWithoutYul) + if (m_testCaseWantsLegacyRun) result = runTest(_stream, _linePrefix, _formatted, false, false); - if (compileViaYul && result == TestResult::Success) + if ((m_testCaseWantsYulRun || m_enforceViaYul) && result == TestResult::Success) result = runTest(_stream, _linePrefix, _formatted, true, false); - if (m_runWithEwasm && result == TestResult::Success) - result = runTest(_stream, _linePrefix, _formatted, true, true); - + if ((m_testCaseWantsEwasmRun || m_enforceCompileToEwasm) && result == TestResult::Success) + { + // TODO: Once we have full Ewasm support, we could remove try/catch here. + try + { + result = runTest(_stream, _linePrefix, _formatted, true, true); + } + catch (...) + { + if (!m_enforceCompileToEwasm) + throw; + } + } return result; } @@ -154,31 +156,34 @@ TestCase::TestResult SemanticTest::runTest( ostream& _stream, string const& _linePrefix, bool _formatted, - bool _compileViaYul, - bool _compileToEwasm -) + bool _isYulRun, + bool _isEwasmRun) { bool success = true; m_gasCostFailure = false; - if (_compileViaYul && _compileToEwasm) + if (_isEwasmRun) + { + soltestAssert(_isYulRun, ""); selectVM(evmc_capabilities::EVMC_CAPABILITY_EWASM); + } else selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1); reset(); - m_compileViaYul = _compileViaYul; - if (_compileToEwasm) + m_compileViaYul = _isYulRun; + if (_isEwasmRun) { soltestAssert(m_compileViaYul, ""); - m_compileToEwasm = _compileToEwasm; + m_compileToEwasm = _isEwasmRun; } - m_compileViaYulCanBeSet = false; + m_canEnableYulRun = false; + m_canEnableEwasmRun = false; - if (_compileViaYul) - AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl; + if (_isYulRun) + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul" << (_isEwasmRun ? " (ewasm):" : ":") << endl; for (TestFunctionCall& test: m_tests) test.reset(); @@ -230,7 +235,7 @@ TestCase::TestResult SemanticTest::runTest( { if (m_transactionSuccessful == test.call().expectations.failure) success = false; - if (success && !checkGasCostExpectation(test, _compileViaYul)) + if (success && !checkGasCostExpectation(test, _isYulRun)) m_gasCostFailure = true; test.setFailure(!m_transactionSuccessful); @@ -268,7 +273,7 @@ TestCase::TestResult SemanticTest::runTest( } bool outputMismatch = (output != test.call().expectations.rawBytes()); - if (!outputMismatch && !checkGasCostExpectation(test, _compileViaYul)) + if (!outputMismatch && !checkGasCostExpectation(test, _isYulRun)) { success = false; m_gasCostFailure = true; @@ -287,9 +292,9 @@ TestCase::TestResult SemanticTest::runTest( } } - if (!m_runWithYul && _compileViaYul) + if (!m_testCaseWantsYulRun && _isYulRun) { - m_compileViaYulCanBeSet = success; + m_canEnableYulRun = success; string message = success ? "Test can pass via Yul, but marked with \"compileViaYul: false.\"" : "Test compiles via Yul, but it gives different test results."; @@ -299,8 +304,31 @@ TestCase::TestResult SemanticTest::runTest( return TestResult::Failure; } - if (!success && (m_runWithYul || !_compileViaYul)) + // Right now we have sometimes different test results in Yul vs. Ewasm. + // The main reason is that Ewasm just returns a failure in some cases. + // TODO: If Ewasm support got fully implemented, we could implement this in the same way as above. + if (success && !m_testCaseWantsEwasmRun && _isEwasmRun) { + // TODO: There is something missing in Ewasm to support other types of revert strings: + // for now, we just ignore test-cases that do not use RevertStrings::Default. + if (m_revertStrings != RevertStrings::Default) + return TestResult::Success; + + m_canEnableEwasmRun = true; + AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << + _linePrefix << endl << + _linePrefix << "Test can pass via Yul (Ewasm), but marked with \"compileToEwasm: false.\"" << endl; + return TestResult::Failure; + } + + if (!success) + { + // Ignore failing tests that can't yet get compiled to Ewasm: + // if the test run was not successful and enforce compiling to ewasm was set, + // but the test case did not want to get run with Ewasm, we just ignore this failure. + if (m_enforceCompileToEwasm && !m_testCaseWantsEwasmRun) + return TestResult::Success; + AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; for (TestFunctionCall const& test: m_tests) { @@ -331,13 +359,13 @@ TestCase::TestResult SemanticTest::runTest( AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix << "Attention: Updates on the test will apply the detected format displayed." << endl; - if (_compileViaYul && m_runWithoutYul) + if (_isYulRun && m_testCaseWantsLegacyRun) { _stream << _linePrefix << endl << _linePrefix; AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) << "Note that the test passed without Yul."; _stream << endl; } - else if (!_compileViaYul && m_runWithYul) + else if (!_isYulRun && m_testCaseWantsYulRun) AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix << "Note that the test also has to pass via Yul." << endl; @@ -431,15 +459,27 @@ void SemanticTest::printUpdatedExpectations(ostream& _stream, string const&) con void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePrefix) { auto& settings = m_reader.settings(); - if (settings.empty() && !m_compileViaYulCanBeSet) + if (settings.empty() && !m_canEnableYulRun) return; _stream << _linePrefix << "// ====" << endl; - if (m_compileViaYulCanBeSet) + if (m_canEnableEwasmRun) + { + soltestAssert(m_canEnableYulRun || m_testCaseWantsYulRun, ""); + string compileViaYul = m_reader.stringSetting("compileViaYul", ""); + if (!compileViaYul.empty()) + _stream << _linePrefix << "// compileViaYul: " << compileViaYul << "\n"; + _stream << _linePrefix << "// compileToEwasm: also\n"; + } + else if (m_canEnableYulRun) _stream << _linePrefix << "// compileViaYul: also\n"; - for (auto const& setting: settings) - if (!m_compileViaYulCanBeSet || setting.first != "compileViaYul") - _stream << _linePrefix << "// " << setting.first << ": " << setting.second << endl; + + for (auto const& [settingName, settingValue]: settings) + if ( + !(settingName == "compileToEwasm" && m_canEnableEwasmRun) && + !(settingName == "compileViaYul" && (m_canEnableYulRun || m_canEnableEwasmRun)) + ) + _stream << _linePrefix << "// " << settingName << ": " << settingValue<< endl; } void SemanticTest::parseExpectations(istream& _stream) diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 2bba341f4..873357f38 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -46,6 +46,7 @@ public: _options.evmVersion, _options.vmPaths, _options.enforceCompileViaYul, + _options.enforceCompileToEwasm, _options.enforceGasCost, _options.enforceGasCostMinValue ); @@ -56,6 +57,7 @@ public: langutil::EVMVersion _evmVersion, std::vector const& _vmPaths, bool _enforceViaYul = false, + bool _enforceCompileToEwasm = false, bool _enforceGasCost = false, u256 _enforceGasCostMinValue = 100000 ); @@ -76,19 +78,21 @@ public: bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map const& _libraries = {}); private: - TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm); + TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _isYulRun, bool _isEwasmRun); bool checkGasCostExpectation(TestFunctionCall& io_test, bool _compileViaYul) const; void initializeBuiltins(); SourceMap m_sources; std::size_t m_lineOffset; std::vector m_tests; - bool m_runWithYul = false; - bool m_runWithEwasm = false; - bool m_runWithoutYul = true; + bool m_testCaseWantsYulRun = false; + bool m_testCaseWantsEwasmRun = false; + bool m_testCaseWantsLegacyRun = true; bool m_enforceViaYul = false; + bool m_enforceCompileToEwasm = false; bool m_runWithABIEncoderV1Only = false; bool m_allowNonExistingFunctions = false; - bool m_compileViaYulCanBeSet = false; + bool m_canEnableYulRun = false; + bool m_canEnableEwasmRun = false; std::map m_builtins{}; bool m_gasCostFailure = false; diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index b05cbe84a..8aa93d6cf 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -163,6 +163,7 @@ TestTool::Result TestTool::process() m_options.evmVersion(), m_options.vmPaths, m_options.enforceViaYul, + m_options.enforceCompileToEwasm, m_options.enforceGasTest, m_options.enforceGasTestMinValue }); From 1a556aaa7f71e380aa4be8e546b91f0c69eaa5a1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 5 Feb 2021 18:02:03 +0000 Subject: [PATCH 002/235] Move compilationTests/stringutils to semanticTests/externalContracts --- .../semanticTests/externalContracts/_stringutils}/LICENSE | 0 .../semanticTests/externalContracts/_stringutils}/README.md | 0 .../semanticTests/externalContracts/_stringutils/stringutils.sol} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename test/{compilationTests/stringutils => libsolidity/semanticTests/externalContracts/_stringutils}/LICENSE (100%) rename test/{compilationTests/stringutils => libsolidity/semanticTests/externalContracts/_stringutils}/README.md (100%) rename test/{compilationTests/stringutils/strings.sol => libsolidity/semanticTests/externalContracts/_stringutils/stringutils.sol} (100%) diff --git a/test/compilationTests/stringutils/LICENSE b/test/libsolidity/semanticTests/externalContracts/_stringutils/LICENSE similarity index 100% rename from test/compilationTests/stringutils/LICENSE rename to test/libsolidity/semanticTests/externalContracts/_stringutils/LICENSE diff --git a/test/compilationTests/stringutils/README.md b/test/libsolidity/semanticTests/externalContracts/_stringutils/README.md similarity index 100% rename from test/compilationTests/stringutils/README.md rename to test/libsolidity/semanticTests/externalContracts/_stringutils/README.md diff --git a/test/compilationTests/stringutils/strings.sol b/test/libsolidity/semanticTests/externalContracts/_stringutils/stringutils.sol similarity index 100% rename from test/compilationTests/stringutils/strings.sol rename to test/libsolidity/semanticTests/externalContracts/_stringutils/stringutils.sol From 176ce4edf7524a17ad47c93cf50541970b338378 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 2 Nov 2020 11:53:17 +0000 Subject: [PATCH 003/235] Split EthAssemblyAdapter from AsmCodeGen --- libyul/AssemblyStack.cpp | 2 +- libyul/CMakeLists.txt | 4 +- libyul/backends/evm/AsmCodeGen.cpp | 183 +------------------ libyul/backends/evm/AsmCodeGen.h | 46 +---- libyul/backends/evm/EthAssemblyAdapter.cpp | 203 +++++++++++++++++++++ libyul/backends/evm/EthAssemblyAdapter.h | 74 ++++++++ 6 files changed, 288 insertions(+), 224 deletions(-) create mode 100644 libyul/backends/evm/EthAssemblyAdapter.cpp create mode 100644 libyul/backends/evm/EthAssemblyAdapter.h diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 9a3f41b68..0f52bdaa2 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index d8bd02842..77d09706a 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -49,10 +49,12 @@ add_library(yul Utilities.h YulString.h backends/evm/AbstractAssembly.h - backends/evm/AsmCodeGen.h backends/evm/AsmCodeGen.cpp + backends/evm/AsmCodeGen.h backends/evm/ConstantOptimiser.cpp backends/evm/ConstantOptimiser.h + backends/evm/EthAssemblyAdapter.cpp + backends/evm/EthAssemblyAdapter.h backends/evm/EVMAssembly.cpp backends/evm/EVMAssembly.h backends/evm/EVMCodeTransform.cpp diff --git a/libyul/backends/evm/AsmCodeGen.cpp b/libyul/backends/evm/AsmCodeGen.cpp index fa9122fdc..bda3ad9e7 100644 --- a/libyul/backends/evm/AsmCodeGen.cpp +++ b/libyul/backends/evm/AsmCodeGen.cpp @@ -16,27 +16,15 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * Adaptor between the abstract assembly and eth assembly. + * Helper to compile Yul code using libevmasm. */ #include -#include -#include - -#include +#include #include - -#include -#include -#include - -#include - -#include - -#include -#include +#include +#include using namespace std; using namespace solidity; @@ -44,169 +32,6 @@ using namespace solidity::yul; using namespace solidity::util; using namespace solidity::langutil; -EthAssemblyAdapter::EthAssemblyAdapter(evmasm::Assembly& _assembly): - m_assembly(_assembly) -{ -} - -void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location) -{ - m_assembly.setSourceLocation(_location); -} - -int EthAssemblyAdapter::stackHeight() const -{ - return m_assembly.deposit(); -} - -void EthAssemblyAdapter::setStackHeight(int height) -{ - m_assembly.setDeposit(height); -} - -void EthAssemblyAdapter::appendInstruction(evmasm::Instruction _instruction) -{ - m_assembly.append(_instruction); -} - -void EthAssemblyAdapter::appendConstant(u256 const& _constant) -{ - m_assembly.append(_constant); -} - -void EthAssemblyAdapter::appendLabel(LabelID _labelId) -{ - m_assembly.append(evmasm::AssemblyItem(evmasm::Tag, _labelId)); -} - -void EthAssemblyAdapter::appendLabelReference(LabelID _labelId) -{ - m_assembly.append(evmasm::AssemblyItem(evmasm::PushTag, _labelId)); -} - -size_t EthAssemblyAdapter::newLabelId() -{ - return assemblyTagToIdentifier(m_assembly.newTag()); -} - -size_t EthAssemblyAdapter::namedLabel(std::string const& _name) -{ - return assemblyTagToIdentifier(m_assembly.namedTag(_name)); -} - -void EthAssemblyAdapter::appendLinkerSymbol(std::string const& _linkerSymbol) -{ - m_assembly.appendLibraryAddress(_linkerSymbol); -} - -void EthAssemblyAdapter::appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables) -{ - m_assembly.appendVerbatim(move(_data), _arguments, _returnVariables); -} - -void EthAssemblyAdapter::appendJump(int _stackDiffAfter, JumpType _jumpType) -{ - appendJumpInstruction(evmasm::Instruction::JUMP, _jumpType); - m_assembly.adjustDeposit(_stackDiffAfter); -} - -void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) -{ - appendLabelReference(_labelId); - appendJump(_stackDiffAfter, _jumpType); -} - -void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId, JumpType _jumpType) -{ - appendLabelReference(_labelId); - appendJumpInstruction(evmasm::Instruction::JUMPI, _jumpType); -} - -void EthAssemblyAdapter::appendAssemblySize() -{ - m_assembly.appendProgramSize(); -} - -pair, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(string _name) -{ - shared_ptr assembly{make_shared(std::move(_name))}; - auto sub = m_assembly.newSub(assembly); - return {make_shared(*assembly), static_cast(sub.data())}; -} - -void EthAssemblyAdapter::appendDataOffset(vector const& _subPath) -{ - if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end()) - { - yulAssert(_subPath.size() == 1, ""); - m_assembly << evmasm::AssemblyItem(evmasm::PushData, it->second); - return; - } - - m_assembly.pushSubroutineOffset(m_assembly.encodeSubPath(_subPath)); -} - -void EthAssemblyAdapter::appendDataSize(vector const& _subPath) -{ - if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end()) - { - yulAssert(_subPath.size() == 1, ""); - m_assembly << u256(m_assembly.data(h256(it->second)).size()); - return; - } - - m_assembly.pushSubroutineSize(m_assembly.encodeSubPath(_subPath)); -} - -AbstractAssembly::SubID EthAssemblyAdapter::appendData(bytes const& _data) -{ - evmasm::AssemblyItem pushData = m_assembly.newData(_data); - SubID subID = m_nextDataCounter++; - m_dataHashBySubId[subID] = pushData.data(); - return subID; -} - -void EthAssemblyAdapter::appendImmutable(std::string const& _identifier) -{ - m_assembly.appendImmutable(_identifier); -} - -void EthAssemblyAdapter::appendImmutableAssignment(std::string const& _identifier) -{ - m_assembly.appendImmutableAssignment(_identifier); -} - -void EthAssemblyAdapter::markAsInvalid() -{ - m_assembly.markAsInvalid(); -} - -EthAssemblyAdapter::LabelID EthAssemblyAdapter::assemblyTagToIdentifier(evmasm::AssemblyItem const& _tag) -{ - u256 id = _tag.data(); - yulAssert(id <= std::numeric_limits::max(), "Tag id too large."); - return LabelID(id); -} - -void EthAssemblyAdapter::appendJumpInstruction(evmasm::Instruction _instruction, JumpType _jumpType) -{ - yulAssert(_instruction == evmasm::Instruction::JUMP || _instruction == evmasm::Instruction::JUMPI, ""); - evmasm::AssemblyItem jump(_instruction); - switch (_jumpType) - { - case JumpType::Ordinary: - yulAssert(jump.getJumpType() == evmasm::AssemblyItem::JumpType::Ordinary, ""); - break; - case JumpType::IntoFunction: - jump.setJumpType(evmasm::AssemblyItem::JumpType::IntoFunction); - break; - case JumpType::OutOfFunction: - jump.setJumpType(evmasm::AssemblyItem::JumpType::OutOfFunction); - break; - } - m_assembly.append(std::move(jump)); -} - void CodeGenerator::assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, diff --git a/libyul/backends/evm/AsmCodeGen.h b/libyul/backends/evm/AsmCodeGen.h index cab2c4003..35db4e7a0 100644 --- a/libyul/backends/evm/AsmCodeGen.h +++ b/libyul/backends/evm/AsmCodeGen.h @@ -16,63 +16,24 @@ */ // SPDX-License-Identifier: GPL-3.0 /** - * Adaptor between the abstract assembly and eth assembly. + * Helper to compile Yul code using libevmasm. */ #pragma once #include #include -#include -#include +#include namespace solidity::evmasm { class Assembly; -class AssemblyItem; } namespace solidity::yul { struct Block; - -class EthAssemblyAdapter: public AbstractAssembly -{ -public: - explicit EthAssemblyAdapter(evmasm::Assembly& _assembly); - void setSourceLocation(langutil::SourceLocation const& _location) override; - int stackHeight() const override; - void setStackHeight(int height) override; - void appendInstruction(evmasm::Instruction _instruction) override; - void appendConstant(u256 const& _constant) override; - void appendLabel(LabelID _labelId) override; - void appendLabelReference(LabelID _labelId) override; - size_t newLabelId() override; - size_t namedLabel(std::string const& _name) override; - void appendLinkerSymbol(std::string const& _linkerSymbol) override; - void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables) override; - void appendJump(int _stackDiffAfter, JumpType _jumpType) override; - void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override; - void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override; - void appendAssemblySize() override; - std::pair, SubID> createSubAssembly(std::string _name = {}) override; - void appendDataOffset(std::vector const& _subPath) override; - void appendDataSize(std::vector const& _subPath) override; - SubID appendData(bytes const& _data) override; - - void appendImmutable(std::string const& _identifier) override; - void appendImmutableAssignment(std::string const& _identifier) override; - - void markAsInvalid() override; - -private: - static LabelID assemblyTagToIdentifier(evmasm::AssemblyItem const& _tag); - void appendJumpInstruction(evmasm::Instruction _instruction, JumpType _jumpType); - - evmasm::Assembly& m_assembly; - std::map m_dataHashBySubId; - size_t m_nextDataCounter = std::numeric_limits::max() / 2; -}; +struct AsmAnalysisInfo; class CodeGenerator { @@ -88,5 +49,4 @@ public: bool _optimizeStackAllocation = false ); }; - } diff --git a/libyul/backends/evm/EthAssemblyAdapter.cpp b/libyul/backends/evm/EthAssemblyAdapter.cpp new file mode 100644 index 000000000..ed2c81777 --- /dev/null +++ b/libyul/backends/evm/EthAssemblyAdapter.cpp @@ -0,0 +1,203 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Adaptor between AbstractAssembly and libevmasm. + */ + +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace solidity::yul; +using namespace solidity::util; +using namespace solidity::langutil; + +EthAssemblyAdapter::EthAssemblyAdapter(evmasm::Assembly& _assembly): + m_assembly(_assembly) +{ +} + +void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location) +{ + m_assembly.setSourceLocation(_location); +} + +int EthAssemblyAdapter::stackHeight() const +{ + return m_assembly.deposit(); +} + +void EthAssemblyAdapter::setStackHeight(int height) +{ + m_assembly.setDeposit(height); +} + +void EthAssemblyAdapter::appendInstruction(evmasm::Instruction _instruction) +{ + m_assembly.append(_instruction); +} + +void EthAssemblyAdapter::appendConstant(u256 const& _constant) +{ + m_assembly.append(_constant); +} + +void EthAssemblyAdapter::appendLabel(LabelID _labelId) +{ + m_assembly.append(evmasm::AssemblyItem(evmasm::Tag, _labelId)); +} + +void EthAssemblyAdapter::appendLabelReference(LabelID _labelId) +{ + m_assembly.append(evmasm::AssemblyItem(evmasm::PushTag, _labelId)); +} + +size_t EthAssemblyAdapter::newLabelId() +{ + return assemblyTagToIdentifier(m_assembly.newTag()); +} + +size_t EthAssemblyAdapter::namedLabel(std::string const& _name) +{ + return assemblyTagToIdentifier(m_assembly.namedTag(_name)); +} + +void EthAssemblyAdapter::appendLinkerSymbol(std::string const& _linkerSymbol) +{ + m_assembly.appendLibraryAddress(_linkerSymbol); +} + +void EthAssemblyAdapter::appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables) +{ + m_assembly.appendVerbatim(move(_data), _arguments, _returnVariables); +} + +void EthAssemblyAdapter::appendJump(int _stackDiffAfter, JumpType _jumpType) +{ + appendJumpInstruction(evmasm::Instruction::JUMP, _jumpType); + m_assembly.adjustDeposit(_stackDiffAfter); +} + +void EthAssemblyAdapter::appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) +{ + appendLabelReference(_labelId); + appendJump(_stackDiffAfter, _jumpType); +} + +void EthAssemblyAdapter::appendJumpToIf(LabelID _labelId, JumpType _jumpType) +{ + appendLabelReference(_labelId); + appendJumpInstruction(evmasm::Instruction::JUMPI, _jumpType); +} + +void EthAssemblyAdapter::appendAssemblySize() +{ + m_assembly.appendProgramSize(); +} + +pair, AbstractAssembly::SubID> EthAssemblyAdapter::createSubAssembly(string _name) +{ + shared_ptr assembly{make_shared(std::move(_name))}; + auto sub = m_assembly.newSub(assembly); + return {make_shared(*assembly), static_cast(sub.data())}; +} + +void EthAssemblyAdapter::appendDataOffset(vector const& _subPath) +{ + if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end()) + { + yulAssert(_subPath.size() == 1, ""); + m_assembly << evmasm::AssemblyItem(evmasm::PushData, it->second); + return; + } + + m_assembly.pushSubroutineOffset(m_assembly.encodeSubPath(_subPath)); +} + +void EthAssemblyAdapter::appendDataSize(vector const& _subPath) +{ + if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end()) + { + yulAssert(_subPath.size() == 1, ""); + m_assembly << u256(m_assembly.data(h256(it->second)).size()); + return; + } + + m_assembly.pushSubroutineSize(m_assembly.encodeSubPath(_subPath)); +} + +AbstractAssembly::SubID EthAssemblyAdapter::appendData(bytes const& _data) +{ + evmasm::AssemblyItem pushData = m_assembly.newData(_data); + SubID subID = m_nextDataCounter++; + m_dataHashBySubId[subID] = pushData.data(); + return subID; +} + +void EthAssemblyAdapter::appendImmutable(std::string const& _identifier) +{ + m_assembly.appendImmutable(_identifier); +} + +void EthAssemblyAdapter::appendImmutableAssignment(std::string const& _identifier) +{ + m_assembly.appendImmutableAssignment(_identifier); +} + +void EthAssemblyAdapter::markAsInvalid() +{ + m_assembly.markAsInvalid(); +} + +EthAssemblyAdapter::LabelID EthAssemblyAdapter::assemblyTagToIdentifier(evmasm::AssemblyItem const& _tag) +{ + u256 id = _tag.data(); + yulAssert(id <= std::numeric_limits::max(), "Tag id too large."); + return LabelID(id); +} + +void EthAssemblyAdapter::appendJumpInstruction(evmasm::Instruction _instruction, JumpType _jumpType) +{ + yulAssert(_instruction == evmasm::Instruction::JUMP || _instruction == evmasm::Instruction::JUMPI, ""); + evmasm::AssemblyItem jump(_instruction); + switch (_jumpType) + { + case JumpType::Ordinary: + yulAssert(jump.getJumpType() == evmasm::AssemblyItem::JumpType::Ordinary, ""); + break; + case JumpType::IntoFunction: + jump.setJumpType(evmasm::AssemblyItem::JumpType::IntoFunction); + break; + case JumpType::OutOfFunction: + jump.setJumpType(evmasm::AssemblyItem::JumpType::OutOfFunction); + break; + } + m_assembly.append(std::move(jump)); +} diff --git a/libyul/backends/evm/EthAssemblyAdapter.h b/libyul/backends/evm/EthAssemblyAdapter.h new file mode 100644 index 000000000..26c256d5b --- /dev/null +++ b/libyul/backends/evm/EthAssemblyAdapter.h @@ -0,0 +1,74 @@ +/* + 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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +/** + * Adaptor between AbstractAssembly and libevmasm. + */ + +#pragma once + +#include +#include +#include +#include + +namespace solidity::evmasm +{ +class Assembly; +class AssemblyItem; +} + +namespace solidity::yul +{ +class EthAssemblyAdapter: public AbstractAssembly +{ +public: + explicit EthAssemblyAdapter(evmasm::Assembly& _assembly); + void setSourceLocation(langutil::SourceLocation const& _location) override; + int stackHeight() const override; + void setStackHeight(int height) override; + void appendInstruction(evmasm::Instruction _instruction) override; + void appendConstant(u256 const& _constant) override; + void appendLabel(LabelID _labelId) override; + void appendLabelReference(LabelID _labelId) override; + size_t newLabelId() override; + size_t namedLabel(std::string const& _name) override; + void appendLinkerSymbol(std::string const& _linkerSymbol) override; + void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables) override; + void appendJump(int _stackDiffAfter, JumpType _jumpType) override; + void appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) override; + void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override; + void appendAssemblySize() override; + std::pair, SubID> createSubAssembly(std::string _name = {}) override; + void appendDataOffset(std::vector const& _subPath) override; + void appendDataSize(std::vector const& _subPath) override; + SubID appendData(bytes const& _data) override; + + void appendImmutable(std::string const& _identifier) override; + void appendImmutableAssignment(std::string const& _identifier) override; + + void markAsInvalid() override; + +private: + static LabelID assemblyTagToIdentifier(evmasm::AssemblyItem const& _tag); + void appendJumpInstruction(evmasm::Instruction _instruction, JumpType _jumpType); + + evmasm::Assembly& m_assembly; + std::map m_dataHashBySubId; + size_t m_nextDataCounter = std::numeric_limits::max() / 2; +}; +} From 28b9d27fae8a60e7ab5684f5cfef1c55dcedf72c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 26 Apr 2021 23:53:36 +0200 Subject: [PATCH 004/235] SoltestErrors: Qualify runtime_error with std:: to make it possible to use in in headers --- test/libsolidity/util/SoltestErrors.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libsolidity/util/SoltestErrors.h b/test/libsolidity/util/SoltestErrors.h index 3291692a8..e223f714b 100644 --- a/test/libsolidity/util/SoltestErrors.h +++ b/test/libsolidity/util/SoltestErrors.h @@ -25,7 +25,7 @@ namespace solidity::frontend::test do \ { \ if (!(CONDITION)) \ - BOOST_THROW_EXCEPTION(runtime_error(DESCRIPTION)); \ + BOOST_THROW_EXCEPTION(std::runtime_error(DESCRIPTION)); \ } \ while (false) From 5023cef3821480651105c643a5f8fe4ed8e0411e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 26 Apr 2021 23:03:22 +0200 Subject: [PATCH 005/235] OptimiserSettings: Add preset() and OptimisationPreset enum --- libsolidity/interface/OptimiserSettings.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/libsolidity/interface/OptimiserSettings.h b/libsolidity/interface/OptimiserSettings.h index 3df99325e..0dea6c3d5 100644 --- a/libsolidity/interface/OptimiserSettings.h +++ b/libsolidity/interface/OptimiserSettings.h @@ -23,12 +23,22 @@ #pragma once +#include + #include #include namespace solidity::frontend { +enum class OptimisationPreset +{ + None, + Minimal, + Standard, + Full, +}; + struct OptimiserSettings { static char constexpr DefaultYulOptimiserSteps[] = @@ -84,6 +94,18 @@ struct OptimiserSettings return standard(); } + static OptimiserSettings preset(OptimisationPreset _preset) + { + switch (_preset) + { + case OptimisationPreset::None: return none(); + case OptimisationPreset::Minimal: return minimal(); + case OptimisationPreset::Standard: return standard(); + case OptimisationPreset::Full: return full(); + default: solAssert(false, ""); + } + } + bool operator==(OptimiserSettings const& _other) const { return From 9c6f8c011fabc2d1c341db5c019dfcc62c0fffdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 26 Apr 2021 22:13:41 +0200 Subject: [PATCH 006/235] TestCaseReader: Add enumSetting() --- test/TestCaseReader.cpp | 8 +------- test/TestCaseReader.h | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/test/TestCaseReader.cpp b/test/TestCaseReader.cpp index 26b625734..decfa3cbc 100644 --- a/test/TestCaseReader.cpp +++ b/test/TestCaseReader.cpp @@ -18,18 +18,12 @@ #include -#include -#include #include #include -#include #include -#include - using namespace std; -using namespace solidity::langutil; using namespace solidity::frontend::test; namespace fs = boost::filesystem; @@ -162,7 +156,7 @@ pair TestCaseReader::parseSourcesAndSettingsWithLineNumber(is else externalSourceName = externalSourceString; - solAssert(!externalSourceName.empty(), ""); + soltestAssert(!externalSourceName.empty(), ""); fs::path externalSourceTarget(externalSourceString); fs::path testCaseParentDir = m_fileName.parent_path(); if (!externalSourceTarget.is_relative()) diff --git a/test/TestCaseReader.h b/test/TestCaseReader.h index 2d0ffe3b0..d1ef95be3 100644 --- a/test/TestCaseReader.h +++ b/test/TestCaseReader.h @@ -16,13 +16,21 @@ */ // SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include + +#include + +#include + #include +#include + #include #include #include -#pragma once - namespace solidity::frontend::test { @@ -58,6 +66,9 @@ public: size_t sizetSetting(std::string const& _name, size_t _defaultValue); std::string stringSetting(std::string const& _name, std::string const& _defaultValue); + template + E enumSetting(std::string const& _name, std::map const& _choices, std::string const& _defaultChoice); + void ensureAllSettingsRead() const; private: @@ -71,4 +82,20 @@ private: std::map m_settings; std::map m_unreadSettings; ///< tracks which settings are left unread }; + +template +E TestCaseReader::enumSetting(std::string const& _name, std::map const& _choices, std::string const& _defaultChoice) +{ + soltestAssert(_choices.count(_defaultChoice) > 0, ""); + + std::string value = stringSetting(_name, _defaultChoice); + + if (_choices.count(value) == 0) + BOOST_THROW_EXCEPTION(std::runtime_error( + "Invalid Enum value: " + value + ". Available choices: " + util::joinHumanReadable(_choices | ranges::views::keys) + "." + )); + + return _choices.at(value); +} + } From a51d72fb89453e6233c776e27bfe44dbb94f44b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 26 Apr 2021 22:18:52 +0200 Subject: [PATCH 007/235] ObjectCompilerTest: Replace 'optimize' setting with 'optimizationPreset' --- test/libyul/ObjectCompilerTest.cpp | 15 +++++++++++++-- test/libyul/ObjectCompilerTest.h | 4 +++- test/libyul/objectCompiler/long_object_name.yul | 2 +- test/libyul/objectCompiler/nested_optimizer.yul | 2 +- test/libyul/objectCompiler/simple_optimizer.yul | 2 +- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/test/libyul/ObjectCompilerTest.cpp b/test/libyul/ObjectCompilerTest.cpp index 6d8cd313e..f4a1c296d 100644 --- a/test/libyul/ObjectCompilerTest.cpp +++ b/test/libyul/ObjectCompilerTest.cpp @@ -18,6 +18,8 @@ #include +#include + #include #include @@ -43,7 +45,16 @@ ObjectCompilerTest::ObjectCompilerTest(string const& _filename): TestCase(_filename) { m_source = m_reader.source(); - m_optimize = m_reader.boolSetting("optimize", false); + m_optimisationPreset = m_reader.enumSetting( + "optimizationPreset", + { + {"none", OptimisationPreset::None}, + {"minimal", OptimisationPreset::Minimal}, + {"standard", OptimisationPreset::Standard}, + {"full", OptimisationPreset::Full}, + }, + "minimal" + ); m_wasm = m_reader.boolSetting("wasm", false); m_expectation = m_reader.simpleExpectations(); } @@ -53,7 +64,7 @@ TestCase::TestResult ObjectCompilerTest::run(ostream& _stream, string const& _li AssemblyStack stack( EVMVersion(), m_wasm ? AssemblyStack::Language::Ewasm : AssemblyStack::Language::StrictAssembly, - m_optimize ? OptimiserSettings::full() : OptimiserSettings::minimal() + OptimiserSettings::preset(m_optimisationPreset) ); if (!stack.parseAndAnalyze("source", m_source)) { diff --git a/test/libyul/ObjectCompilerTest.h b/test/libyul/ObjectCompilerTest.h index 771942e59..e75f72fa9 100644 --- a/test/libyul/ObjectCompilerTest.h +++ b/test/libyul/ObjectCompilerTest.h @@ -20,6 +20,8 @@ #include +#include + namespace solidity::langutil { class Scanner; @@ -54,7 +56,7 @@ private: static void printErrors(std::ostream& _stream, langutil::ErrorList const& _errors); - bool m_optimize = false; + frontend::OptimisationPreset m_optimisationPreset; bool m_wasm = false; }; diff --git a/test/libyul/objectCompiler/long_object_name.yul b/test/libyul/objectCompiler/long_object_name.yul index 322079928..0aa8c4405 100644 --- a/test/libyul/objectCompiler/long_object_name.yul +++ b/test/libyul/objectCompiler/long_object_name.yul @@ -7,7 +7,7 @@ object "t" { } } // ==== -// optimize: true +// optimizationPreset: full // ---- // Assembly: // /* "source":23:147 */ diff --git a/test/libyul/objectCompiler/nested_optimizer.yul b/test/libyul/objectCompiler/nested_optimizer.yul index e4512f03d..01ecf5aff 100644 --- a/test/libyul/objectCompiler/nested_optimizer.yul +++ b/test/libyul/objectCompiler/nested_optimizer.yul @@ -15,7 +15,7 @@ object "a" { } } // ==== -// optimize: true +// optimizationPreset: full // ---- // Assembly: // /* "source":48:49 */ diff --git a/test/libyul/objectCompiler/simple_optimizer.yul b/test/libyul/objectCompiler/simple_optimizer.yul index 40234a1db..375fa6347 100644 --- a/test/libyul/objectCompiler/simple_optimizer.yul +++ b/test/libyul/objectCompiler/simple_optimizer.yul @@ -5,7 +5,7 @@ sstore(add(x, 0), z) } // ==== -// optimize: true +// optimizationPreset: full // ---- // Assembly: // /* "source":26:27 */ From 43de99dee568e8768accd25afd100be1356d288f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 26 Apr 2021 22:35:16 +0200 Subject: [PATCH 008/235] Use 'optimizationPreset: none' for objectCompiler tests that rely on things that could get optimized out --- test/libyul/objectCompiler/function_series.yul | 2 ++ test/libyul/objectCompiler/jump_tags.yul | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/libyul/objectCompiler/function_series.yul b/test/libyul/objectCompiler/function_series.yul index 34c66ec46..9efcf77af 100644 --- a/test/libyul/objectCompiler/function_series.yul +++ b/test/libyul/objectCompiler/function_series.yul @@ -6,6 +6,8 @@ object "Contract" { } } +// ==== +// optimizationPreset: none // ---- // Assembly: // /* "source":33:48 */ diff --git a/test/libyul/objectCompiler/jump_tags.yul b/test/libyul/objectCompiler/jump_tags.yul index 22c8b2495..bd3920900 100644 --- a/test/libyul/objectCompiler/jump_tags.yul +++ b/test/libyul/objectCompiler/jump_tags.yul @@ -6,6 +6,8 @@ object "Contract" { } } +// ==== +// optimizationPreset: none // ---- // Assembly: // /* "source":33:54 */ From 6b3357be50d8f674f9136249a35a5caa4a19da71 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 24 Apr 2021 01:21:46 +0100 Subject: [PATCH 009/235] Add semantic tests using stringutils --- .../externalContracts/strings.sol | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 test/libsolidity/semanticTests/externalContracts/strings.sol diff --git a/test/libsolidity/semanticTests/externalContracts/strings.sol b/test/libsolidity/semanticTests/externalContracts/strings.sol new file mode 100644 index 000000000..6c16e205d --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/strings.sol @@ -0,0 +1,76 @@ +==== ExternalSource: _stringutils/stringutils.sol ==== +==== Source: strings.sol ==== +pragma abicoder v2; +import "_stringutils/stringutils.sol"; + +contract test { + using strings for bytes32; + using strings for string; + using strings for strings.slice; + + function toSlice(string memory a) external pure returns (strings.slice memory) { + return a.toSlice(); + } + + function roundtrip(string memory a) external pure returns (string memory) { + return a.toSlice().toString(); + } + + function utf8len(string memory a) external pure returns (uint) { + return a.toSlice().len(); + } + + function multiconcat(string memory a, uint count) public pure returns (string memory) { + strings.slice memory s = a.toSlice(); + for (uint i = 0; i < count; i++) { + s = s.concat(s).toSlice(); + } + return s.toString(); + } + + function benchmark(string memory text, bytes32 seed) external pure returns (uint) { + // Grow text. + text = multiconcat(text, 10); + + strings.slice memory a = text.toSlice(); + strings.slice memory b = seed.toSliceB32(); + + // Some heavy computation. + bool c = b.equals(a) || b.startsWith(a); + + // Join as a list. + strings.slice memory delim = c ? string(",").toSlice() : string(";").toSlice(); + strings.slice[] memory parts = new strings.slice[](2); + parts[0] = a; + parts[1] = b; + string memory d = delim.join(parts); + return d.toSlice().len(); + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() +// gas irOptimized: 926170 +// gas legacy: 1192776 +// gas legacyOptimized: 777238 +// toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0 +// gas irOptimized: 22877 +// gas legacy: 23190 +// gas legacyOptimized: 22508 +// roundtrip(string): 0x20, 11, "hello world" -> 0x20, 11, "hello world" +// gas irOptimized: 23783 +// gas legacy: 24560 +// gas legacyOptimized: 23481 +// utf8len(string): 0x20, 16, "\xf0\x9f\x98\x83\xf0\x9f\x98\x83\xf0\x9f\x98\x83\xf0\x9f\x98\x83" -> 4 # Input: "😃😃😃😃" # +// gas irOptimized: 24779 +// gas legacy: 25716 +// gas legacyOptimized: 24115 +// multiconcat(string,uint256): 0x40, 3, 11, "hello world" -> 0x20, 0x58, 0x68656c6c6f20776f726c6468656c6c6f20776f726c6468656c6c6f20776f726c, 0x6468656c6c6f20776f726c6468656c6c6f20776f726c6468656c6c6f20776f72, 49027192869463622675296414541903001712009715982962058146354235762728281047040 # concatenating 3 times # +// gas irOptimized: 30478 +// gas legacy: 36801 +// gas legacyOptimized: 30420 +// benchmark(string,bytes32): 0x40, 0x0842021, 8, "solidity" -> FAILURE, hex"4e487b71", 0x11 +// gas irOptimized: 27298 +// gas legacy: 31621 +// gas legacyOptimized: 27668 From 5331bc679a6b9d8af77c43a335a4ceb92a59693e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 24 Apr 2021 01:22:13 +0100 Subject: [PATCH 010/235] Fix some issues regarding checked arithmethics in stringutils --- .../_stringutils/stringutils.sol | 13 ++++++++-- .../externalContracts/strings.sol | 24 +++++++++---------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/test/libsolidity/semanticTests/externalContracts/_stringutils/stringutils.sol b/test/libsolidity/semanticTests/externalContracts/_stringutils/stringutils.sol index 47d8bd3f6..4dff7f916 100644 --- a/test/libsolidity/semanticTests/externalContracts/_stringutils/stringutils.sol +++ b/test/libsolidity/semanticTests/externalContracts/_stringutils/stringutils.sol @@ -52,8 +52,15 @@ library strings { src += 32; } + // The following masking would overflow in the case of len=0 + // and the code path in that case is useless, albeit correct. + // This shortcut avoids it and saves gas. + if (len == 0) + return; + // Copy remaining bytes - uint mask = 256 ** (32 - len) - 1; + uint mask; + unchecked { mask = 256 ** (32 - len) - 1; } assembly { let srcpart := and(mload(src), not(mask)) let destpart := and(mload(dest), mask) @@ -215,7 +222,9 @@ library strings { if(shortest < 32) { mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); } - uint256 diff = (a & mask) - (b & mask); + uint256 diff; + // This depends on potential underflow. + unchecked { diff = (a & mask) - (b & mask); } if (diff != 0) return int(diff); } diff --git a/test/libsolidity/semanticTests/externalContracts/strings.sol b/test/libsolidity/semanticTests/externalContracts/strings.sol index 6c16e205d..1f209ef6f 100644 --- a/test/libsolidity/semanticTests/externalContracts/strings.sol +++ b/test/libsolidity/semanticTests/externalContracts/strings.sol @@ -51,26 +51,26 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 926170 -// gas legacy: 1192776 -// gas legacyOptimized: 777238 +// gas irOptimized: 912777 +// gas legacy: 1188228 +// gas legacyOptimized: 771634 // toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0 // gas irOptimized: 22877 // gas legacy: 23190 // gas legacyOptimized: 22508 // roundtrip(string): 0x20, 11, "hello world" -> 0x20, 11, "hello world" -// gas irOptimized: 23783 -// gas legacy: 24560 -// gas legacyOptimized: 23481 +// gas irOptimized: 23676 +// gas legacy: 23820 +// gas legacyOptimized: 23123 // utf8len(string): 0x20, 16, "\xf0\x9f\x98\x83\xf0\x9f\x98\x83\xf0\x9f\x98\x83\xf0\x9f\x98\x83" -> 4 # Input: "😃😃😃😃" # // gas irOptimized: 24779 // gas legacy: 25716 // gas legacyOptimized: 24115 // multiconcat(string,uint256): 0x40, 3, 11, "hello world" -> 0x20, 0x58, 0x68656c6c6f20776f726c6468656c6c6f20776f726c6468656c6c6f20776f726c, 0x6468656c6c6f20776f726c6468656c6c6f20776f726c6468656c6c6f20776f72, 49027192869463622675296414541903001712009715982962058146354235762728281047040 # concatenating 3 times # -// gas irOptimized: 30478 -// gas legacy: 36801 -// gas legacyOptimized: 30420 -// benchmark(string,bytes32): 0x40, 0x0842021, 8, "solidity" -> FAILURE, hex"4e487b71", 0x11 -// gas irOptimized: 27298 +// gas irOptimized: 29729 // gas legacy: 31621 -// gas legacyOptimized: 27668 +// gas legacyOptimized: 27914 +// benchmark(string,bytes32): 0x40, 0x0842021, 8, "solidity" -> 0x2020 +// gas irOptimized: 2903627 +// gas legacy: 4381235 +// gas legacyOptimized: 2317529 From ada046ba9a2856090ee85613b74ebf4d25989d19 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Apr 2021 12:33:01 +0100 Subject: [PATCH 011/235] Import prb-math for testing From https://github.com/hifi-finance/prb-math/commit/62021c1abc3413f20d0bdc8f941cf9f21d5a7d2d --- .../externalContracts/_prbmath/LICENSE.md | 7 + .../_prbmath/PRBMathCommon.sol | 361 +++++++++++ .../_prbmath/PRBMathSD59x18.sol | 578 ++++++++++++++++++ .../_prbmath/PRBMathUD60x18.sol | 437 +++++++++++++ .../externalContracts/_prbmath/README.md | 1 + 5 files changed, 1384 insertions(+) create mode 100644 test/libsolidity/semanticTests/externalContracts/_prbmath/LICENSE.md create mode 100644 test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathCommon.sol create mode 100644 test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathSD59x18.sol create mode 100644 test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathUD60x18.sol create mode 100644 test/libsolidity/semanticTests/externalContracts/_prbmath/README.md diff --git a/test/libsolidity/semanticTests/externalContracts/_prbmath/LICENSE.md b/test/libsolidity/semanticTests/externalContracts/_prbmath/LICENSE.md new file mode 100644 index 000000000..77ac2a09f --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/_prbmath/LICENSE.md @@ -0,0 +1,7 @@ +# WTFPL + +by Paul Razvan Berg (@PaulRBerg) + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathCommon.sol b/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathCommon.sol new file mode 100644 index 000000000..923b54c2d --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathCommon.sol @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: WTFPL +pragma solidity >=0.8.0; + +/// @dev Common mathematical functions used in both PRBMathSD59x18 and PRBMathUD60x18. Note that this shared library +/// does not always assume the signed 59.18-decimal fixed-point or the unsigned 60.18-decimal fixed-point +// representation. When it does not, it is annonated in the function's NatSpec documentation. +library PRBMathCommon { + /// @dev How many trailing decimals can be represented. + uint256 internal constant SCALE = 1e18; + + /// @dev Largest power of two divisor of SCALE. + uint256 internal constant SCALE_LPOTD = 262144; + + /// @dev SCALE inverted mod 2^256. + uint256 internal constant SCALE_INVERSE = 78156646155174841979727994598816262306175212592076161876661508869554232690281; + + /// @notice Calculates the binary exponent of x using the binary fraction method. + /// @dev Uses 128.128-bit fixed-point numbers, which is the most efficient way. + /// See https://ethereum.stackexchange.com/a/96594/24693. + /// @param x The exponent as an unsigned 128.128-bit fixed-point number. + /// @return result The result as an unsigned 60x18 decimal fixed-point number. + function exp2(uint256 x) internal pure returns (uint256 result) { + unchecked { + // Start from 0.5 in the 128.128-bit fixed-point format. + result = 0x80000000000000000000000000000000; + + // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows + // because the initial result is 2^127 and all magic factors are less than 2^129. + if (x & 0x80000000000000000000000000000000 > 0) result = (result * 0x16A09E667F3BCC908B2FB1366EA957D3E) >> 128; + if (x & 0x40000000000000000000000000000000 > 0) result = (result * 0x1306FE0A31B7152DE8D5A46305C85EDED) >> 128; + if (x & 0x20000000000000000000000000000000 > 0) result = (result * 0x1172B83C7D517ADCDF7C8C50EB14A7920) >> 128; + if (x & 0x10000000000000000000000000000000 > 0) result = (result * 0x10B5586CF9890F6298B92B71842A98364) >> 128; + if (x & 0x8000000000000000000000000000000 > 0) result = (result * 0x1059B0D31585743AE7C548EB68CA417FE) >> 128; + if (x & 0x4000000000000000000000000000000 > 0) result = (result * 0x102C9A3E778060EE6F7CACA4F7A29BDE9) >> 128; + if (x & 0x2000000000000000000000000000000 > 0) result = (result * 0x10163DA9FB33356D84A66AE336DCDFA40) >> 128; + if (x & 0x1000000000000000000000000000000 > 0) result = (result * 0x100B1AFA5ABCBED6129AB13EC11DC9544) >> 128; + if (x & 0x800000000000000000000000000000 > 0) result = (result * 0x10058C86DA1C09EA1FF19D294CF2F679C) >> 128; + if (x & 0x400000000000000000000000000000 > 0) result = (result * 0x1002C605E2E8CEC506D21BFC89A23A011) >> 128; + if (x & 0x200000000000000000000000000000 > 0) result = (result * 0x100162F3904051FA128BCA9C55C31E5E0) >> 128; + if (x & 0x100000000000000000000000000000 > 0) result = (result * 0x1000B175EFFDC76BA38E31671CA939726) >> 128; + if (x & 0x80000000000000000000000000000 > 0) result = (result * 0x100058BA01FB9F96D6CACD4B180917C3E) >> 128; + if (x & 0x40000000000000000000000000000 > 0) result = (result * 0x10002C5CC37DA9491D0985C348C68E7B4) >> 128; + if (x & 0x20000000000000000000000000000 > 0) result = (result * 0x1000162E525EE054754457D5995292027) >> 128; + if (x & 0x10000000000000000000000000000 > 0) result = (result * 0x10000B17255775C040618BF4A4ADE83FD) >> 128; + if (x & 0x8000000000000000000000000000 > 0) result = (result * 0x1000058B91B5BC9AE2EED81E9B7D4CFAC) >> 128; + if (x & 0x4000000000000000000000000000 > 0) result = (result * 0x100002C5C89D5EC6CA4D7C8ACC017B7CA) >> 128; + if (x & 0x2000000000000000000000000000 > 0) result = (result * 0x10000162E43F4F831060E02D839A9D16D) >> 128; + if (x & 0x1000000000000000000000000000 > 0) result = (result * 0x100000B1721BCFC99D9F890EA06911763) >> 128; + if (x & 0x800000000000000000000000000 > 0) result = (result * 0x10000058B90CF1E6D97F9CA14DBCC1629) >> 128; + if (x & 0x400000000000000000000000000 > 0) result = (result * 0x1000002C5C863B73F016468F6BAC5CA2C) >> 128; + if (x & 0x200000000000000000000000000 > 0) result = (result * 0x100000162E430E5A18F6119E3C02282A6) >> 128; + if (x & 0x100000000000000000000000000 > 0) result = (result * 0x1000000B1721835514B86E6D96EFD1BFF) >> 128; + if (x & 0x80000000000000000000000000 > 0) result = (result * 0x100000058B90C0B48C6BE5DF846C5B2F0) >> 128; + if (x & 0x40000000000000000000000000 > 0) result = (result * 0x10000002C5C8601CC6B9E94213C72737B) >> 128; + if (x & 0x20000000000000000000000000 > 0) result = (result * 0x1000000162E42FFF037DF38AA2B219F07) >> 128; + if (x & 0x10000000000000000000000000 > 0) result = (result * 0x10000000B17217FBA9C739AA5819F44FA) >> 128; + if (x & 0x8000000000000000000000000 > 0) result = (result * 0x1000000058B90BFCDEE5ACD3C1CEDC824) >> 128; + if (x & 0x4000000000000000000000000 > 0) result = (result * 0x100000002C5C85FE31F35A6A30DA1BE51) >> 128; + if (x & 0x2000000000000000000000000 > 0) result = (result * 0x10000000162E42FF0999CE3541B9FFFD0) >> 128; + if (x & 0x1000000000000000000000000 > 0) result = (result * 0x100000000B17217F80F4EF5AADDA45554) >> 128; + if (x & 0x800000000000000000000000 > 0) result = (result * 0x10000000058B90BFBF8479BD5A81B51AE) >> 128; + if (x & 0x400000000000000000000000 > 0) result = (result * 0x1000000002C5C85FDF84BD62AE30A74CD) >> 128; + if (x & 0x200000000000000000000000 > 0) result = (result * 0x100000000162E42FEFB2FED257559BDAA) >> 128; + if (x & 0x100000000000000000000000 > 0) result = (result * 0x1000000000B17217F7D5A7716BBA4A9AF) >> 128; + if (x & 0x80000000000000000000000 > 0) result = (result * 0x100000000058B90BFBE9DDBAC5E109CCF) >> 128; + if (x & 0x40000000000000000000000 > 0) result = (result * 0x10000000002C5C85FDF4B15DE6F17EB0E) >> 128; + if (x & 0x20000000000000000000000 > 0) result = (result * 0x1000000000162E42FEFA494F1478FDE05) >> 128; + if (x & 0x10000000000000000000000 > 0) result = (result * 0x10000000000B17217F7D20CF927C8E94D) >> 128; + if (x & 0x8000000000000000000000 > 0) result = (result * 0x1000000000058B90BFBE8F71CB4E4B33E) >> 128; + if (x & 0x4000000000000000000000 > 0) result = (result * 0x100000000002C5C85FDF477B662B26946) >> 128; + if (x & 0x2000000000000000000000 > 0) result = (result * 0x10000000000162E42FEFA3AE53369388D) >> 128; + if (x & 0x1000000000000000000000 > 0) result = (result * 0x100000000000B17217F7D1D351A389D41) >> 128; + if (x & 0x800000000000000000000 > 0) result = (result * 0x10000000000058B90BFBE8E8B2D3D4EDF) >> 128; + if (x & 0x400000000000000000000 > 0) result = (result * 0x1000000000002C5C85FDF4741BEA6E77F) >> 128; + if (x & 0x200000000000000000000 > 0) result = (result * 0x100000000000162E42FEFA39FE95583C3) >> 128; + if (x & 0x100000000000000000000 > 0) result = (result * 0x1000000000000B17217F7D1CFB72B45E3) >> 128; + if (x & 0x80000000000000000000 > 0) result = (result * 0x100000000000058B90BFBE8E7CC35C3F2) >> 128; + if (x & 0x40000000000000000000 > 0) result = (result * 0x10000000000002C5C85FDF473E242EA39) >> 128; + if (x & 0x20000000000000000000 > 0) result = (result * 0x1000000000000162E42FEFA39F02B772C) >> 128; + if (x & 0x10000000000000000000 > 0) result = (result * 0x10000000000000B17217F7D1CF7D83C1A) >> 128; + if (x & 0x8000000000000000000 > 0) result = (result * 0x1000000000000058B90BFBE8E7BDCBE2E) >> 128; + if (x & 0x4000000000000000000 > 0) result = (result * 0x100000000000002C5C85FDF473DEA871F) >> 128; + if (x & 0x2000000000000000000 > 0) result = (result * 0x10000000000000162E42FEFA39EF44D92) >> 128; + if (x & 0x1000000000000000000 > 0) result = (result * 0x100000000000000B17217F7D1CF79E949) >> 128; + if (x & 0x800000000000000000 > 0) result = (result * 0x10000000000000058B90BFBE8E7BCE545) >> 128; + if (x & 0x400000000000000000 > 0) result = (result * 0x1000000000000002C5C85FDF473DE6ECA) >> 128; + if (x & 0x200000000000000000 > 0) result = (result * 0x100000000000000162E42FEFA39EF366F) >> 128; + if (x & 0x100000000000000000 > 0) result = (result * 0x1000000000000000B17217F7D1CF79AFA) >> 128; + if (x & 0x80000000000000000 > 0) result = (result * 0x100000000000000058B90BFBE8E7BCD6E) >> 128; + if (x & 0x40000000000000000 > 0) result = (result * 0x10000000000000002C5C85FDF473DE6B3) >> 128; + if (x & 0x20000000000000000 > 0) result = (result * 0x1000000000000000162E42FEFA39EF359) >> 128; + if (x & 0x10000000000000000 > 0) result = (result * 0x10000000000000000B17217F7D1CF79AC) >> 128; + + // We do two things at the same time below: + // + // 1. Multiply the result by 2^n + 1, where 2^n is the integer part and 1 is an extra bit to account + // for the fact that we initially set the result to 0.5 We implement this by subtracting from 127 + // instead of 128. + // 2. Convert the result to the unsigned 60.18-decimal fixed-point format. + // + // This works because result * SCALE * 2^ip / 2^127 = result * SCALE / 2^(127 - ip), where ip is the integer + // part and SCALE / 2^128 is what converts the result to our unsigned fixed-point format. + result *= SCALE; + result >>= (127 - (x >> 128)); + } + } + + /// @notice Finds the zero-based index of the first one in the binary representation of x. + /// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set + /// @param x The uint256 number for which to find the index of the most significant bit. + /// @return msb The index of the most significant bit as an uint256. + function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) { + if (x >= 2**128) { + x >>= 128; + msb += 128; + } + if (x >= 2**64) { + x >>= 64; + msb += 64; + } + if (x >= 2**32) { + x >>= 32; + msb += 32; + } + if (x >= 2**16) { + x >>= 16; + msb += 16; + } + if (x >= 2**8) { + x >>= 8; + msb += 8; + } + if (x >= 2**4) { + x >>= 4; + msb += 4; + } + if (x >= 2**2) { + x >>= 2; + msb += 2; + } + if (x >= 2**1) { + // No need to shift x any more. + msb += 1; + } + } + + /// @notice Calculates floor(x*y÷denominator) with full precision. + /// + /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv. + /// + /// Requirements: + /// - The denominator cannot be zero. + /// - The result must fit within uint256. + /// + /// Caveats: + /// - This function does not work with fixed-point numbers. + /// + /// @param x The multiplicand as an uint256. + /// @param y The multiplier as an uint256. + /// @param denominator The divisor as an uint256. + /// @return result The result as an uint256. + function mulDiv( + uint256 x, + uint256 y, + uint256 denominator + ) internal pure returns (uint256 result) { + // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2**256 and mod 2**256 - 1, then use + // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 + // variables such that product = prod1 * 2**256 + prod0. + uint256 prod0; // Least significant 256 bits of the product + uint256 prod1; // Most significant 256 bits of the product + assembly { + let mm := mulmod(x, y, not(0)) + prod0 := mul(x, y) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + // Handle non-overflow cases, 256 by 256 division + if (prod1 == 0) { + require(denominator > 0); + assembly { + result := div(prod0, denominator) + } + return result; + } + + // Make sure the result is less than 2**256. Also prevents denominator == 0. + require(denominator > prod1); + + /////////////////////////////////////////////// + // 512 by 256 division. + /////////////////////////////////////////////// + + // Make division exact by subtracting the remainder from [prod1 prod0]. + uint256 remainder; + assembly { + // Compute remainder using mulmod. + remainder := mulmod(x, y, denominator) + + // Subtract 256 bit number from 512 bit number + prod1 := sub(prod1, gt(remainder, prod0)) + prod0 := sub(prod0, remainder) + } + + // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. + // See https://cs.stackexchange.com/q/138556/92363. + unchecked { + // Does not overflow because the denominator cannot be zero at this stage in the function. + uint256 lpotdod = denominator & (~denominator + 1); + assembly { + // Divide denominator by lpotdod. + denominator := div(denominator, lpotdod) + + // Divide [prod1 prod0] by lpotdod. + prod0 := div(prod0, lpotdod) + + // Flip lpotdod such that it is 2**256 / lpotdod. If lpotdod is zero, then it becomes one. + lpotdod := add(div(sub(0, lpotdod), lpotdod), 1) + } + + // Shift in bits from prod1 into prod0. + prod0 |= prod1 * lpotdod; + + // Invert denominator mod 2**256. Now that denominator is an odd number, it has an inverse modulo 2**256 such + // that denominator * inv = 1 mod 2**256. Compute the inverse by starting with a seed that is correct for + // four bits. That is, denominator * inv = 1 mod 2**4 + uint256 inverse = (3 * denominator) ^ 2; + + // Now use Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works + // in modular arithmetic, doubling the correct bits in each step. + inverse *= 2 - denominator * inverse; // inverse mod 2**8 + inverse *= 2 - denominator * inverse; // inverse mod 2**16 + inverse *= 2 - denominator * inverse; // inverse mod 2**32 + inverse *= 2 - denominator * inverse; // inverse mod 2**64 + inverse *= 2 - denominator * inverse; // inverse mod 2**128 + inverse *= 2 - denominator * inverse; // inverse mod 2**256 + + // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. + // This will give us the correct result modulo 2**256. Since the precoditions guarantee that the outcome is + // less than 2**256, this is the final result. We don't need to compute the high bits of the result and prod1 + // is no longer required. + result = prod0 * inverse; + return result; + } + } + + /// @notice Calculates floor(x*y÷1e18) with full precision. + /// + /// @dev Variant of "mulDiv" with constant folding, i.e. in which the denominator is always 1e18. Before returning the + /// final result, we add 1 if (x * y) % SCALE >= HALF_SCALE. Without this, 6.6e-19 would be truncated to 0 instead of + /// being rounded to 1e-18. See "Listing 6" and text above it at https://accu.org/index.php/journals/1717. + /// + /// Requirements: + /// - The result must fit within uint256. + /// + /// Caveats: + /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMathCommon.mulDiv" to understand how this works. + /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two queations: + /// 1. x * y = type(uint256).max * SCALE + /// 2. (x * y) % SCALE >= SCALE / 2 + /// + /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number. + /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number. + /// @return result The result as an unsigned 60.18-decimal fixed-point number. + function mulDivFixedPoint(uint256 x, uint256 y) internal pure returns (uint256 result) { + uint256 prod0; + uint256 prod1; + assembly { + let mm := mulmod(x, y, not(0)) + prod0 := mul(x, y) + prod1 := sub(sub(mm, prod0), lt(mm, prod0)) + } + + uint256 remainder; + uint256 roundUpUnit; + assembly { + remainder := mulmod(x, y, SCALE) + roundUpUnit := gt(remainder, 499999999999999999) + } + + if (prod1 == 0) { + unchecked { + result = (prod0 / SCALE) + roundUpUnit; + return result; + } + } + + require(SCALE > prod1); + + assembly { + result := add( + mul( + or( + div(sub(prod0, remainder), SCALE_LPOTD), + mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, SCALE_LPOTD), SCALE_LPOTD), 1)) + ), + SCALE_INVERSE + ), + roundUpUnit + ) + } + } + + /// @notice Calculates the square root of x, rounding down. + /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method. + /// + /// Caveats: + /// - This function does not work with fixed-point numbers. + /// + /// @param x The uint256 number for which to calculate the square root. + /// @return result The result as an uint256. + function sqrt(uint256 x) internal pure returns (uint256 result) { + if (x == 0) { + return 0; + } + + // Calculate the square root of the perfect square of a power of two that is the closest to x. + uint256 xAux = uint256(x); + result = 1; + if (xAux >= 0x100000000000000000000000000000000) { + xAux >>= 128; + result <<= 64; + } + if (xAux >= 0x10000000000000000) { + xAux >>= 64; + result <<= 32; + } + if (xAux >= 0x100000000) { + xAux >>= 32; + result <<= 16; + } + if (xAux >= 0x10000) { + xAux >>= 16; + result <<= 8; + } + if (xAux >= 0x100) { + xAux >>= 8; + result <<= 4; + } + if (xAux >= 0x10) { + xAux >>= 4; + result <<= 2; + } + if (xAux >= 0x8) { + result <<= 1; + } + + // The operations can never overflow because the result is max 2^127 when it enters this block. + unchecked { + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; + result = (result + x / result) >> 1; // Seven iterations should be enough + uint256 roundedDownResult = x / result; + return result >= roundedDownResult ? roundedDownResult : result; + } + } +} diff --git a/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathSD59x18.sol b/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathSD59x18.sol new file mode 100644 index 000000000..9f3a10942 --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathSD59x18.sol @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: WTFPL +pragma solidity >=0.8.0; + +import "./PRBMathCommon.sol"; + +/// @title PRBMathSD59x18 +/// @author Paul Razvan Berg +/// @notice Smart contract library for advanced fixed-point math. It works with int256 numbers considered to have 18 +/// trailing decimals. We call this number representation signed 59.18-decimal fixed-point, since the numbers can have +/// a sign and there can be up to 59 digits in the integer part and up to 18 decimals in the fractional part. The numbers +/// are bound by the minimum and the maximum values permitted by the Solidity type int256. +library PRBMathSD59x18 { + /// @dev log2(e) as a signed 59.18-decimal fixed-point number. + int256 internal constant LOG2_E = 1442695040888963407; + + /// @dev Half the SCALE number. + int256 internal constant HALF_SCALE = 5e17; + + /// @dev The maximum value a signed 59.18-decimal fixed-point number can have. + int256 internal constant MAX_SD59x18 = 57896044618658097711785492504343953926634992332820282019728792003956564819967; + + /// @dev The maximum whole value a signed 59.18-decimal fixed-point number can have. + int256 internal constant MAX_WHOLE_SD59x18 = 57896044618658097711785492504343953926634992332820282019728000000000000000000; + + /// @dev The minimum value a signed 59.18-decimal fixed-point number can have. + int256 internal constant MIN_SD59x18 = -57896044618658097711785492504343953926634992332820282019728792003956564819968; + + /// @dev The minimum whole value a signed 59.18-decimal fixed-point number can have. + int256 internal constant MIN_WHOLE_SD59x18 = -57896044618658097711785492504343953926634992332820282019728000000000000000000; + + /// @dev How many trailing decimals can be represented. + int256 internal constant SCALE = 1e18; + + /// INTERNAL FUNCTIONS /// + + /// @notice Calculate the absolute value of x. + /// + /// @dev Requirements: + /// - x must be greater than MIN_SD59x18. + /// + /// @param x The number to calculate the absolute value for. + /// @param result The absolute value of x. + function abs(int256 x) internal pure returns (int256 result) { + unchecked { + require(x > MIN_SD59x18); + result = x < 0 ? -x : x; + } + } + + /// @notice Calculates arithmetic average of x and y, rounding down. + /// @param x The first operand as a signed 59.18-decimal fixed-point number. + /// @param y The second operand as a signed 59.18-decimal fixed-point number. + /// @return result The arithmetic average as a signed 59.18-decimal fixed-point number. + function avg(int256 x, int256 y) internal pure returns (int256 result) { + // The operations can never overflow. + unchecked { + // The last operand checks if both x and y are odd and if that is the case, we add 1 to the result. We need + // to do this because if both numbers are odd, the 0.5 remainder gets truncated twice. + result = (x >> 1) + (y >> 1) + (x & y & 1); + } + } + + /// @notice Yields the least greatest signed 59.18 decimal fixed-point number greater than or equal to x. + /// + /// @dev Optimised for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts. + /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions. + /// + /// Requirements: + /// - x must be less than or equal to MAX_WHOLE_SD59x18. + /// + /// @param x The signed 59.18-decimal fixed-point number to ceil. + /// @param result The least integer greater than or equal to x, as a signed 58.18-decimal fixed-point number. + function ceil(int256 x) internal pure returns (int256 result) { + require(x <= MAX_WHOLE_SD59x18); + unchecked { + int256 remainder = x % SCALE; + if (remainder == 0) { + result = x; + } else { + // Solidity uses C fmod style, which returns a modulus with the same sign as x. + result = x - remainder; + if (x > 0) { + result += SCALE; + } + } + } + } + + /// @notice Divides two signed 59.18-decimal fixed-point numbers, returning a new signed 59.18-decimal fixed-point number. + /// + /// @dev Variant of "mulDiv" that works with signed numbers. Works by computing the signs and the absolute values separately. + /// + /// Requirements: + /// - All from "PRBMathCommon.mulDiv". + /// - None of the inputs can be type(int256).min. + /// - y cannot be zero. + /// - The result must fit within int256. + /// + /// Caveats: + /// - All from "PRBMathCommon.mulDiv". + /// + /// @param x The numerator as a signed 59.18-decimal fixed-point number. + /// @param y The denominator as a signed 59.18-decimal fixed-point number. + /// @param result The quotient as a signed 59.18-decimal fixed-point number. + function div(int256 x, int256 y) internal pure returns (int256 result) { + require(x > type(int256).min); + require(y > type(int256).min); + + // Get hold of the absolute values of x and y. + uint256 ax; + uint256 ay; + unchecked { + ax = x < 0 ? uint256(-x) : uint256(x); + ay = y < 0 ? uint256(-y) : uint256(y); + } + + // Compute the absolute value of (x*SCALE)÷y. The result must fit within int256. + uint256 resultUnsigned = PRBMathCommon.mulDiv(ax, uint256(SCALE), ay); + require(resultUnsigned <= uint256(type(int256).max)); + + // Get the signs of x and y. + uint256 sx; + uint256 sy; + assembly { + sx := sgt(x, sub(0, 1)) + sy := sgt(y, sub(0, 1)) + } + + // XOR over sx and sy. This is basically checking whether the inputs have the same sign. If yes, the result + // should be positive. Otherwise, it should be negative. + result = sx ^ sy == 1 ? -int256(resultUnsigned) : int256(resultUnsigned); + } + + /// @notice Returns Euler's number as a signed 59.18-decimal fixed-point number. + /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant). + function e() internal pure returns (int256 result) { + result = 2718281828459045235; + } + + /// @notice Calculates the natural exponent of x. + /// + /// @dev Based on the insight that e^x = 2^(x * log2(e)). + /// + /// Requirements: + /// - All from "log2". + /// - x must be less than 88722839111672999628. + /// + /// @param x The exponent as a signed 59.18-decimal fixed-point number. + /// @return result The result as a signed 59.18-decimal fixed-point number. + function exp(int256 x) internal pure returns (int256 result) { + // Without this check, the value passed to "exp2" would be less than -59794705707972522261. + if (x < -41446531673892822322) { + return 0; + } + + // Without this check, the value passed to "exp2" would be greater than 128e18. + require(x < 88722839111672999628); + + // Do the fixed-point multiplication inline to save gas. + unchecked { + int256 doubleScaleProduct = x * LOG2_E; + result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE); + } + } + + /// @notice Calculates the binary exponent of x using the binary fraction method. + /// + /// @dev See https://ethereum.stackexchange.com/q/79903/24693. + /// + /// Requirements: + /// - x must be 128e18 or less. + /// - The result must fit within MAX_SD59x18. + /// + /// Caveats: + /// - For any x less than -59794705707972522261, the result is zero. + /// + /// @param x The exponent as a signed 59.18-decimal fixed-point number. + /// @return result The result as a signed 59.18-decimal fixed-point number. + function exp2(int256 x) internal pure returns (int256 result) { + // This works because 2^(-x) = 1/2^x. + if (x < 0) { + // 2**59.794705707972522262 is the maximum number whose inverse does not turn into zero. + if (x < -59794705707972522261) { + return 0; + } + + // Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE. + unchecked { result = 1e36 / exp2(-x); } + } else { + // 2**128 doesn't fit within the 128.128-bit fixed-point representation. + require(x < 128e18); + + unchecked { + // Convert x to the 128.128-bit fixed-point format. + uint256 x128x128 = (uint256(x) << 128) / uint256(SCALE); + + // Safe to convert the result to int256 directly because the maximum input allowed is 128e18. + result = int256(PRBMathCommon.exp2(x128x128)); + } + } + } + + /// @notice Yields the greatest signed 59.18 decimal fixed-point number less than or equal to x. + /// + /// @dev Optimised for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts. + /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions. + /// + /// Requirements: + /// - x must be greater than or equal to MIN_WHOLE_SD59x18. + /// + /// @param x The signed 59.18-decimal fixed-point number to floor. + /// @param result The greatest integer less than or equal to x, as a signed 58.18-decimal fixed-point number. + function floor(int256 x) internal pure returns (int256 result) { + require(x >= MIN_WHOLE_SD59x18); + unchecked { + int256 remainder = x % SCALE; + if (remainder == 0) { + result = x; + } else { + // Solidity uses C fmod style, which returns a modulus with the same sign as x. + result = x - remainder; + if (x < 0) { + result -= SCALE; + } + } + } + } + + /// @notice Yields the excess beyond the floor of x for positive numbers and the part of the number to the right + /// of the radix point for negative numbers. + /// @dev Based on the odd function definition. https://en.wikipedia.org/wiki/Fractional_part + /// @param x The signed 59.18-decimal fixed-point number to get the fractional part of. + /// @param result The fractional part of x as a signed 59.18-decimal fixed-point number. + function frac(int256 x) internal pure returns (int256 result) { + unchecked { result = x % SCALE; } + } + + /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down. + /// + /// @dev Requirements: + /// - x * y must fit within MAX_SD59x18, lest it overflows. + /// - x * y cannot be negative. + /// + /// @param x The first operand as a signed 59.18-decimal fixed-point number. + /// @param y The second operand as a signed 59.18-decimal fixed-point number. + /// @return result The result as a signed 59.18-decimal fixed-point number. + function gm(int256 x, int256 y) internal pure returns (int256 result) { + if (x == 0) { + return 0; + } + + unchecked { + // Checking for overflow this way is faster than letting Solidity do it. + int256 xy = x * y; + require(xy / x == y); + + // The product cannot be negative. + require(xy >= 0); + + // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE + // during multiplication. See the comments within the "sqrt" function. + result = int256(PRBMathCommon.sqrt(uint256(xy))); + } + } + + /// @notice Calculates 1 / x, rounding towards zero. + /// + /// @dev Requirements: + /// - x cannot be zero. + /// + /// @param x The signed 59.18-decimal fixed-point number for which to calculate the inverse. + /// @return result The inverse as a signed 59.18-decimal fixed-point number. + function inv(int256 x) internal pure returns (int256 result) { + unchecked { + // 1e36 is SCALE * SCALE. + result = 1e36 / x; + } + } + + /// @notice Calculates the natural logarithm of x. + /// + /// @dev Based on the insight that ln(x) = log2(x) / log2(e). + /// + /// Requirements: + /// - All from "log2". + /// + /// Caveats: + /// - All from "log2". + /// - This doesn't return exactly 1 for 2718281828459045235, for that we would need more fine-grained precision. + /// + /// @param x The signed 59.18-decimal fixed-point number for which to calculate the natural logarithm. + /// @return result The natural logarithm as a signed 59.18-decimal fixed-point number. + function ln(int256 x) internal pure returns (int256 result) { + // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x) + // can return is 195205294292027477728. + unchecked { result = (log2(x) * SCALE) / LOG2_E; } + } + + /// @notice Calculates the common logarithm of x. + /// + /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common + /// logarithm based on the insight that log10(x) = log2(x) / log2(10). + /// + /// Requirements: + /// - All from "log2". + /// + /// Caveats: + /// - All from "log2". + /// + /// @param x The signed 59.18-decimal fixed-point number for which to calculate the common logarithm. + /// @return result The common logarithm as a signed 59.18-decimal fixed-point number. + function log10(int256 x) internal pure returns (int256 result) { + require(x > 0); + + // Note that the "mul" in this block is the assembly mul operation, not the "mul" function defined in this contract. + // prettier-ignore + assembly { + switch x + case 1 { result := mul(SCALE, sub(0, 18)) } + case 10 { result := mul(SCALE, sub(1, 18)) } + case 100 { result := mul(SCALE, sub(2, 18)) } + case 1000 { result := mul(SCALE, sub(3, 18)) } + case 10000 { result := mul(SCALE, sub(4, 18)) } + case 100000 { result := mul(SCALE, sub(5, 18)) } + case 1000000 { result := mul(SCALE, sub(6, 18)) } + case 10000000 { result := mul(SCALE, sub(7, 18)) } + case 100000000 { result := mul(SCALE, sub(8, 18)) } + case 1000000000 { result := mul(SCALE, sub(9, 18)) } + case 10000000000 { result := mul(SCALE, sub(10, 18)) } + case 100000000000 { result := mul(SCALE, sub(11, 18)) } + case 1000000000000 { result := mul(SCALE, sub(12, 18)) } + case 10000000000000 { result := mul(SCALE, sub(13, 18)) } + case 100000000000000 { result := mul(SCALE, sub(14, 18)) } + case 1000000000000000 { result := mul(SCALE, sub(15, 18)) } + case 10000000000000000 { result := mul(SCALE, sub(16, 18)) } + case 100000000000000000 { result := mul(SCALE, sub(17, 18)) } + case 1000000000000000000 { result := 0 } + case 10000000000000000000 { result := SCALE } + case 100000000000000000000 { result := mul(SCALE, 2) } + case 1000000000000000000000 { result := mul(SCALE, 3) } + case 10000000000000000000000 { result := mul(SCALE, 4) } + case 100000000000000000000000 { result := mul(SCALE, 5) } + case 1000000000000000000000000 { result := mul(SCALE, 6) } + case 10000000000000000000000000 { result := mul(SCALE, 7) } + case 100000000000000000000000000 { result := mul(SCALE, 8) } + case 1000000000000000000000000000 { result := mul(SCALE, 9) } + case 10000000000000000000000000000 { result := mul(SCALE, 10) } + case 100000000000000000000000000000 { result := mul(SCALE, 11) } + case 1000000000000000000000000000000 { result := mul(SCALE, 12) } + case 10000000000000000000000000000000 { result := mul(SCALE, 13) } + case 100000000000000000000000000000000 { result := mul(SCALE, 14) } + case 1000000000000000000000000000000000 { result := mul(SCALE, 15) } + case 10000000000000000000000000000000000 { result := mul(SCALE, 16) } + case 100000000000000000000000000000000000 { result := mul(SCALE, 17) } + case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) } + case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) } + case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) } + case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) } + case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) } + case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) } + case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) } + case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) } + case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) } + case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) } + case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) } + case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) } + case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) } + case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) } + case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) } + case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) } + case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) } + case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) } + case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) } + case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) } + case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) } + case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) } + case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) } + case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) } + case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) } + case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) } + case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) } + case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) } + case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) } + case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) } + case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) } + case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) } + case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) } + case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) } + case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) } + case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) } + case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) } + case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) } + case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) } + case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) } + case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) } + default { + result := MAX_SD59x18 + } + } + + if (result == MAX_SD59x18) { + // Do the fixed-point division inline to save gas. The denominator is log2(10). + unchecked { result = (log2(x) * SCALE) / 332192809488736234; } + } + } + + /// @notice Calculates the binary logarithm of x. + /// + /// @dev Based on the iterative approximation algorithm. + /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation + /// + /// Requirements: + /// - x must be greater than zero. + /// + /// Caveats: + /// - The results are nor perfectly accurate to the last digit, due to the lossy precision of the iterative approximation. + /// + /// @param x The signed 59.18-decimal fixed-point number for which to calculate the binary logarithm. + /// @return result The binary logarithm as a signed 59.18-decimal fixed-point number. + function log2(int256 x) internal pure returns (int256 result) { + require(x > 0); + unchecked { + // This works because log2(x) = -log2(1/x). + int256 sign; + if (x >= SCALE) { + sign = 1; + } else { + sign = -1; + // Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE. + assembly { + x := div(1000000000000000000000000000000000000, x) + } + } + + // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n). + uint256 n = PRBMathCommon.mostSignificantBit(uint256(x / SCALE)); + + // The integer part of the logarithm as a signed 59.18-decimal fixed-point number. The operation can't overflow + // because n is maximum 255, SCALE is 1e18 and sign is either 1 or -1. + result = int256(n) * SCALE; + + // This is y = x * 2^(-n). + int256 y = x >> n; + + // If y = 1, the fractional part is zero. + if (y == SCALE) { + return result * sign; + } + + // Calculate the fractional part via the iterative approximation. + // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster. + for (int256 delta = int256(HALF_SCALE); delta > 0; delta >>= 1) { + y = (y * y) / SCALE; + + // Is y^2 > 2 and so in the range [2,4)? + if (y >= 2 * SCALE) { + // Add the 2^(-m) factor to the logarithm. + result += delta; + + // Corresponds to z/2 on Wikipedia. + y >>= 1; + } + } + result *= sign; + } + } + + /// @notice Multiplies two signed 59.18-decimal fixed-point numbers together, returning a new signed 59.18-decimal + /// fixed-point number. + /// + /// @dev Variant of "mulDiv" that works with signed numbers and employs constant folding, i.e. the denominator is + /// alawys 1e18. + /// + /// Requirements: + /// - All from "PRBMathCommon.mulDivFixedPoint". + /// - The result must fit within MAX_SD59x18. + /// + /// Caveats: + /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMathCommon.mulDiv" to understand how this works. + /// + /// @param x The multiplicand as a signed 59.18-decimal fixed-point number. + /// @param y The multiplier as a signed 59.18-decimal fixed-point number. + /// @return result The result as a signed 59.18-decimal fixed-point number. + function mul(int256 x, int256 y) internal pure returns (int256 result) { + require(x > MIN_SD59x18); + require(y > MIN_SD59x18); + + unchecked { + uint256 ax; + uint256 ay; + ax = x < 0 ? uint256(-x) : uint256(x); + ay = y < 0 ? uint256(-y) : uint256(y); + + uint256 resultUnsigned = PRBMathCommon.mulDivFixedPoint(ax, ay); + require(resultUnsigned <= uint256(MAX_SD59x18)); + + uint256 sx; + uint256 sy; + assembly { + sx := sgt(x, sub(0, 1)) + sy := sgt(y, sub(0, 1)) + } + result = sx ^ sy == 1 ? -int256(resultUnsigned) : int256(resultUnsigned); + } + } + + /// @notice Retrieves PI as a signed 59.18-decimal fixed-point number. + function pi() internal pure returns (int256 result) { + result = 3141592653589793238; + } + + /// @notice Raises x (signed 59.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the + /// famous algorithm "exponentiation by squaring". + /// + /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring + /// + /// Requirements: + /// - All from "abs" and "PRBMathCommon.mulDivFixedPoint". + /// - The result must fit within MAX_SD59x18. + /// + /// Caveats: + /// - All from "PRBMathCommon.mulDivFixedPoint". + /// - Assumes 0^0 is 1. + /// + /// @param x The base as a signed 59.18-decimal fixed-point number. + /// @param y The exponent as an uint256. + /// @return result The result as a signed 59.18-decimal fixed-point number. + function pow(int256 x, uint256 y) internal pure returns (int256 result) { + uint256 absX = uint256(abs(x)); + + // Calculate the first iteration of the loop in advance. + uint256 absResult = y & 1 > 0 ? absX : uint256(SCALE); + + // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster. + for (y >>= 1; y > 0; y >>= 1) { + absX = PRBMathCommon.mulDivFixedPoint(absX, absX); + + // Equivalent to "y % 2 == 1" but faster. + if (y & 1 > 0) { + absResult = PRBMathCommon.mulDivFixedPoint(absResult, absX); + } + } + + // The result must fit within the 59.18-decimal fixed-point representation. + require(absResult <= uint256(MAX_SD59x18)); + + // Is the base negative and the exponent an odd number? + bool isNegative = x < 0 && y & 1 == 1; + result = isNegative ? -int256(absResult) : int256(absResult); + } + + /// @notice Returns 1 as a signed 59.18-decimal fixed-point number. + function scale() internal pure returns (int256 result) { + result = SCALE; + } + + /// @notice Calculates the square root of x, rounding down. + /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method. + /// + /// Requirements: + /// - x cannot be negative. + /// - x must be less than MAX_SD59x18 / SCALE. + /// + /// Caveats: + /// - The maximum fixed-point number permitted is 57896044618658097711785492504343953926634.992332820282019729. + /// + /// @param x The signed 59.18-decimal fixed-point number for which to calculate the square root. + /// @return result The result as a signed 59.18-decimal fixed-point . + function sqrt(int256 x) internal pure returns (int256 result) { + require(x >= 0); + require(x < 57896044618658097711785492504343953926634992332820282019729); + unchecked { + // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two signed + // 59.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root). + result = int256(PRBMathCommon.sqrt(uint256(x * SCALE))); + } + } +} diff --git a/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathUD60x18.sol b/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathUD60x18.sol new file mode 100644 index 000000000..2732d95b6 --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/_prbmath/PRBMathUD60x18.sol @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: WTFPL +pragma solidity >=0.8.0; + +import "./PRBMathCommon.sol"; + +/// @title PRBMathUD60x18 +/// @author Paul Razvan Berg +/// @notice Smart contract library for advanced fixed-point math. It works with uint256 numbers considered to have 18 +/// trailing decimals. We call this number representation unsigned 60.18-decimal fixed-point, since there can be up to 60 +/// digits in the integer part and up to 18 decimals in the fractional part. The numbers are bound by the minimum and the +/// maximum values permitted by the Solidity type uint256. +library PRBMathUD60x18 { + /// @dev Half the SCALE number. + uint256 internal constant HALF_SCALE = 5e17; + + /// @dev log2(e) as an unsigned 60.18-decimal fixed-point number. + uint256 internal constant LOG2_E = 1442695040888963407; + + /// @dev The maximum value an unsigned 60.18-decimal fixed-point number can have. + uint256 internal constant MAX_UD60x18 = 115792089237316195423570985008687907853269984665640564039457584007913129639935; + + /// @dev The maximum whole value an unsigned 60.18-decimal fixed-point number can have. + uint256 internal constant MAX_WHOLE_UD60x18 = 115792089237316195423570985008687907853269984665640564039457000000000000000000; + + /// @dev How many trailing decimals can be represented. + uint256 internal constant SCALE = 1e18; + + /// @notice Calculates arithmetic average of x and y, rounding down. + /// @param x The first operand as an unsigned 60.18-decimal fixed-point number. + /// @param y The second operand as an unsigned 60.18-decimal fixed-point number. + /// @return result The arithmetic average as an usigned 60.18-decimal fixed-point number. + function avg(uint256 x, uint256 y) internal pure returns (uint256 result) { + // The operations can never overflow. + unchecked { + // The last operand checks if both x and y are odd and if that is the case, we add 1 to the result. We need + // to do this because if both numbers are odd, the 0.5 remainder gets truncated twice. + result = (x >> 1) + (y >> 1) + (x & y & 1); + } + } + + /// @notice Yields the least unsigned 60.18 decimal fixed-point number greater than or equal to x. + /// + /// @dev Optimised for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts. + /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions. + /// + /// Requirements: + /// - x must be less than or equal to MAX_WHOLE_UD60x18. + /// + /// @param x The unsigned 60.18-decimal fixed-point number to ceil. + /// @param result The least integer greater than or equal to x, as an unsigned 60.18-decimal fixed-point number. + function ceil(uint256 x) internal pure returns (uint256 result) { + require(x <= MAX_WHOLE_UD60x18); + assembly { + // Equivalent to "x % SCALE" but faster. + let remainder := mod(x, SCALE) + + // Equivalent to "SCALE - remainder" but faster. + let delta := sub(SCALE, remainder) + + // Equivalent to "x + delta * (remainder > 0 ? 1 : 0)" but faster. + result := add(x, mul(delta, gt(remainder, 0))) + } + } + + /// @notice Divides two unsigned 60.18-decimal fixed-point numbers, returning a new unsigned 60.18-decimal fixed-point number. + /// + /// @dev Uses mulDiv to enable overflow-safe multiplication and division. + /// + /// Requirements: + /// - y cannot be zero. + /// + /// @param x The numerator as an unsigned 60.18-decimal fixed-point number. + /// @param y The denominator as an unsigned 60.18-decimal fixed-point number. + /// @param result The quotient as an unsigned 60.18-decimal fixed-point number. + function div(uint256 x, uint256 y) internal pure returns (uint256 result) { + result = PRBMathCommon.mulDiv(x, SCALE, y); + } + + /// @notice Returns Euler's number as an unsigned 60.18-decimal fixed-point number. + /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant). + function e() internal pure returns (uint256 result) { + result = 2718281828459045235; + } + + /// @notice Calculates the natural exponent of x. + /// + /// @dev Based on the insight that e^x = 2^(x * log2(e)). + /// + /// Requirements: + /// - All from "log2". + /// - x must be less than 88722839111672999628. + /// + /// @param x The exponent as an unsigned 60.18-decimal fixed-point number. + /// @return result The result as an unsigned 60.18-decimal fixed-point number. + function exp(uint256 x) internal pure returns (uint256 result) { + // Without this check, the value passed to "exp2" would be greater than 128e18. + require(x < 88722839111672999628); + + // Do the fixed-point multiplication inline to save gas. + unchecked { + uint256 doubleScaleProduct = x * LOG2_E; + result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE); + } + } + + /// @notice Calculates the binary exponent of x using the binary fraction method. + /// + /// @dev See https://ethereum.stackexchange.com/q/79903/24693. + /// + /// Requirements: + /// - x must be 128e18 or less. + /// - The result must fit within MAX_UD60x18. + /// + /// @param x The exponent as an unsigned 60.18-decimal fixed-point number. + /// @return result The result as an unsigned 60.18-decimal fixed-point number. + function exp2(uint256 x) internal pure returns (uint256 result) { + // 2**128 doesn't fit within the 128.128-bit format used internally in this function. + require(x < 128e18); + + unchecked { + // Convert x to the 128.128-bit fixed-point format. + uint256 x128x128 = (x << 128) / SCALE; + + // Pass x to the PRBMathCommon.exp2 function, which uses the 128.128-bit fixed-point number representation. + result = PRBMathCommon.exp2(x128x128); + } + } + + /// @notice Yields the greatest unsigned 60.18 decimal fixed-point number less than or equal to x. + /// @dev Optimised for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts. + /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions. + /// @param x The unsigned 60.18-decimal fixed-point number to floor. + /// @param result The greatest integer less than or equal to x, as an unsigned 60.18-decimal fixed-point number. + function floor(uint256 x) internal pure returns (uint256 result) { + assembly { + // Equivalent to "x % SCALE" but faster. + let remainder := mod(x, SCALE) + + // Equivalent to "x - remainder * (remainder > 0 ? 1 : 0)" but faster. + result := sub(x, mul(remainder, gt(remainder, 0))) + } + } + + /// @notice Yields the excess beyond the floor of x. + /// @dev Based on the odd function definition https://en.wikipedia.org/wiki/Fractional_part. + /// @param x The unsigned 60.18-decimal fixed-point number to get the fractional part of. + /// @param result The fractional part of x as an unsigned 60.18-decimal fixed-point number. + function frac(uint256 x) internal pure returns (uint256 result) { + assembly { + result := mod(x, SCALE) + } + } + + /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down. + /// + /// @dev Requirements: + /// - x * y must fit within MAX_UD60x18, lest it overflows. + /// + /// @param x The first operand as an unsigned 60.18-decimal fixed-point number. + /// @param y The second operand as an unsigned 60.18-decimal fixed-point number. + /// @return result The result as an unsigned 60.18-decimal fixed-point number. + function gm(uint256 x, uint256 y) internal pure returns (uint256 result) { + if (x == 0) { + return 0; + } + + unchecked { + // Checking for overflow this way is faster than letting Solidity do it. + uint256 xy = x * y; + require(xy / x == y); + + // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE + // during multiplication. See the comments within the "sqrt" function. + result = PRBMathCommon.sqrt(xy); + } + } + + /// @notice Calculates 1 / x, rounding towards zero. + /// + /// @dev Requirements: + /// - x cannot be zero. + /// + /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the inverse. + /// @return result The inverse as an unsigned 60.18-decimal fixed-point number. + function inv(uint256 x) internal pure returns (uint256 result) { + unchecked { + // 1e36 is SCALE * SCALE. + result = 1e36 / x; + } + } + + /// @notice Calculates the natural logarithm of x. + /// + /// @dev Based on the insight that ln(x) = log2(x) / log2(e). + /// + /// Requirements: + /// - All from "log2". + /// + /// Caveats: + /// - All from "log2". + /// - This doesn't return exactly 1 for 2718281828459045235, for that we would need more fine-grained precision. + /// + /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the natural logarithm. + /// @return result The natural logarithm as an unsigned 60.18-decimal fixed-point number. + function ln(uint256 x) internal pure returns (uint256 result) { + // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x) + // can return is 196205294292027477728. + unchecked { result = (log2(x) * SCALE) / LOG2_E; } + } + + /// @notice Calculates the common logarithm of x. + /// + /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common + /// logarithm based on the insight that log10(x) = log2(x) / log2(10). + /// + /// Requirements: + /// - All from "log2". + /// + /// Caveats: + /// - All from "log2". + /// + /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the common logarithm. + /// @return result The common logarithm as an unsigned 60.18-decimal fixed-point number. + function log10(uint256 x) internal pure returns (uint256 result) { + require(x >= SCALE); + + // Note that the "mul" in this block is the assembly mul operation, not the "mul" function defined in this contract. + // prettier-ignore + assembly { + switch x + case 1 { result := mul(SCALE, sub(0, 18)) } + case 10 { result := mul(SCALE, sub(1, 18)) } + case 100 { result := mul(SCALE, sub(2, 18)) } + case 1000 { result := mul(SCALE, sub(3, 18)) } + case 10000 { result := mul(SCALE, sub(4, 18)) } + case 100000 { result := mul(SCALE, sub(5, 18)) } + case 1000000 { result := mul(SCALE, sub(6, 18)) } + case 10000000 { result := mul(SCALE, sub(7, 18)) } + case 100000000 { result := mul(SCALE, sub(8, 18)) } + case 1000000000 { result := mul(SCALE, sub(9, 18)) } + case 10000000000 { result := mul(SCALE, sub(10, 18)) } + case 100000000000 { result := mul(SCALE, sub(11, 18)) } + case 1000000000000 { result := mul(SCALE, sub(12, 18)) } + case 10000000000000 { result := mul(SCALE, sub(13, 18)) } + case 100000000000000 { result := mul(SCALE, sub(14, 18)) } + case 1000000000000000 { result := mul(SCALE, sub(15, 18)) } + case 10000000000000000 { result := mul(SCALE, sub(16, 18)) } + case 100000000000000000 { result := mul(SCALE, sub(17, 18)) } + case 1000000000000000000 { result := 0 } + case 10000000000000000000 { result := SCALE } + case 100000000000000000000 { result := mul(SCALE, 2) } + case 1000000000000000000000 { result := mul(SCALE, 3) } + case 10000000000000000000000 { result := mul(SCALE, 4) } + case 100000000000000000000000 { result := mul(SCALE, 5) } + case 1000000000000000000000000 { result := mul(SCALE, 6) } + case 10000000000000000000000000 { result := mul(SCALE, 7) } + case 100000000000000000000000000 { result := mul(SCALE, 8) } + case 1000000000000000000000000000 { result := mul(SCALE, 9) } + case 10000000000000000000000000000 { result := mul(SCALE, 10) } + case 100000000000000000000000000000 { result := mul(SCALE, 11) } + case 1000000000000000000000000000000 { result := mul(SCALE, 12) } + case 10000000000000000000000000000000 { result := mul(SCALE, 13) } + case 100000000000000000000000000000000 { result := mul(SCALE, 14) } + case 1000000000000000000000000000000000 { result := mul(SCALE, 15) } + case 10000000000000000000000000000000000 { result := mul(SCALE, 16) } + case 100000000000000000000000000000000000 { result := mul(SCALE, 17) } + case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) } + case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) } + case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) } + case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) } + case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) } + case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) } + case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) } + case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) } + case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) } + case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) } + case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) } + case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) } + case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) } + case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) } + case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) } + case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) } + case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) } + case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) } + case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) } + case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) } + case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) } + case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) } + case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) } + case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) } + case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) } + case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) } + case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) } + case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) } + case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) } + case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) } + case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) } + case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) } + case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) } + case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) } + case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) } + case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) } + case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) } + case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) } + case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) } + case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) } + case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) } + case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 59) } + default { + result := MAX_UD60x18 + } + } + + if (result == MAX_UD60x18) { + // Do the fixed-point division inline to save gas. The denominator is log2(10). + unchecked { result = (log2(x) * SCALE) / 332192809488736234; } + } + } + + /// @notice Calculates the binary logarithm of x. + /// + /// @dev Based on the iterative approximation algorithm. + /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation + /// + /// Requirements: + /// - x must be greater than or equal to SCALE, otherwise the result would be negative. + /// + /// Caveats: + /// - The results are nor perfectly accurate to the last digit, due to the lossy precision of the iterative approximation. + /// + /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the binary logarithm. + /// @return result The binary logarithm as an unsigned 60.18-decimal fixed-point number. + function log2(uint256 x) internal pure returns (uint256 result) { + require(x >= SCALE); + unchecked { + // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n). + uint256 n = PRBMathCommon.mostSignificantBit(x / SCALE); + + // The integer part of the logarithm as an unsigned 60.18-decimal fixed-point number. The operation can't overflow + // because n is maximum 255 and SCALE is 1e18. + result = n * SCALE; + + // This is y = x * 2^(-n). + uint256 y = x >> n; + + // If y = 1, the fractional part is zero. + if (y == SCALE) { + return result; + } + + // Calculate the fractional part via the iterative approximation. + // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster. + for (uint256 delta = HALF_SCALE; delta > 0; delta >>= 1) { + y = (y * y) / SCALE; + + // Is y^2 > 2 and so in the range [2,4)? + if (y >= 2 * SCALE) { + // Add the 2^(-m) factor to the logarithm. + result += delta; + + // Corresponds to z/2 on Wikipedia. + y >>= 1; + } + } + } + } + + /// @notice Multiplies two unsigned 60.18-decimal fixed-point numbers together, returning a new unsigned 60.18-decimal + /// fixed-point number. + /// @dev See the documentation for the "PRBMathCommon.mulDivFixedPoint" function. + /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number. + /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number. + /// @return result The result as an unsigned 60.18-decimal fixed-point number. + function mul(uint256 x, uint256 y) internal pure returns (uint256 result) { + result = PRBMathCommon.mulDivFixedPoint(x, y); + } + + /// @notice Retrieves PI as an unsigned 60.18-decimal fixed-point number. + function pi() internal pure returns (uint256 result) { + result = 3141592653589793238; + } + + /// @notice Raises x (unsigned 60.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the + /// famous algorithm "exponentiation by squaring". + /// + /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring + /// + /// Requirements: + /// - The result must fit within MAX_UD60x18. + /// + /// Caveats: + /// - All from "mul". + /// - Assumes 0^0 is 1. + /// + /// @param x The base as an unsigned 60.18-decimal fixed-point number. + /// @param y The exponent as an uint256. + /// @return result The result as an unsigned 60.18-decimal fixed-point number. + function pow(uint256 x, uint256 y) internal pure returns (uint256 result) { + // Calculate the first iteration of the loop in advance. + result = y & 1 > 0 ? x : SCALE; + + // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster. + for (y >>= 1; y > 0; y >>= 1) { + x = PRBMathCommon.mulDivFixedPoint(x, x); + + // Equivalent to "y % 2 == 1" but faster. + if (y & 1 > 0) { + result = PRBMathCommon.mulDivFixedPoint(result, x); + } + } + } + + /// @notice Returns 1 as an unsigned 60.18-decimal fixed-point number. + function scale() internal pure returns (uint256 result) { + result = SCALE; + } + + /// @notice Calculates the square root of x, rounding down. + /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method. + /// + /// Requirements: + /// - x must be less than MAX_UD60x18 / SCALE. + /// + /// Caveats: + /// - The maximum fixed-point number permitted is 115792089237316195423570985008687907853269.984665640564039458. + /// + /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the square root. + /// @return result The result as an unsigned 60.18-decimal fixed-point . + function sqrt(uint256 x) internal pure returns (uint256 result) { + require(x < 115792089237316195423570985008687907853269984665640564039458); + unchecked { + // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two unsigned + // 60.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root). + result = PRBMathCommon.sqrt(x * SCALE); + } + } +} diff --git a/test/libsolidity/semanticTests/externalContracts/_prbmath/README.md b/test/libsolidity/semanticTests/externalContracts/_prbmath/README.md new file mode 100644 index 000000000..1c0fbaa22 --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/_prbmath/README.md @@ -0,0 +1 @@ +Imported from https://github.com/hifi-finance/prb-math/commit/62021c1abc3413f20d0bdc8f941cf9f21d5a7d2d From 484a4398a7390876b50e80e489de8fb36767d108 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Apr 2021 12:33:13 +0100 Subject: [PATCH 012/235] Add semantic tests using prb-math --- .../externalContracts/prbmath_signed.sol | 95 +++++++++++++++++++ .../externalContracts/prbmath_unsigned.sol | 95 +++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol create mode 100644 test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol new file mode 100644 index 000000000..c04bc3cde --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol @@ -0,0 +1,95 @@ +==== ExternalSource: _prbmath/PRBMathCommon.sol ==== +==== ExternalSource: _prbmath/PRBMathSD59x18.sol ==== +==== Source: prbmath.sol ==== +import "_prbmath/PRBMathSD59x18.sol"; + +contract test { + using PRBMathSD59x18 for int256; + + function div(int256 x, int256 y) external pure returns (int256 ret) { + ret = x.div(y); + } + function exp(int256 x) external pure returns (int256 ret) { + ret = x.log10(); + } + function exp2(int256 x) external pure returns (int256 ret) { + ret = x.exp2(); + } + function gm(int256 x, int256 y) external pure returns (int256 ret) { + ret = x.gm(y); + } + function log10(int256 x) external pure returns (int256 ret) { + ret = x.log10(); + } + function log2(int256 x) external pure returns (int256 ret) { + ret = x.log2(); + } + function mul(int256 x, int256 y) external pure returns (int256 ret) { + ret = x.mul(y); + } + function pow(int256 x, uint256 y) external pure returns (int256 ret) { + ret = x.pow(y); + } + function sqrt(int256 x) external pure returns (int256 ret) { + ret = x.sqrt(); + } + function benchmark(int256 x) external pure returns (int256 ret, int256 z1, int256 z2) { + int256 y = x.mul(3).ceil(); + int256 z = y.div(x); + for (uint i = 0; i < 10; i++) + z = z.sqrt(); + ret = z; + + // Check precision + z1 = z.ceil(); + z2 = z.sqrt().pow(2).ceil(); + assert(z1 == z2); + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() +// gas irOptimized: 2149588 +// gas legacy: 2561184 +// gas legacyOptimized: 1874610 +// div(int256,int256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 +// gas irOptimized: 22303 +// gas legacy: 22767 +// gas legacyOptimized: 22282 +// exp(int256): 3141592653589793238 -> 4971498726941338506 +// gas irOptimized: 31438 +// gas legacy: 32933 +// gas legacyOptimized: 30322 +// exp2(int256): 3141592653589793238 -> 8824977827076287620 +// gas irOptimized: 24808 +// gas legacy: 24864 +// gas legacyOptimized: 24110 +// gm(int256,int256): 3141592653589793238, 88714123 -> 16694419339601 +// gas irOptimized: 23573 +// gas legacy: 23228 +// gas legacyOptimized: 22683 +// log10(int256): 3141592653589793238 -> 4971498726941338506 +// gas irOptimized: 31328 +// gas legacy: 32934 +// gas legacyOptimized: 30323 +// log2(int256): 3141592653589793238 -> 1651496129472318782 +// gas irOptimized: 29484 +// gas legacy: 31067 +// gas legacyOptimized: 28426 +// mul(int256,int256): 3141592653589793238, 88714123 -> 278703637 +// gas irOptimized: 22414 +// gas legacy: 22807 +// gas legacyOptimized: 22295 +// pow(int256,uint256): 3141592653589793238, 5 -> 306019684785281453040 +// gas irOptimized: 22974 +// gas legacy: 23508 +// gas legacyOptimized: 22921 +// sqrt(int256): 3141592653589793238 -> 1772453850905516027 +// gas irOptimized: 23246 +// gas legacy: 22802 +// gas legacyOptimized: 22422 +// benchmark(int256): 3141592653589793238 -> 998882724338592125, 1000000000000000000, 1000000000000000000 +// gas irOptimized: 43323 +// gas legacy: 36673 +// gas legacyOptimized: 34729 diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol new file mode 100644 index 000000000..a81d2ecf7 --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol @@ -0,0 +1,95 @@ +==== ExternalSource: _prbmath/PRBMathCommon.sol ==== +==== ExternalSource: _prbmath/PRBMathUD60x18.sol ==== +==== Source: prbmath.sol ==== +import "_prbmath/PRBMathUD60x18.sol"; + +contract test { + using PRBMathUD60x18 for uint256; + + function div(uint256 x, uint256 y) external pure returns (uint256 ret) { + ret = x.div(y); + } + function exp(uint256 x) external pure returns (uint256 ret) { + ret = x.log10(); + } + function exp2(uint256 x) external pure returns (uint256 ret) { + ret = x.exp2(); + } + function gm(uint256 x, uint256 y) external pure returns (uint256 ret) { + ret = x.gm(y); + } + function log10(uint256 x) external pure returns (uint256 ret) { + ret = x.log10(); + } + function log2(uint256 x) external pure returns (uint256 ret) { + ret = x.log2(); + } + function mul(uint256 x, uint256 y) external pure returns (uint256 ret) { + ret = x.mul(y); + } + function pow(uint256 x, uint256 y) external pure returns (uint256 ret) { + ret = x.pow(y); + } + function sqrt(uint256 x) external pure returns (uint256 ret) { + ret = x.sqrt(); + } + function benchmark(uint256 x) external pure returns (uint256 ret, uint256 z1, uint256 z2) { + uint256 y = x.mul(3).ceil(); + uint256 z = y.div(x); + for (uint i = 0; i < 10; i++) + z = z.sqrt(); + ret = z; + + // Check precision + z1 = z.ceil(); + z2 = z.sqrt().pow(2).ceil(); + assert(z1 == z2); + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() +// gas irOptimized: 1945345 +// gas legacy: 2326345 +// gas legacyOptimized: 1750080 +// div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 +// gas irOptimized: 22103 +// gas legacy: 22497 +// gas legacyOptimized: 22010 +// exp(uint256): 3141592653589793238 -> 0x44fe4fc084a52b8a +// gas irOptimized: 30892 +// gas legacy: 32854 +// gas legacyOptimized: 29881 +// exp2(uint256): 3141592653589793238 -> 8824977827076287620 +// gas irOptimized: 24746 +// gas legacy: 24814 +// gas legacyOptimized: 24062 +// gm(uint256,uint256): 3141592653589793238, 88714123 -> 16694419339601 +// gas irOptimized: 23575 +// gas legacy: 23269 +// gas legacyOptimized: 22724 +// log10(uint256): 3141592653589793238 -> 0x44fe4fc084a52b8a +// gas irOptimized: 30936 +// gas legacy: 32898 +// gas legacyOptimized: 29925 +// log2(uint256): 3141592653589793238 -> 1651496129472318782 +// gas irOptimized: 28838 +// gas legacy: 30986 +// gas legacyOptimized: 28001 +// mul(uint256,uint256): 3141592653589793238, 88714123 -> 278703637 +// gas irOptimized: 22146 +// gas legacy: 22604 +// gas legacyOptimized: 22090 +// pow(uint256,uint256): 3141592653589793238, 5 -> 306019684785281453040 +// gas irOptimized: 22653 +// gas legacy: 23245 +// gas legacyOptimized: 22646 +// sqrt(uint256): 3141592653589793238 -> 1772453850905516027 +// gas irOptimized: 23264 +// gas legacy: 22820 +// gas legacyOptimized: 22440 +// benchmark(uint256): 3141592653589793238 -> 998882724338592125, 1000000000000000000, 1000000000000000000 +// gas irOptimized: 42063 +// gas legacy: 35385 +// gas legacyOptimized: 33449 From 0b3f3dff6087a104a0e3bc02019e8f37b4a1158d Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Apr 2021 20:44:09 +0100 Subject: [PATCH 013/235] Add ramanujan's pi approximation as a semantic test --- .../externalContracts/ramanujan_pi.sol | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol diff --git a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol new file mode 100644 index 000000000..b280cfa33 --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol @@ -0,0 +1,44 @@ +==== ExternalSource: _prbmath/PRBMathCommon.sol ==== +==== ExternalSource: _prbmath/PRBMathSD59x18.sol ==== +==== Source: ramanujan_pi.sol ==== +import "_prbmath/PRBMathSD59x18.sol"; + +// The goal of this test file is to implement Ramanujan's pi approximation using various libraries. + +function factorial(uint n) pure returns (uint ret) { + ret = 1; + for (; n > 1; --n) + ret *= n; +} + +contract test { + using PRBMathSD59x18 for int256; + + function prb_scale(uint n) internal pure returns (int256 ret) { + // Scale to SD59x18 + ret = int256(n * 10e17); + } + + // This dumb implementation of Ramanujan series calculates 1/pi + function prb_pi() external pure returns (int256 ret) { + uint n = 6; // More than 6 iterations results in failure + for (uint k = 0; k < n; k++) { + int256 a = prb_scale(factorial(4 * k)).div(prb_scale(factorial(k)).pow(4)); + int256 b = (prb_scale(25390).mul(prb_scale(k)) + prb_scale(1103)).div(prb_scale(396).pow(4 * k)); + ret += a.mul(b); + } + ret = ret.mul(prb_scale(2).sqrt().mul(prb_scale(2)).div(prb_scale(99).pow(2))); + ret = prb_scale(1).div(ret); + } +} +// ==== +// compileViaYul: also +// ---- +// constructor() +// gas irOptimized: 590192 +// gas legacy: 733634 +// gas legacyOptimized: 498033 +// prb_pi() -> 3141592656369545286 +// gas irOptimized: 66098 +// gas legacy: 98903 +// gas legacyOptimized: 75735 From 1f2711b0aa9de79ea705fbeddd4d4aea26686220 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Thu, 7 Jan 2021 22:45:19 -0500 Subject: [PATCH 014/235] [isoltest] Replace storage command with 'storage_empty' builtin. --- test/libsolidity/SemanticTest.cpp | 18 +++++++------- .../array/byte_array_storage_layout.sol | 8 +++---- .../copying/array_copy_including_array.sol | 4 ++-- .../array_copy_storage_storage_dyn_dyn.sol | 2 +- .../array_copy_storage_storage_struct.sol | 2 +- .../array/copying/bytes_inside_mappings.sol | 8 +++---- .../copy_byte_array_in_struct_to_storage.sol | 2 +- .../array/copying/copy_removes_bytes_data.sol | 4 ++-- .../delete/delete_removes_bytes_data.sol | 4 ++-- .../array/dynamic_array_cleanup.sol | 8 +++---- .../array/dynamic_multi_array_cleanup.sol | 6 ++--- .../array/fixed_array_cleanup.sol | 6 ++--- .../array/pop/array_pop_array_transition.sol | 2 +- .../array/pop/array_pop_storage_empty.sol | 2 +- .../array/pop/array_pop_uint16_transition.sol | 2 +- .../array/pop/array_pop_uint24_transition.sol | 2 +- .../pop/byte_array_pop_long_storage_empty.sol | 2 +- ...ray_pop_long_storage_empty_garbage_ref.sol | 2 +- .../pop/byte_array_pop_storage_empty.sol | 2 +- .../array/short_fixed_array_cleanup.sol | 6 ++--- .../builtins/storage/storage_empty.sol | 6 +++++ .../builtins/storage/storage_nonempty.sol | 10 ++++++++ .../storage/empty_nonempty_empty.sol | 24 +++++++++---------- ...truct_containing_bytes_copy_and_delete.sol | 10 ++++---- test/libsolidity/util/SoltestTypes.h | 3 +-- test/libsolidity/util/TestFileParser.cpp | 15 ------------ test/libsolidity/util/TestFileParserTests.cpp | 22 ----------------- test/libsolidity/util/TestFunctionCall.cpp | 17 ------------- 28 files changed, 79 insertions(+), 120 deletions(-) create mode 100644 test/libsolidity/semanticTests/builtins/storage/storage_empty.sol create mode 100644 test/libsolidity/semanticTests/builtins/storage/storage_nonempty.sol diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index d17416d4f..dfa160cc2 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -124,6 +125,12 @@ void SemanticTest::initializeBuiltins() { return util::toBigEndian(u256(0x1234)); }; + soltestAssert(m_builtins.count("storageEmpty") == 0, ""); + m_builtins["storageEmpty"] = [this](FunctionCall const& _call) -> std::optional + { + soltestAssert(_call.arguments.parameters.empty(), "No arguments expected."); + return toBigEndian(u256(storageEmpty(m_contractAddress) ? 1 : 0)); + }; } TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) @@ -222,16 +229,7 @@ TestCase::TestResult SemanticTest::runTest( constructed = true; } - if (test.call().kind == FunctionCall::Kind::Storage) - { - test.setFailure(false); - bytes result(1, !storageEmpty(m_contractAddress)); - test.setRawBytes(result); - soltestAssert(test.call().expectations.rawBytes().size() == 1, ""); - if (test.call().expectations.rawBytes() != result) - success = false; - } - else if (test.call().kind == FunctionCall::Kind::Constructor) + if (test.call().kind == FunctionCall::Kind::Constructor) { if (m_transactionSuccessful == test.call().expectations.failure) success = false; diff --git a/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol b/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol index 317cab5cb..43c693e8b 100644 --- a/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol +++ b/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol @@ -41,17 +41,17 @@ contract c { // ==== // compileViaYul: also // ---- -// storage: empty +// storageEmpty -> 1 // test_short() -> 1780731860627700044960722568376587075150542249149356309979516913770823710 // gas legacy: 110938 // gas legacyOptimized: 109706 -// storage: nonempty +// storageEmpty -> 0 // test_long() -> 67 // gas irOptimized: 134320 // gas legacy: 213590 // gas legacyOptimized: 211044 -// storage: nonempty +// storageEmpty -> 0 // test_pop() -> 1780731860627700044960722568376592200742329637303199754547598369979433020 // gas legacy: 176030 // gas legacyOptimized: 173504 -// storage: nonempty +// storageEmpty -> 0 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol index ccb9ec2af..b9b928653 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol @@ -40,9 +40,9 @@ contract c { // gas irOptimized: 2470372 // gas legacy: 2288641 // gas legacyOptimized: 2258654 -// storage: empty +// storageEmpty -> 1 // clear() -> 0, 0 // gas irOptimized: 1852821 // gas legacy: 1727169 // gas legacyOptimized: 1698931 -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol index f2235bf0d..5856e93fc 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_dyn_dyn.sol @@ -21,4 +21,4 @@ contract c { // setData1(uint256,uint256,uint256): 0, 0, 0 -> // copyStorageStorage() -> // getData2(uint256): 0 -> 0, 0 -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol index 57ffa7090..9b5616994 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_storage_storage_struct.sol @@ -22,4 +22,4 @@ contract c { // gas irOptimized: 257752 // gas legacy: 255936 // gas legacyOptimized: 254359 -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol index b946f7987..b1c321992 100644 --- a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol +++ b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol @@ -14,10 +14,10 @@ contract c { // gas irOptimized: 163929 // gas legacy: 164121 // gas legacyOptimized: 163766 -// storage: nonempty +// storageEmpty -> 0 // copy(uint256,uint256): 1, 2 -> true -// storage: nonempty +// storageEmpty -> 0 // copy(uint256,uint256): 99, 1 -> true -// storage: nonempty +// storageEmpty -> 0 // copy(uint256,uint256): 99, 2 -> true -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol index 4a4acad3c..7d9708703 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol @@ -43,4 +43,4 @@ contract C { // g() -> 0x40, 0xc0, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000, 0x11, 0x3132333435363738393233343536373839000000000000000000000000000000 // gas legacy: 100595 // h() -> 0x40, 0x60, 0x00, 0x00 -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol index 354eee307..bbf1b7ddd 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol @@ -12,6 +12,6 @@ contract c { // gas irOptimized: 163680 // gas legacy: 163756 // gas legacyOptimized: 163596 -// storage: nonempty +// storageEmpty -> 0 // reset() -> true -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/delete/delete_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/delete/delete_removes_bytes_data.sol index 28d6e5d8f..4c573f6fa 100644 --- a/test/libsolidity/semanticTests/array/delete/delete_removes_bytes_data.sol +++ b/test/libsolidity/semanticTests/array/delete/delete_removes_bytes_data.sol @@ -7,6 +7,6 @@ contract c { // compileViaYul: also // ---- // (): 7 -> -// storage: nonempty +// storageEmpty -> 0 // del(): 7 -> true -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol b/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol index 24efe49ba..70864edbb 100644 --- a/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol +++ b/test/libsolidity/semanticTests/array/dynamic_array_cleanup.sol @@ -14,13 +14,13 @@ contract c { // ==== // compileViaYul: also // ---- -// storage: empty +// storageEmpty -> 1 // fill() -> // gas irOptimized: 535098 // gas legacy: 504373 // gas legacyOptimized: 499648 -// storage: nonempty +// storageEmpty -> 0 // halfClear() -> -// storage: nonempty +// storageEmpty -> 0 // fullClear() -> -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol b/test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol index 79ce756ed..da759c7b1 100644 --- a/test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol +++ b/test/libsolidity/semanticTests/array/dynamic_multi_array_cleanup.sol @@ -16,11 +16,11 @@ contract c { // ==== // compileViaYul: also // ---- -// storage: empty +// storageEmpty -> 1 // fill() -> 8 // gas irOptimized: 168980 // gas legacy: 165456 // gas legacyOptimized: 164387 -// storage: nonempty +// storageEmpty -> 0 // clear() -> -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol index 49faa7777..5ba2ceab6 100644 --- a/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol +++ b/test/libsolidity/semanticTests/array/fixed_array_cleanup.sol @@ -11,11 +11,11 @@ contract c { // compileToEwasm: also // compileViaYul: also // ---- -// storage: empty +// storageEmpty -> 1 // fill() -> // gas irOptimized: 423878 // gas legacy: 429460 // gas legacyOptimized: 425520 -// storage: nonempty +// storageEmpty -> 0 // clear() -> -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol index 8ef94a432..7bbd86839 100644 --- a/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol +++ b/test/libsolidity/semanticTests/array/pop/array_pop_array_transition.sol @@ -28,4 +28,4 @@ contract c { // gas irOptimized: 2455497 // gas legacy: 2416722 // gas legacyOptimized: 2405396 -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_storage_empty.sol b/test/libsolidity/semanticTests/array/pop/array_pop_storage_empty.sol index 24f210177..b49a738ec 100644 --- a/test/libsolidity/semanticTests/array/pop/array_pop_storage_empty.sol +++ b/test/libsolidity/semanticTests/array/pop/array_pop_storage_empty.sol @@ -9,4 +9,4 @@ contract c { // compileViaYul: also // ---- // test() -> -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_uint16_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_uint16_transition.sol index 375487d97..5004ad13a 100644 --- a/test/libsolidity/semanticTests/array/pop/array_pop_uint16_transition.sol +++ b/test/libsolidity/semanticTests/array/pop/array_pop_uint16_transition.sol @@ -23,4 +23,4 @@ contract c { // gas irOptimized: 527367 // gas legacy: 454080 // gas legacyOptimized: 443170 -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/array_pop_uint24_transition.sol b/test/libsolidity/semanticTests/array/pop/array_pop_uint24_transition.sol index e849e2c91..aaf96d210 100644 --- a/test/libsolidity/semanticTests/array/pop/array_pop_uint24_transition.sol +++ b/test/libsolidity/semanticTests/array/pop/array_pop_uint24_transition.sol @@ -23,4 +23,4 @@ contract c { // gas irOptimized: 367121 // gas legacy: 320859 // gas legacyOptimized: 314681 -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol index 103563b2a..1c1323fdc 100644 --- a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol @@ -21,4 +21,4 @@ contract c { // gas irOptimized: 445718 // gas legacy: 552064 // gas legacyOptimized: 533164 -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol index 0e8bda56c..ee65fd911 100644 --- a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol @@ -20,4 +20,4 @@ contract c { // gas irOptimized: 291114 // gas legacy: 372763 // gas legacyOptimized: 366846 -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol index 4bad1d7ac..52716ecb3 100644 --- a/test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_storage_empty.sol @@ -14,4 +14,4 @@ contract c { // compileViaYul: also // ---- // test() -> -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol b/test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol index 9adb8446a..3665a8784 100644 --- a/test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol +++ b/test/libsolidity/semanticTests/array/short_fixed_array_cleanup.sol @@ -11,8 +11,8 @@ contract c { // compileToEwasm: also // compileViaYul: also // ---- -// storage: empty +// storageEmpty -> 1 // fill() -> -// storage: nonempty +// storageEmpty -> 0 // clear() -> -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/builtins/storage/storage_empty.sol b/test/libsolidity/semanticTests/builtins/storage/storage_empty.sol new file mode 100644 index 000000000..8fd5a635e --- /dev/null +++ b/test/libsolidity/semanticTests/builtins/storage/storage_empty.sol @@ -0,0 +1,6 @@ +contract StorageEmpty { +} +// ==== +// compileViaYul: also +// ---- +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/builtins/storage/storage_nonempty.sol b/test/libsolidity/semanticTests/builtins/storage/storage_nonempty.sol new file mode 100644 index 000000000..9c8a582e7 --- /dev/null +++ b/test/libsolidity/semanticTests/builtins/storage/storage_nonempty.sol @@ -0,0 +1,10 @@ +contract StorageNotEmpty { + uint256 x; + function set(uint256 _a) public { x = _a; } +} +// ==== +// compileViaYul: also +// ---- +// storageEmpty -> 1 +// set(uint256): 1 -> +// storageEmpty -> 0 diff --git a/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol b/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol index 234f0191f..eeeea2943 100644 --- a/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol +++ b/test/libsolidity/semanticTests/storage/empty_nonempty_empty.sol @@ -6,26 +6,26 @@ contract Test { // compileViaYul: also // ---- // set(bytes): 0x20, 3, "abc" -// storage: nonempty +// storageEmpty -> 0 // set(bytes): 0x20, 0 -// storage: empty +// storageEmpty -> 1 // set(bytes): 0x20, 31, "1234567890123456789012345678901" -// storage: nonempty +// storageEmpty -> 0 // set(bytes): 0x20, 36, "12345678901234567890123456789012", "XXXX" -// storage: nonempty +// storageEmpty -> 0 // set(bytes): 0x20, 3, "abc" -// storage: nonempty +// storageEmpty -> 0 // set(bytes): 0x20, 0 -// storage: empty +// storageEmpty -> 1 // set(bytes): 0x20, 3, "abc" -// storage: nonempty +// storageEmpty -> 0 // set(bytes): 0x20, 36, "12345678901234567890123456789012", "XXXX" -// storage: nonempty +// storageEmpty -> 0 // set(bytes): 0x20, 0 -// storage: empty +// storageEmpty -> 1 // set(bytes): 0x20, 66, "12345678901234567890123456789012", "12345678901234567890123456789012", "12" -// storage: nonempty +// storageEmpty -> 0 // set(bytes): 0x20, 3, "abc" -// storage: nonempty +// storageEmpty -> 0 // set(bytes): 0x20, 0 -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol index 363a44023..42bb2a118 100644 --- a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol +++ b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol @@ -23,16 +23,16 @@ contract c { // ==== // compileViaYul: also // ---- -// storage: empty +// storageEmpty -> 1 // set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true // gas irOptimized: 124227 // gas legacy: 124736 // gas legacyOptimized: 124179 // test(uint256): 32 -> "3" -// storage: nonempty +// storageEmpty -> 0 // copy() -> true -// storage: empty +// storageEmpty -> 1 // set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true -// storage: nonempty +// storageEmpty -> 0 // del() -> true -// storage: empty +// storageEmpty -> 1 diff --git a/test/libsolidity/util/SoltestTypes.h b/test/libsolidity/util/SoltestTypes.h index a19378030..b746fd80a 100644 --- a/test/libsolidity/util/SoltestTypes.h +++ b/test/libsolidity/util/SoltestTypes.h @@ -294,9 +294,8 @@ struct FunctionCall LowLevel, /// Marks a library deployment call. Library, - /// Check that the storage of the current contract is empty or non-empty. - Storage, /// Call to a builtin. + /// Builtins get registered in `SemanticTest::initializeBuiltins()`. Builtin }; Kind kind = Kind::Regular; diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index 19819ac1c..64ca552b8 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -106,21 +106,6 @@ vector TestFileParser::parseFunctionCall call.kind = FunctionCall::Kind::Library; call.expectations.failure = false; } - else if (accept(Token::Storage, true)) - { - expect(Token::Colon); - call.expectations.failure = false; - call.expectations.result.push_back(Parameter()); - // empty / non-empty is encoded as false / true - if (m_scanner.currentLiteral() == "empty") - call.expectations.result.back().rawBytes = bytes(1, uint8_t(false)); - else if (m_scanner.currentLiteral() == "nonempty") - call.expectations.result.back().rawBytes = bytes(1, uint8_t(true)); - else - BOOST_THROW_EXCEPTION(TestParserError("Expected \"empty\" or \"nonempty\".")); - call.kind = FunctionCall::Kind::Storage; - m_scanner.scanNextToken(); - } else { bool lowLevelCall = false; diff --git a/test/libsolidity/util/TestFileParserTests.cpp b/test/libsolidity/util/TestFileParserTests.cpp index 30d501669..02e95a355 100644 --- a/test/libsolidity/util/TestFileParserTests.cpp +++ b/test/libsolidity/util/TestFileParserTests.cpp @@ -958,28 +958,6 @@ BOOST_AUTO_TEST_CASE(library) ); } -BOOST_AUTO_TEST_CASE(empty_storage) -{ - char const* source = R"( - // storage: empty - )"; - auto const calls = parse(source); - BOOST_REQUIRE_EQUAL(calls.size(), 1); - BOOST_CHECK(calls.at(0).kind == FunctionCall::Kind::Storage); - BOOST_CHECK(calls.at(0).expectations.result.front().rawBytes == bytes(1, 0)); -} - -BOOST_AUTO_TEST_CASE(nonempty_storage) -{ - char const* source = R"( - // storage: nonempty - )"; - auto const calls = parse(source); - BOOST_REQUIRE_EQUAL(calls.size(), 1); - BOOST_CHECK(calls.at(0).kind == FunctionCall::Kind::Storage); - BOOST_CHECK(calls.at(0).expectations.result.front().rawBytes == bytes(1, 1)); -} - BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index 0e411a458..65300512c 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -61,23 +61,6 @@ string TestFunctionCall::format( stream << _linePrefix << newline << ws << "library:" << ws << m_call.signature; return; } - else if (m_call.kind == FunctionCall::Kind::Storage) - { - stream << _linePrefix << newline << ws << "storage" << colon << ws; - soltestAssert(m_rawBytes.size() == 1, ""); - soltestAssert(m_call.expectations.rawBytes().size() == 1, ""); - bool isEmpty = - _renderMode == RenderMode::ActualValuesExpectedGas ? - m_rawBytes.front() == 0 : - m_call.expectations.rawBytes().front() == 0; - string output = isEmpty ? "empty" : "nonempty"; - if (_renderMode == RenderMode::ActualValuesExpectedGas && !matchesExpectation()) - AnsiColorized(stream, highlight, {util::formatting::RED_BACKGROUND}) << output; - else - stream << output; - - return; - } /// Formats the function signature. This is the same independent from the display-mode. stream << _linePrefix << newline << ws << m_call.signature; From ed27c77defd98feb65038659f689eb86ae55ceaa Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Tue, 27 Apr 2021 16:22:36 -0500 Subject: [PATCH 015/235] [isoltest] Ignore gas checks on isoltest builtins. --- test/libsolidity/SemanticTest.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index dfa160cc2..51a5a3c9c 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -383,12 +383,14 @@ bool SemanticTest::checkGasCostExpectation(TestFunctionCall& io_test, bool _comp // or test is run with abi encoder v1 only // or gas used less than threshold for enforcing feature // or setting is "ir" and it's not included in expectations + // or if the called function is an isoltest builtin e.g. `smokeTest` or `storageEmpty` if ( !m_enforceGasCost || ( (setting == "ir" || m_gasUsed < m_enforceGasCostMinValue || m_gasUsed >= m_gas) && io_test.call().expectations.gasUsed.count(setting) == 0 - ) + ) || + io_test.call().kind == FunctionCall::Kind::Builtin ) return true; From bd1fc0e88dc33c3b2327efbfcb573a38d320c6fc Mon Sep 17 00:00:00 2001 From: cxxboy Date: Mon, 26 Apr 2021 11:37:33 -0400 Subject: [PATCH 016/235] script to automate updates to robots.txt #11199 Rewritten the script to work with a 'robots.txt' template file and we generate the 'robots.txt' from the template when we execute the script. It's found to be a better alternative to editing the 'robots.txt' file in-place. --- ReleaseChecklist.md | 1 - docs/_static/robots.txt | 95 ------------------------------------- docs/robots.txt.template | 14 ++++++ scripts/docs.sh | 1 + scripts/update_robotstxt.sh | 27 +++++++++++ 5 files changed, 42 insertions(+), 96 deletions(-) delete mode 100644 docs/_static/robots.txt create mode 100644 docs/robots.txt.template create mode 100755 scripts/update_robotstxt.sh diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index af3bbfdaa..fbc66279b 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -61,7 +61,6 @@ ### Documentation - [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` at the bottom of the page and click `BUILD`). - [ ] In the admin panel, select `Versions` in the menu and set the default version to the released one. - - [ ] If it is a non-breaking release, block indexing of previous release version in the ``robots.txt`` file. ### Release solc-js - [ ] Wait until solc-bin was properly deployed. You can test this via remix - a test run through remix is advisable anyway. diff --git a/docs/_static/robots.txt b/docs/_static/robots.txt deleted file mode 100644 index c526f3786..000000000 --- a/docs/_static/robots.txt +++ /dev/null @@ -1,95 +0,0 @@ -Sitemap: http://docs.soliditylang.org/sitemap.xml - -# Prevent documentation for the development branches from showing up in search results. -Disallow: /en/develop/ -Disallow: /en/breaking/ - -# Prevent documentation for the older Solidity versions from showing up in search results. - -Disallow: /en/v0.1.2/ -Disallow: /en/v0.1.3/ -Disallow: /en/v0.1.4/ -Disallow: /en/v0.1.5/ -Disallow: /en/v0.1.6/ -Disallow: /en/v0.1.7/ -Disallow: /en/v0.2.0/ -Disallow: /en/v0.2.1/ -Disallow: /en/v0.2.2/ -Disallow: /en/v0.3.0/ -Disallow: /en/v0.3.1/ -Disallow: /en/v0.3.2/ -Disallow: /en/v0.3.3/ -Disallow: /en/v0.3.4/ -Disallow: /en/v0.3.5/ -Disallow: /en/v0.3.6/ -Disallow: /en/v0.4.0/ -Disallow: /en/v0.4.1/ -Disallow: /en/v0.4.2/ -Disallow: /en/v0.4.3/ -Disallow: /en/v0.4.4/ -Disallow: /en/v0.4.5/ -Disallow: /en/v0.4.6/ -Disallow: /en/v0.4.7/ -Disallow: /en/v0.4.8/ -Disallow: /en/v0.4.9/ -Disallow: /en/v0.4.10/ -Disallow: /en/v0.4.11/ -Disallow: /en/v0.4.12/ -Disallow: /en/v0.4.13/ -Disallow: /en/v0.4.14/ -Disallow: /en/v0.4.15/ -Disallow: /en/v0.4.16/ -Disallow: /en/v0.4.17/ -Disallow: /en/v0.4.18/ -Disallow: /en/v0.4.19/ -Disallow: /en/v0.4.20/ -Disallow: /en/v0.4.21/ -Disallow: /en/v0.4.22/ -Disallow: /en/v0.4.23/ -Disallow: /en/v0.4.24/ -Disallow: /en/v0.4.25/ -Disallow: /en/v0.4.26/ -Disallow: /en/v0.5.0/ -Disallow: /en/v0.5.1/ -Disallow: /en/v0.5.2/ -Disallow: /en/v0.5.3/ -Disallow: /en/v0.5.4/ -Disallow: /en/v0.5.5/ -Disallow: /en/v0.5.6/ -Disallow: /en/v0.5.7/ -Disallow: /en/v0.5.8/ -Disallow: /en/v0.5.9/ -Disallow: /en/v0.5.10/ -Disallow: /en/v0.5.11/ -Disallow: /en/v0.5.12/ -Disallow: /en/v0.5.13/ -Disallow: /en/v0.5.14/ -Disallow: /en/v0.5.15/ -Disallow: /en/v0.5.16/ -Disallow: /en/v0.5.17/ -Disallow: /en/v0.6.0/ -Disallow: /en/v0.6.1/ -Disallow: /en/v0.6.2/ -Disallow: /en/v0.6.3/ -Disallow: /en/v0.6.4/ -Disallow: /en/v0.6.5/ -Disallow: /en/v0.6.6/ -Disallow: /en/v0.6.7/ -Disallow: /en/v0.6.8/ -Disallow: /en/v0.6.9/ -Disallow: /en/v0.6.10/ -Disallow: /en/v0.6.11/ -Disallow: /en/v0.6.12/ -Disallow: /en/v0.7.0/ -Disallow: /en/v0.7.1/ -Disallow: /en/v0.7.2/ -Disallow: /en/v0.7.3/ -Disallow: /en/v0.7.4/ -Disallow: /en/v0.7.5/ -# Allow the last patch release of the 0.7.x series -#Disallow: /en/v0.7.6/ -Disallow: /en/v0.8.0/ -Disallow: /en/v0.8.1/ -Disallow: /en/v0.8.2/ -# Allow the latest release -#Disallow: /en/v0.8.3/ diff --git a/docs/robots.txt.template b/docs/robots.txt.template new file mode 100644 index 000000000..c5d0a9d0a --- /dev/null +++ b/docs/robots.txt.template @@ -0,0 +1,14 @@ +User-Agent: * +Sitemap: http://docs.soliditylang.org/sitemap.xml + +Allow: /en/latest/ +Allow: /en/v0.7.6/ +Allow: /en/v{{ LATEST_VERSION }}/ +Allow: /_/downloads/en/latest/ +Allow: /_/downloads/en/0.7.6/ +Allow: /_/downloads/en/{{ LATEST_VERSION }}/ + +# Prevent documentation for the development branches and older Solidity +# versions from showing up in search results. +Disallow: /en/* +Disallow: /_/downloads/en/* diff --git a/scripts/docs.sh b/scripts/docs.sh index 4d31277da..e2493511b 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -27,6 +27,7 @@ #------------------------------------------------------------------------------ set -e +scripts/update_robotstxt.sh cd docs pip3 install -r requirements.txt sphinx-build -nW -b html -d _build/doctrees . _build/html diff --git a/scripts/update_robotstxt.sh b/scripts/update_robotstxt.sh new file mode 100755 index 000000000..cd09c1433 --- /dev/null +++ b/scripts/update_robotstxt.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +#------------------------------------------------------------------------------ +# 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 +# +# (c) 2021 solidity contributors. +#------------------------------------------------------------------------------ + +set -eu + +repo_root="$(dirname "$0")/.." +robots_template_path="${repo_root}/docs/robots.txt.template" +robots_txt_path="${repo_root}/docs/_static/robots.txt" +latest_version=$("${repo_root}/scripts/get_version.sh") + +sed -E -e "s/\{\{[[:space:]]LATEST_VERSION[[:space:]]\}\}/${latest_version}/g" "$robots_template_path" > "$robots_txt_path" From 8b4eaeabbfb7ca9e645196fd1de69a999ea43f44 Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Mon, 26 Apr 2021 17:31:45 +0200 Subject: [PATCH 017/235] Added a few optimizer tests for Verbatim --- test/libevmasm/Optimiser.cpp | 168 ++++++++++++++++++ .../loadResolver/verbatim_mload.yul | 16 ++ .../loadResolver/verbatim_sload.yul | 21 +++ .../unusedPruner/verbatim.yul | 17 ++ 4 files changed, 222 insertions(+) create mode 100644 test/libyul/yulOptimizerTests/loadResolver/verbatim_mload.yul create mode 100644 test/libyul/yulOptimizerTests/loadResolver/verbatim_sload.yul create mode 100644 test/libyul/yulOptimizerTests/unusedPruner/verbatim.yul diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index cdc4a7db1..8903cc331 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -93,6 +93,45 @@ namespace BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); } + /// In contrast to the function `CSE`, this function doesn't finish the CSE optimization on an + /// instruction that breaks CSE Analysis block. Copied from Assembly.cpp + AssemblyItems fullCSE(AssemblyItems const& _input) + { + AssemblyItems optimisedItems; + + bool usesMSize = ranges::any_of(_input, [](AssemblyItem const& _i) { + return _i == AssemblyItem{Instruction::MSIZE} || _i.type() == VerbatimBytecode; + }); + + auto iter = _input.begin(); + while (iter != _input.end()) + { + KnownState emptyState; + CommonSubexpressionEliminator eliminator{emptyState}; + auto orig = iter; + iter = eliminator.feedItems(iter, _input.end(), usesMSize); + bool shouldReplace = false; + AssemblyItems optimisedChunk; + optimisedChunk = eliminator.getOptimizedItems(); + shouldReplace = (optimisedChunk.size() < static_cast(iter - orig)); + if (shouldReplace) + optimisedItems += optimisedChunk; + else + copy(orig, iter, back_inserter(optimisedItems)); + } + + return optimisedItems; + } + + void checkFullCSE( + AssemblyItems const& _input, + AssemblyItems const& _expectation + ) + { + AssemblyItems output = fullCSE(_input); + BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); + } + AssemblyItems CFG(AssemblyItems const& _input) { AssemblyItems output = _input; @@ -1292,6 +1331,135 @@ BOOST_AUTO_TEST_CASE(cse_sub_zero) }); } +BOOST_AUTO_TEST_CASE(cse_simple_verbatim) +{ + auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0}; + AssemblyItems input{verbatim}; + checkCSE(input, input); + checkFullCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(cse_mload_pop) +{ + AssemblyItems input{ + u256(1000), + Instruction::MLOAD, + Instruction::POP, + }; + + AssemblyItems output{ + }; + + checkCSE(input, output); + checkFullCSE(input, output); +} + +BOOST_AUTO_TEST_CASE(cse_verbatim_mload) +{ + auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0}; + AssemblyItems input{ + u256(1000), + Instruction::MLOAD, // Should not be removed + Instruction::POP, + verbatim, + u256(1000), + Instruction::MLOAD, // Should not be removed + Instruction::POP, + }; + + checkFullCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(cse_sload_verbatim_dup) +{ + auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0}; + AssemblyItems input{ + u256(0), + Instruction::SLOAD, + u256(0), + Instruction::SLOAD, + verbatim + }; + + AssemblyItems output{ + u256(0), + Instruction::SLOAD, + Instruction::DUP1, + verbatim + }; + + checkCSE(input, output); + checkFullCSE(input, output); +} + +BOOST_AUTO_TEST_CASE(cse_verbatim_sload_sideeffect) +{ + auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0}; + AssemblyItems input{ + u256(0), + Instruction::SLOAD, + verbatim, + u256(0), + Instruction::SLOAD, + }; + + checkFullCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(cse_verbatim_eq) +{ + auto verbatim = AssemblyItem{bytes{1, 2, 3, 4, 5}, 0, 0}; + AssemblyItems input{ + u256(0), + Instruction::SLOAD, + verbatim, + Instruction::DUP1, + Instruction::EQ + }; + + checkFullCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(verbatim_knownstate) +{ + KnownState state = createInitialState(AssemblyItems{ + Instruction::DUP1, + Instruction::DUP2, + Instruction::DUP3, + Instruction::DUP4 + }); + map const& stackElements = state.stackElements(); + + BOOST_CHECK(state.stackHeight() == 4); + // One more than stack height because of the initial unknown element. + BOOST_CHECK(stackElements.size() == 5); + BOOST_CHECK(stackElements.count(0)); + unsigned initialElement = stackElements.at(0); + // Check if all the DUPs were correctly matched to the same class. + for (auto const& height: {1, 2, 3, 4}) + BOOST_CHECK(stackElements.at(height) == initialElement); + + auto verbatim2i5o = AssemblyItem{bytes{1, 2, 3, 4, 5}, 2, 5}; + state.feedItem(verbatim2i5o); + + BOOST_CHECK(state.stackHeight() == 7); + // Stack elements + // Before verbatim: {{0, x}, {1, x}, {2, x}, {3, x}, {4, x}} + // After verbatim: {{0, x}, {1, x}, {2, x}, {3, a}, {4, b}, {5, c}, {6, d}, {7, e}} + BOOST_CHECK(stackElements.size() == 8); + + for (auto const& height: {1, 2}) + BOOST_CHECK(stackElements.at(height) == initialElement); + + for (auto const& height: {3, 4, 5, 6, 7}) + BOOST_CHECK(stackElements.at(height) != initialElement); + + for (auto const& height1: {3, 4, 5, 6, 7}) + for (auto const& height2: {3, 4, 5, 6, 7}) + if (height1 < height2) + BOOST_CHECK(stackElements.at(height1) != stackElements.at(height2)); +} + BOOST_AUTO_TEST_CASE(cse_remove_redundant_shift_masking) { if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) diff --git a/test/libyul/yulOptimizerTests/loadResolver/verbatim_mload.yul b/test/libyul/yulOptimizerTests/loadResolver/verbatim_mload.yul new file mode 100644 index 000000000..150c0240b --- /dev/null +++ b/test/libyul/yulOptimizerTests/loadResolver/verbatim_mload.yul @@ -0,0 +1,16 @@ +{ + mstore(10, 20) + // cannot be resolved because of verbatim + sstore(0, mload(10)) + verbatim_0i_0o("test") +} +// ---- +// step: loadResolver +// +// { +// let _1 := 20 +// let _2 := 10 +// mstore(_2, _1) +// sstore(0, mload(_2)) +// verbatim_0i_0o("test") +// } diff --git a/test/libyul/yulOptimizerTests/loadResolver/verbatim_sload.yul b/test/libyul/yulOptimizerTests/loadResolver/verbatim_sload.yul new file mode 100644 index 000000000..ba9cb32d8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/loadResolver/verbatim_sload.yul @@ -0,0 +1,21 @@ +{ + sstore(10, 20) + // will be resolved + sstore(30, sload(10)) + verbatim_0i_0o("test") + // will not be resolved + sstore(30, sload(10)) +} +// ---- +// step: loadResolver +// +// { +// let _1 := 20 +// let _2 := 10 +// sstore(_2, _1) +// let _4 := _1 +// let _5 := 30 +// sstore(_5, _4) +// verbatim_0i_0o("test") +// sstore(_5, sload(_2)) +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/verbatim.yul b/test/libyul/yulOptimizerTests/unusedPruner/verbatim.yul new file mode 100644 index 000000000..65b464f7f --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/verbatim.yul @@ -0,0 +1,17 @@ +{ + // cannot be removed because of verbatim + let a := mload(10) + // cannot be removed because of verbatim + let b := keccak256(10, 32) + // can be removed + let c := add(a, b) + verbatim_0i_0o("test") +} +// ---- +// step: unusedPruner +// +// { +// pop(mload(10)) +// pop(keccak256(10, 32)) +// verbatim_0i_0o("test") +// } From e1ae4e37a02db42e1e15685d96c0800eef474a23 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 28 Apr 2021 16:15:58 +0200 Subject: [PATCH 018/235] Allow user-defined functions called like builtins. --- test/libsolidity/util/TestFileParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/libsolidity/util/TestFileParser.cpp b/test/libsolidity/util/TestFileParser.cpp index 64ca552b8..f6261f2b7 100644 --- a/test/libsolidity/util/TestFileParser.cpp +++ b/test/libsolidity/util/TestFileParser.cpp @@ -204,7 +204,7 @@ pair TestFileParser::parseFunctionSignature() expect(Token::Identifier); } - if (isBuiltinFunction(signature)) + if (isBuiltinFunction(signature) && m_scanner.currentToken() != Token::LParen) return {signature, false}; signature += formatToken(Token::LParen); From 19ad9fac996ad56b31ac6f2cbc5d0798912e84a7 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Thu, 18 Feb 2021 13:33:05 -0500 Subject: [PATCH 019/235] [isoltest] Add support to query balance. --- test/libsolidity/SemanticTest.cpp | 11 +++++++ .../balance_other_contract.sol | 29 +++++++++++++++++++ .../isoltestTesting/balance_with_balance.sol | 8 +++++ .../isoltestTesting/balance_with_balance2.sol | 8 +++++ .../balance_without_balance.sol | 7 +++++ .../smoke_test.sol | 0 .../storage/storage_empty.sol | 0 .../storage/storage_nonempty.sol | 0 ...o_nonpayable_circumvention_by_modifier.sol | 6 +--- .../semanticTests/smoke/constructor.sol | 2 ++ .../semanticTests/smoke/fallback.sol | 2 +- 11 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol create mode 100644 test/libsolidity/semanticTests/isoltestTesting/balance_with_balance.sol create mode 100644 test/libsolidity/semanticTests/isoltestTesting/balance_with_balance2.sol create mode 100644 test/libsolidity/semanticTests/isoltestTesting/balance_without_balance.sol rename test/libsolidity/semanticTests/{builtins => isoltestTesting}/smoke_test.sol (100%) rename test/libsolidity/semanticTests/{builtins => isoltestTesting}/storage/storage_empty.sol (100%) rename test/libsolidity/semanticTests/{builtins => isoltestTesting}/storage/storage_nonempty.sol (100%) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 51a5a3c9c..004459277 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -125,6 +125,17 @@ void SemanticTest::initializeBuiltins() { return util::toBigEndian(u256(0x1234)); }; + soltestAssert(m_builtins.count("balance") == 0, ""); + m_builtins["balance"] = [this](FunctionCall const& _call) -> std::optional + { + soltestAssert(_call.arguments.parameters.size() <= 1, "Account address expected."); + h160 address; + if (_call.arguments.parameters.size() == 1) + address = h160(_call.arguments.parameters.at(0).rawString); + else + address = m_contractAddress; + return util::toBigEndian(SolidityExecutionFramework::balanceAt(address)); + }; soltestAssert(m_builtins.count("storageEmpty") == 0, ""); m_builtins["storageEmpty"] = [this](FunctionCall const& _call) -> std::optional { diff --git a/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol b/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol new file mode 100644 index 000000000..59ad2cd84 --- /dev/null +++ b/test/libsolidity/semanticTests/isoltestTesting/balance_other_contract.sol @@ -0,0 +1,29 @@ +contract Other { + constructor() payable { + } + function getAddress() public returns (address) { + return address(this); + } +} +contract ClientReceipt { + Other other; + constructor() payable { + other = new Other{value:500}(); + } + function getAddress() public returns (address) { + return other.getAddress(); + } +} +// ==== +// compileViaYul: also +// ---- +// constructor(), 2000 wei -> +// gas irOptimized: 191881 +// gas legacy: 235167 +// gas legacyOptimized: 180756 +// balance -> 1500 +// gas irOptimized: 191881 +// gas legacy: 235167 +// gas legacyOptimized: 180756 +// getAddress() -> 0xf01f7809444bd9a93a854361c6fae3f23d9e23db +// balance: 0xf01f7809444bd9a93a854361c6fae3f23d9e23db -> 500 diff --git a/test/libsolidity/semanticTests/isoltestTesting/balance_with_balance.sol b/test/libsolidity/semanticTests/isoltestTesting/balance_with_balance.sol new file mode 100644 index 000000000..5b6324910 --- /dev/null +++ b/test/libsolidity/semanticTests/isoltestTesting/balance_with_balance.sol @@ -0,0 +1,8 @@ +contract ClientReceipt { + constructor() payable {} +} +// ==== +// compileViaYul: also +// ---- +// constructor(), 1000 wei -> +// balance -> 1000 diff --git a/test/libsolidity/semanticTests/isoltestTesting/balance_with_balance2.sol b/test/libsolidity/semanticTests/isoltestTesting/balance_with_balance2.sol new file mode 100644 index 000000000..f7d62afe5 --- /dev/null +++ b/test/libsolidity/semanticTests/isoltestTesting/balance_with_balance2.sol @@ -0,0 +1,8 @@ +contract ClientReceipt { + constructor() payable {} +} +// ==== +// compileViaYul: also +// ---- +// constructor(), 1 ether -> +// balance -> 1000000000000000000 diff --git a/test/libsolidity/semanticTests/isoltestTesting/balance_without_balance.sol b/test/libsolidity/semanticTests/isoltestTesting/balance_without_balance.sol new file mode 100644 index 000000000..b89977035 --- /dev/null +++ b/test/libsolidity/semanticTests/isoltestTesting/balance_without_balance.sol @@ -0,0 +1,7 @@ +contract ClientReceipt { +} +// ==== +// compileViaYul: also +// ---- +// balance -> 0 +// balance: 0x0000000000000000000000000000000000000000 -> 0 diff --git a/test/libsolidity/semanticTests/builtins/smoke_test.sol b/test/libsolidity/semanticTests/isoltestTesting/smoke_test.sol similarity index 100% rename from test/libsolidity/semanticTests/builtins/smoke_test.sol rename to test/libsolidity/semanticTests/isoltestTesting/smoke_test.sol diff --git a/test/libsolidity/semanticTests/builtins/storage/storage_empty.sol b/test/libsolidity/semanticTests/isoltestTesting/storage/storage_empty.sol similarity index 100% rename from test/libsolidity/semanticTests/builtins/storage/storage_empty.sol rename to test/libsolidity/semanticTests/isoltestTesting/storage/storage_empty.sol diff --git a/test/libsolidity/semanticTests/builtins/storage/storage_nonempty.sol b/test/libsolidity/semanticTests/isoltestTesting/storage/storage_nonempty.sol similarity index 100% rename from test/libsolidity/semanticTests/builtins/storage/storage_nonempty.sol rename to test/libsolidity/semanticTests/isoltestTesting/storage/storage_nonempty.sol diff --git a/test/libsolidity/semanticTests/payable/no_nonpayable_circumvention_by_modifier.sol b/test/libsolidity/semanticTests/payable/no_nonpayable_circumvention_by_modifier.sol index cb456a412..b24ca2291 100644 --- a/test/libsolidity/semanticTests/payable/no_nonpayable_circumvention_by_modifier.sol +++ b/test/libsolidity/semanticTests/payable/no_nonpayable_circumvention_by_modifier.sol @@ -8,14 +8,10 @@ contract C { function msgvalue() internal returns (uint) { return msg.value; } - // TODO: remove this helper function once isoltest supports balance checking - function balance() external returns (uint) { - return address(this).balance; - } } // ==== // compileViaYul: also // compileToEwasm: also // ---- // f(), 27 wei -> FAILURE -// balance() -> 0 +// balance -> 0 diff --git a/test/libsolidity/semanticTests/smoke/constructor.sol b/test/libsolidity/semanticTests/smoke/constructor.sol index 9cf435087..3837fe071 100644 --- a/test/libsolidity/semanticTests/smoke/constructor.sol +++ b/test/libsolidity/semanticTests/smoke/constructor.sol @@ -14,7 +14,9 @@ contract C { // compileViaYul: also // ---- // constructor(), 2 wei: 3 -> +// gas legacy: 148000 // state() -> 3 // balance() -> 2 +// balance -> 2 // update(uint256): 4 // state() -> 4 diff --git a/test/libsolidity/semanticTests/smoke/fallback.sol b/test/libsolidity/semanticTests/smoke/fallback.sol index c6aa251cd..f789ed276 100644 --- a/test/libsolidity/semanticTests/smoke/fallback.sol +++ b/test/libsolidity/semanticTests/smoke/fallback.sol @@ -9,8 +9,8 @@ contract A { } } // ==== -// compileViaYul: also // compileToEwasm: also +// compileViaYul: also // ---- // data() -> 0 // () From b06a09fd8208fa5ff26bbdb5cb74057e0d2ad8b4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Mar 2021 17:33:21 +0100 Subject: [PATCH 020/235] Fix forwarding revert. --- libsolidity/codegen/YulUtilFunctions.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 5134822bb..08dd6bc20 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -3792,11 +3792,13 @@ string YulUtilFunctions::forwardingRevertFunction() if (forward) return Whiskers(R"( function () { - returndatacopy(0, 0, returndatasize()) - revert(0, returndatasize()) + let pos := () + returndatacopy(pos, 0, returndatasize()) + revert(pos, returndatasize()) } )") ("functionName", functionName) + ("allocateUnbounded", allocateUnboundedFunction()) .render(); else return Whiskers(R"( From f30ef06d627e1703ac903435cbd055dbc138473b Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Apr 2021 15:06:33 +0200 Subject: [PATCH 021/235] Update tests. --- test/cmdlineTests/ir_compiler_subobjects/output | 5 +++-- test/cmdlineTests/standard_viair_requested/output.json | 5 +++-- test/cmdlineTests/viair_subobjects/output | 9 +++++---- .../semanticTests/array/function_array_cross_calls.sol | 2 +- .../semanticTests/functionCall/failed_create.sol | 6 +++--- .../semanticTests/functionTypes/store_function.sol | 2 +- .../semanticTests/salted_create/salted_create.sol | 2 +- .../various/staticcall_for_view_and_pure.sol | 4 ++-- 8 files changed, 19 insertions(+), 16 deletions(-) diff --git a/test/cmdlineTests/ir_compiler_subobjects/output b/test/cmdlineTests/ir_compiler_subobjects/output index 20c2cee8c..41a19e81c 100644 --- a/test/cmdlineTests/ir_compiler_subobjects/output +++ b/test/cmdlineTests/ir_compiler_subobjects/output @@ -66,8 +66,9 @@ object "D_16" { datacopy(128, dataoffset("C_3"), _2) if iszero(create(_1, 128, _2)) { - returndatacopy(_1, _1, returndatasize()) - revert(_1, returndatasize()) + let pos := mload(64) + returndatacopy(pos, _1, returndatasize()) + revert(pos, returndatasize()) } return(mload(64), _1) } diff --git a/test/cmdlineTests/standard_viair_requested/output.json b/test/cmdlineTests/standard_viair_requested/output.json index 6972dd5b2..74c47d6a0 100644 --- a/test/cmdlineTests/standard_viair_requested/output.json +++ b/test/cmdlineTests/standard_viair_requested/output.json @@ -136,8 +136,9 @@ object \"D_16\" { } function revert_forward_1() { - returndatacopy(0, 0, returndatasize()) - revert(0, returndatasize()) + let pos := allocate_unbounded() + returndatacopy(pos, 0, returndatasize()) + revert(pos, returndatasize()) } function shift_right_224_unsigned(value) -> newValue { diff --git a/test/cmdlineTests/viair_subobjects/output b/test/cmdlineTests/viair_subobjects/output index 135683bf9..e53814796 100644 --- a/test/cmdlineTests/viair_subobjects/output +++ b/test/cmdlineTests/viair_subobjects/output @@ -35,9 +35,9 @@ object "C_3" { ======= viair_subobjects/input.sol:D ======= Binary: -608060405234156100105760006000fd5b60b680610020600039806000f350fe60806040526004361015156087576000803560e01c6326121ff0141560855734156027578081fd5b80600319360112156036578081fd5b6028806080016080811067ffffffffffffffff82111715606457634e487b7160e01b83526041600452602483fd5b508061008e60803980608083f01515607e573d82833e3d82fd5b5080604051f35b505b60006000fdfe60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd +608060405234156100105760006000fd5b60ba80610020600039806000f350fe6080604052600436101515608b576000803560e01c6326121ff0141560895734156027578081fd5b80600319360112156036578081fd5b6028806080016080811067ffffffffffffffff82111715606457634e487b7160e01b83526041600452602483fd5b508061009260803980608083f015156082576040513d83823e3d81fd505b5080604051f35b505b60006000fdfe60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd Binary of the runtime part: -60806040526004361015156087576000803560e01c6326121ff0141560855734156027578081fd5b80600319360112156036578081fd5b6028806080016080811067ffffffffffffffff82111715606457634e487b7160e01b83526041600452602483fd5b508061008e60803980608083f01515607e573d82833e3d82fd5b5080604051f35b505b60006000fdfe60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd +6080604052600436101515608b576000803560e01c6326121ff0141560895734156027578081fd5b80600319360112156036578081fd5b6028806080016080811067ffffffffffffffff82111715606457634e487b7160e01b83526041600452602483fd5b508061009260803980608083f015156082576040513d83823e3d81fd505b5080604051f35b505b60006000fdfe60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd Optimized IR: /******************************************************* * WARNING * @@ -78,8 +78,9 @@ object "D_16" { datacopy(128, dataoffset("C_3"), _2) if iszero(create(_1, 128, _2)) { - returndatacopy(_1, _1, returndatasize()) - revert(_1, returndatasize()) + let pos := mload(64) + returndatacopy(pos, _1, returndatasize()) + revert(pos, returndatasize()) } return(mload(64), _1) } diff --git a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol index c120d3bc7..bf7816b51 100644 --- a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol +++ b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol @@ -45,6 +45,6 @@ contract C { // compileViaYul: also // ---- // test() -> 5, 6, 7 -// gas irOptimized: 345542 +// gas irOptimized: 345942 // gas legacy: 500424 // gas legacyOptimized: 309013 diff --git a/test/libsolidity/semanticTests/functionCall/failed_create.sol b/test/libsolidity/semanticTests/functionCall/failed_create.sol index d84b2d02b..f74427462 100644 --- a/test/libsolidity/semanticTests/functionCall/failed_create.sol +++ b/test/libsolidity/semanticTests/functionCall/failed_create.sol @@ -18,15 +18,15 @@ contract C { // compileViaYul: also // ---- // constructor(), 20 wei -// gas irOptimized: 265125 +// gas irOptimized: 255579 // gas legacy: 285485 -// gas legacyOptimized: 177957 +// gas legacyOptimized: 177933 // f(uint256): 20 -> 1370859564726510389319704988634906228201275401179 // x() -> 1 // f(uint256): 20 -> FAILURE // x() -> 1 // stack(uint256): 1023 -> FAILURE -// gas irOptimized: 853785 +// gas irOptimized: 856335 // gas legacy: 981671 // gas legacyOptimized: 824895 // x() -> 1 diff --git a/test/libsolidity/semanticTests/functionTypes/store_function.sol b/test/libsolidity/semanticTests/functionTypes/store_function.sol index d625c76ba..3ba14b0da 100644 --- a/test/libsolidity/semanticTests/functionTypes/store_function.sol +++ b/test/libsolidity/semanticTests/functionTypes/store_function.sol @@ -28,6 +28,6 @@ contract C { // compileViaYul: also // ---- // t() -> 9 -// gas irOptimized: 103946 +// gas irOptimized: 103941 // gas legacy: 161097 // gas legacyOptimized: 112116 diff --git a/test/libsolidity/semanticTests/salted_create/salted_create.sol b/test/libsolidity/semanticTests/salted_create/salted_create.sol index d73b25dd6..be2d7bf35 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create.sol @@ -22,6 +22,6 @@ contract A { // ---- // different_salt() -> true // same_salt() -> true -// gas irOptimized: 98438968 +// gas irOptimized: 98438966 // gas legacy: 98439116 // gas legacyOptimized: 98438970 diff --git a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol index 1d3a435c2..2623bdcae 100644 --- a/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol +++ b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol @@ -38,10 +38,10 @@ contract D { // f() -> 0x1 # This should work, next should throw # // gas legacy: 102944 // fview() -> FAILURE -// gas irOptimized: 98438658 +// gas irOptimized: 98438664 // gas legacy: 98438822 // gas legacyOptimized: 98438615 // fpure() -> FAILURE -// gas irOptimized: 98438658 +// gas irOptimized: 98438664 // gas legacy: 98438822 // gas legacyOptimized: 98438616 From 79acebe46ffc69a8fa3f15f8c7f97b848e9a0b74 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Mar 2021 16:57:56 +0100 Subject: [PATCH 022/235] Allocate for returning runtime code. --- libsolidity/codegen/ir/IRGenerator.cpp | 45 +++++++++----------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 4837b3ff2..a1d1522a6 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -802,29 +802,24 @@ void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contra string IRGenerator::deployCode(ContractDefinition const& _contract) { Whiskers t(R"X( - <#loadImmutables> - let := mload() - - - codecopy(0, dataoffset(""), datasize("")) - - <#storeImmutables> - setimmutable(0, "", ) - - - return(0, datasize("")) + let := () + codecopy(, dataoffset(""), datasize("")) + <#immutables> + setimmutable(, "", ) + + return(, datasize("")) )X"); + t("allocateUnbounded", m_utils.allocateUnboundedFunction()); + t("codeOffset", m_context.newYulVariable()); t("object", IRNames::deployedObject(_contract)); - vector> loadImmutables; - vector> storeImmutables; - + vector> immutables; if (_contract.isLibrary()) { solAssert(ContractType(_contract).immutableVariables().empty(), ""); - storeImmutables.emplace_back(map{ - {"var"s, "address()"}, - {"immutableName"s, IRNames::libraryAddressImmutable()} + immutables.emplace_back(map{ + {"immutableName"s, IRNames::libraryAddressImmutable()}, + {"value"s, "address()"} }); } @@ -833,20 +828,12 @@ string IRGenerator::deployCode(ContractDefinition const& _contract) { solUnimplementedAssert(immutable->type()->isValueType(), ""); solUnimplementedAssert(immutable->type()->sizeOnStack() == 1, ""); - string yulVar = m_context.newYulVariable(); - loadImmutables.emplace_back(map{ - {"var"s, yulVar}, - {"memoryOffset"s, to_string(m_context.immutableMemoryOffset(*immutable))} - }); - storeImmutables.emplace_back(map{ - {"var"s, yulVar}, - {"immutableName"s, to_string(immutable->id())} + immutables.emplace_back(map{ + {"immutableName"s, to_string(immutable->id())}, + {"value"s, "mload(" + to_string(m_context.immutableMemoryOffset(*immutable)) + ")"} }); } - t("loadImmutables", std::move(loadImmutables)); - // reverse order to ease stack strain - reverse(storeImmutables.begin(), storeImmutables.end()); - t("storeImmutables", std::move(storeImmutables)); + t("immutables", std::move(immutables)); return t.render(); } From fcc98d12b81b26d0dfcd8342cb3211b0a88805fc Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Apr 2021 11:40:54 +0200 Subject: [PATCH 023/235] Gas updates. --- .../abi_encode_v2_in_function_inherited_in_v1_contract.sol | 2 +- .../abi_encode_v2_in_modifier_used_in_v1_contract.sol | 2 +- .../semanticTests/array/fixed_arrays_as_return_type.sol | 2 +- .../semanticTests/array/function_array_cross_calls.sol | 2 +- test/libsolidity/semanticTests/array/reusing_memory.sol | 2 +- .../semanticTests/constructor/arrays_in_constructors.sol | 2 +- .../constructor/bytes_in_constructors_packer.sol | 2 +- .../semanticTests/constructor/no_callvalue_check.sol | 2 +- .../semanticTests/functionTypes/store_function.sol | 2 +- test/libsolidity/semanticTests/immutable/multi_creation.sol | 2 +- .../inheritance/address_overload_resolution.sol | 4 ++-- .../inherited_function_calldata_calldata_interface.sol | 2 +- .../inherited_function_calldata_memory_interface.sol | 2 +- .../semanticTests/interface_inheritance_conversions.sol | 6 +++--- .../salted_create/salted_create_with_value.sol | 2 +- 15 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol index ea22588df..5a1ed846a 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_function_inherited_in_v1_contract.sol @@ -30,6 +30,6 @@ contract C is B { // compileViaYul: also // ---- // test() -> 77 -// gas irOptimized: 133623 +// gas irOptimized: 133635 // gas legacy: 156573 // gas legacyOptimized: 112940 diff --git a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol index 3f68dd068..d5eb7b2c5 100644 --- a/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol +++ b/test/libsolidity/semanticTests/abiEncoderV2/abi_encode_v2_in_modifier_used_in_v1_contract.sol @@ -38,5 +38,5 @@ contract C is B { // compileViaYul: also // ---- // test() -> 5, 10 -// gas irOptimized: 92612 +// gas irOptimized: 92624 // gas legacy: 100441 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol index c0a1c4a9a..8e9c7ef53 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol @@ -21,6 +21,6 @@ contract B { // compileViaYul: also // ---- // f() -> 2, 3, 4, 5, 6, 1000, 1001, 1002, 1003, 1004 -// gas irOptimized: 135871 +// gas irOptimized: 135883 // gas legacy: 264410 // gas legacyOptimized: 135699 diff --git a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol index bf7816b51..8a8590434 100644 --- a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol +++ b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol @@ -45,6 +45,6 @@ contract C { // compileViaYul: also // ---- // test() -> 5, 6, 7 -// gas irOptimized: 345942 +// gas irOptimized: 345955 // gas legacy: 500424 // gas legacyOptimized: 309013 diff --git a/test/libsolidity/semanticTests/array/reusing_memory.sol b/test/libsolidity/semanticTests/array/reusing_memory.sol index d8e1bfcb8..08deac565 100644 --- a/test/libsolidity/semanticTests/array/reusing_memory.sol +++ b/test/libsolidity/semanticTests/array/reusing_memory.sol @@ -26,6 +26,6 @@ contract Main { // compileViaYul: also // ---- // f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 -// gas irOptimized: 115528 +// gas irOptimized: 115543 // gas legacy: 127152 // gas legacyOptimized: 113679 diff --git a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol index d448f55c8..a9f65676b 100644 --- a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol @@ -26,6 +26,6 @@ contract Creator { // compileViaYul: also // ---- // f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 -// gas irOptimized: 474619 +// gas irOptimized: 474718 // gas legacy: 570900 // gas legacyOptimized: 436724 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index 12b9beeb2..e12576a4e 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -26,6 +26,6 @@ contract Creator { // compileViaYul: also // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" -// gas irOptimized: 330923 +// gas irOptimized: 330976 // gas legacy: 414850 // gas legacyOptimized: 292281 diff --git a/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol b/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol index b60a7b5ba..10b87a9e0 100644 --- a/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol +++ b/test/libsolidity/semanticTests/constructor/no_callvalue_check.sol @@ -19,6 +19,6 @@ contract C { // compileViaYul: also // ---- // f(), 2000 ether -> true -// gas irOptimized: 123725 +// gas irOptimized: 123743 // gas legacy: 123226 // gas legacyOptimized: 123092 diff --git a/test/libsolidity/semanticTests/functionTypes/store_function.sol b/test/libsolidity/semanticTests/functionTypes/store_function.sol index 3ba14b0da..e48ff21e1 100644 --- a/test/libsolidity/semanticTests/functionTypes/store_function.sol +++ b/test/libsolidity/semanticTests/functionTypes/store_function.sol @@ -28,6 +28,6 @@ contract C { // compileViaYul: also // ---- // t() -> 9 -// gas irOptimized: 103941 +// gas irOptimized: 103953 // gas legacy: 161097 // gas legacyOptimized: 112116 diff --git a/test/libsolidity/semanticTests/immutable/multi_creation.sol b/test/libsolidity/semanticTests/immutable/multi_creation.sol index 0f4e3b44b..1b10dd3c5 100644 --- a/test/libsolidity/semanticTests/immutable/multi_creation.sol +++ b/test/libsolidity/semanticTests/immutable/multi_creation.sol @@ -29,7 +29,7 @@ contract C { // compileViaYul: also // ---- // f() -> 3, 7, 5 -// gas irOptimized: 131350 +// gas irOptimized: 131380 // gas legacy: 153990 // gas legacyOptimized: 127822 // x() -> 7 diff --git a/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol b/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol index a18f79aac..3f988d9bf 100644 --- a/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol +++ b/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol @@ -23,8 +23,8 @@ contract D { // compileViaYul: also // ---- // f() -> 1 -// gas irOptimized: 86492 +// gas irOptimized: 86504 // gas legacy: 114412 // g() -> 5 -// gas irOptimized: 86588 +// gas irOptimized: 86600 // gas legacy: 114872 diff --git a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol index 028dfd7db..44688ada4 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol @@ -25,5 +25,5 @@ contract B { // compileViaYul: also // ---- // g() -> 42 -// gas irOptimized: 90623 +// gas irOptimized: 90635 // gas legacy: 117797 diff --git a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol index ddc88fe9d..3b5ea5ee8 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol @@ -25,6 +25,6 @@ contract B { // compileViaYul: also // ---- // g() -> 42 -// gas irOptimized: 119646 +// gas irOptimized: 119658 // gas legacy: 180597 // gas legacyOptimized: 117351 diff --git a/test/libsolidity/semanticTests/interface_inheritance_conversions.sol b/test/libsolidity/semanticTests/interface_inheritance_conversions.sol index 8cd65d9bd..a2e45fbc3 100644 --- a/test/libsolidity/semanticTests/interface_inheritance_conversions.sol +++ b/test/libsolidity/semanticTests/interface_inheritance_conversions.sol @@ -37,10 +37,10 @@ contract C { // compileViaYul: also // ---- // convertParent() -> 1 -// gas irOptimized: 103625 +// gas irOptimized: 103637 // convertSubA() -> 1, 2 -// gas irOptimized: 105708 +// gas irOptimized: 105720 // gas legacy: 101703 // convertSubB() -> 1, 3 -// gas irOptimized: 105642 +// gas irOptimized: 105654 // gas legacy: 101637 diff --git a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol index ee3d77253..cc279c917 100644 --- a/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol +++ b/test/libsolidity/semanticTests/salted_create/salted_create_with_value.sol @@ -22,6 +22,6 @@ contract A { // compileViaYul: also // ---- // f(), 10 ether -> 3007, 3008, 3009 -// gas irOptimized: 294216 +// gas irOptimized: 294279 // gas legacy: 422027 // gas legacyOptimized: 287256 From 426d60f07bc264df1aab4a06890c989cb618ba7c Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Apr 2021 11:43:27 +0200 Subject: [PATCH 024/235] Commandline test updates. --- .../constant_optimizer_yul/output | 4 +-- test/cmdlineTests/exp_base_literal/output | 9 ++++-- .../output | 8 ++--- .../ir_compiler_subobjects/output | 12 ++++---- .../output | 4 +-- .../output | 4 +-- .../keccak_optimization_deploy_code/output | 4 +-- .../keccak_optimization_low_runs/output | 4 +-- test/cmdlineTests/name_simplifier/output | 4 +-- .../cmdlineTests/optimizer_array_sload/output | 4 +-- .../standard_ewasm_requested/output.json | 30 +++++++++++++++---- .../output.json | 7 +++-- .../standard_ir_requested/output.json | 9 ++++-- .../standard_viair_requested/output.json | 27 +++++++++++++---- test/cmdlineTests/viair_abicoder_v1/output | 9 ++++-- test/cmdlineTests/viair_subobjects/output | 18 +++++------ test/cmdlineTests/yul_optimizer_steps/output | 7 +++-- .../yul_string_format_ascii/output.json | 9 ++++-- .../output.json | 9 ++++-- .../output.json | 9 ++++-- .../yul_string_format_ascii_long/output.json | 9 ++++-- .../yul_string_format_hex/output.json | 9 ++++-- 22 files changed, 144 insertions(+), 65 deletions(-) diff --git a/test/cmdlineTests/constant_optimizer_yul/output b/test/cmdlineTests/constant_optimizer_yul/output index 9a125bd36..181a8105b 100644 --- a/test/cmdlineTests/constant_optimizer_yul/output +++ b/test/cmdlineTests/constant_optimizer_yul/output @@ -13,8 +13,8 @@ object "C_12" { if callvalue() { revert(0, 0) } sstore(0, shl(180, 1)) let _1 := datasize("C_12_deployed") - codecopy(0, dataoffset("C_12_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("C_12_deployed"), _1) + return(128, _1) } } object "C_12_deployed" { diff --git a/test/cmdlineTests/exp_base_literal/output b/test/cmdlineTests/exp_base_literal/output index d75851fd4..698180169 100644 --- a/test/cmdlineTests/exp_base_literal/output +++ b/test/cmdlineTests/exp_base_literal/output @@ -14,9 +14,14 @@ object "C_81" { constructor_C_81() - codecopy(0, dataoffset("C_81_deployed"), datasize("C_81_deployed")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("C_81_deployed"), datasize("C_81_deployed")) - return(0, datasize("C_81_deployed")) + return(_1, datasize("C_81_deployed")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_C_81() { diff --git a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output index 418676634..829981fcf 100644 --- a/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output +++ b/test/cmdlineTests/ir_compiler_inheritance_nosubobjects/output @@ -12,8 +12,8 @@ object "C_7" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_7_deployed") - codecopy(0, dataoffset("C_7_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("C_7_deployed"), _1) + return(128, _1) } } object "C_7_deployed" { @@ -40,8 +40,8 @@ object "D_10" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_10_deployed") - codecopy(0, dataoffset("D_10_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("D_10_deployed"), _1) + return(128, _1) } } object "D_10_deployed" { diff --git a/test/cmdlineTests/ir_compiler_subobjects/output b/test/cmdlineTests/ir_compiler_subobjects/output index 41a19e81c..96ebe2727 100644 --- a/test/cmdlineTests/ir_compiler_subobjects/output +++ b/test/cmdlineTests/ir_compiler_subobjects/output @@ -12,8 +12,8 @@ object "C_3" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") - codecopy(0, dataoffset("C_3_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("C_3_deployed"), _1) + return(128, _1) } } object "C_3_deployed" { @@ -40,8 +40,8 @@ object "D_16" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_16_deployed") - codecopy(0, dataoffset("D_16_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("D_16_deployed"), _1) + return(128, _1) } } object "D_16_deployed" { @@ -82,8 +82,8 @@ object "D_16" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") - codecopy(0, dataoffset("C_3_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("C_3_deployed"), _1) + return(128, _1) } } object "C_3_deployed" { diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output index ff7449119..72ed66a1d 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_creation/output @@ -12,8 +12,8 @@ object "D_12" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_12_deployed") - codecopy(0, dataoffset("D_12_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("D_12_deployed"), _1) + return(128, _1) } } object "D_12_deployed" { diff --git a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output index 2ed33af2e..a04c2f13b 100644 --- a/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output +++ b/test/cmdlineTests/ir_with_assembly_no_memoryguard_runtime/output @@ -12,8 +12,8 @@ object "D_8" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_8_deployed") - codecopy(0, dataoffset("D_8_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("D_8_deployed"), _1) + return(128, _1) } } object "D_8_deployed" { diff --git a/test/cmdlineTests/keccak_optimization_deploy_code/output b/test/cmdlineTests/keccak_optimization_deploy_code/output index 5bdfaf647..48721502a 100644 --- a/test/cmdlineTests/keccak_optimization_deploy_code/output +++ b/test/cmdlineTests/keccak_optimization_deploy_code/output @@ -14,8 +14,8 @@ object "C_12" { mstore(0, 100) sstore(0, keccak256(0, 32)) let _1 := datasize("C_12_deployed") - codecopy(0, dataoffset("C_12_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("C_12_deployed"), _1) + return(128, _1) } } object "C_12_deployed" { diff --git a/test/cmdlineTests/keccak_optimization_low_runs/output b/test/cmdlineTests/keccak_optimization_low_runs/output index ab6027035..c2e9b023a 100644 --- a/test/cmdlineTests/keccak_optimization_low_runs/output +++ b/test/cmdlineTests/keccak_optimization_low_runs/output @@ -12,8 +12,8 @@ object "C_7" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_7_deployed") - codecopy(0, dataoffset("C_7_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("C_7_deployed"), _1) + return(128, _1) } } object "C_7_deployed" { diff --git a/test/cmdlineTests/name_simplifier/output b/test/cmdlineTests/name_simplifier/output index 74c654798..526e5ce0f 100644 --- a/test/cmdlineTests/name_simplifier/output +++ b/test/cmdlineTests/name_simplifier/output @@ -12,8 +12,8 @@ object "C_59" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_59_deployed") - codecopy(0, dataoffset("C_59_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("C_59_deployed"), _1) + return(128, _1) } } object "C_59_deployed" { diff --git a/test/cmdlineTests/optimizer_array_sload/output b/test/cmdlineTests/optimizer_array_sload/output index 0576e2890..f5a3cf77d 100644 --- a/test/cmdlineTests/optimizer_array_sload/output +++ b/test/cmdlineTests/optimizer_array_sload/output @@ -12,8 +12,8 @@ object "Arraysum_34" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("Arraysum_34_deployed") - codecopy(0, dataoffset("Arraysum_34_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("Arraysum_34_deployed"), _1) + return(128, _1) } } object "Arraysum_34_deployed" { diff --git a/test/cmdlineTests/standard_ewasm_requested/output.json b/test/cmdlineTests/standard_ewasm_requested/output.json index aaa63db62..d3eef7f27 100644 --- a/test/cmdlineTests/standard_ewasm_requested/output.json +++ b/test/cmdlineTests/standard_ewasm_requested/output.json @@ -1,4 +1,4 @@ -{"contracts":{"A":{"C":{"ewasm":{"wasm":"0061736d010000000125086000006000017e6000017f60017e017f60017f0060017f017f60027f7f0060037f7f7f0002510408657468657265756d08636f6465436f7079000708657468657265756d06726576657274000608657468657265756d0c67657443616c6c56616c7565000408657468657265756d0666696e697368000603090800020302020505010503010001060100071102066d656d6f72790200046d61696e000400b6030c435f335f6465706c6f7965640061736d010000000112046000006000017f60017f017f60027f7f0002130108657468657265756d06726576657274000303060500010102020503010001060100071102066d656d6f72790200046d61696e00010ad20205a10104027f017e017f047e024010022100200041c0006a210120012000490440000b420021022002a7210320031005ad42208621042002422088210520042005a71005ad84210620012006370000200141086a2006370000200141106a2006370000428001a71005ad4220862107200141186a2007428001422088a71005ad8437000020022002200284200284520440000b20022005520440000b1003200310000b0b2b01017f024042004200420084420084520440000b420042c000422088520440000b42c000a721000b20000b4203017f017e017f02404200210120012001200184200184520440000b20012001422088520440000b2001a72102200241c0006a210020002002490440000b0b20000b1f01017f024020004108744180fe0371200041087641ff01717221010b20010b1e01027f02402000100441107421022002200041107610047221010b20010b0aa603089a0102027f047e024010072100200041c0006a210120012000490440000b4200a7100aad422086210220024200422088a7100aad84210320012003370000200141086a2003370000200141106a2003370000200141186a100b37000041001002410829000021044200420084200441002900008484504504401008100510010b42a9032105100842b801100620051006100010082005100610030b0b2f02017f017e02404200210120012001200184200184520440000b20012001422088520440000b2001a721000b20000b2901017f024042004200420084420084520440000b42002000422088520440000b2000a721010b20010b2b01017f024042004200420084420084520440000b420042c000422088520440000b42c000a721000b20000b1e01027f024010052101200141c0006a210020002001490440000b0b20000b1f01017f024020004108744180fe0371200041087641ff01717221010b20010b1e01027f02402000100941107421022002200041107610097221010b20010b2401027e0240428001a7100aad42208621012001428001422088a7100aad8421000b20000b","wast":"(module +{"contracts":{"A":{"C":{"ewasm":{"wasm":"0061736d010000000125086000006000017e6000017f60017e017f60017f0060017f017f60027f7f0060037f7f7f0002510408657468657265756d08636f6465436f7079000708657468657265756d06726576657274000608657468657265756d0c67657443616c6c56616c7565000408657468657265756d0666696e6973680006030a090002030202020505010503010001060100071102066d656d6f72790200046d61696e000400b6030c435f335f6465706c6f7965640061736d010000000112046000006000017f60017f017f60027f7f0002130108657468657265756d06726576657274000303060500010102020503010001060100071102066d656d6f72790200046d61696e00010ad20205a10104027f017e017f047e024010022100200041c0006a210120012000490440000b420021022002a7210320031005ad42208621042002422088210520042005a71005ad84210620012006370000200141086a2006370000200141106a2006370000428001a71005ad4220862107200141186a2007428001422088a71005ad8437000020022002200284200284520440000b20022005520440000b1003200310000b0b2b01017f024042004200420084420084520440000b420042c000422088520440000b42c000a721000b20000b4203017f017e017f02404200210120012001200184200184520440000b20012001422088520440000b2001a72102200241c0006a210020002002490440000b0b20000b1f01017f024020004108744180fe0371200041087641ff01717221010b20010b1e01027f02402000100441107421022002200041107610047221010b20010b0ae303099a0102027f047e024010072100200041c0006a210120012000490440000b4200a7100bad422086210220024200422088a7100bad84210320012003370000200141086a2003370000200141106a2003370000200141186a100c37000041001002410829000021044200420084200441002900008484504504401008100510010b42a9032105100942b901100620051006100010092005100610030b0b2f02017f017e02404200210120012001200184200184520440000b20012001422088520440000b2001a721000b20000b2901017f024042004200420084420084520440000b42002000422088520440000b2000a721010b20010b2b01017f024042004200420084420084520440000b420042c000422088520440000b42c000a721000b20000b1e01027f024010052101200141c0006a210020002001490440000b0b20000b3c01027f024042004200420084420084520440000b4200428001422088520440000b428001a72101200141c0006a210020002001490440000b0b20000b1f01017f024020004108744180fe0371200041087641ff01717221010b20010b1e01027f02402000100a411074210220022000411076100a7221010b20010b2401027e0240428001a7100bad42208621012001428001422088a7100bad8421000b20000b","wast":"(module ;; custom section for sub-module ;; The Keccak-256 hash of the text representation of \"C_3_deployed\": d5523336521d49fa8bd64dba28ece7291aa7d45c646a23eabd038bbeecc2d803 ;; (@custom \"C_3_deployed\" \"0061736d010000000112046000006000017f60017f017f60027f7f0002130108657468657265756d06726576657274000303060500010102020503010001060100071102066d656d6f72790200046d61696e00010ad20205a10104027f017e017f047e024010022100200041c0006a210120012000490440000b420021022002a7210320031005ad42208621042002422088210520042005a71005ad84210620012006370000200141086a2006370000200141106a2006370000428001a71005ad4220862107200141186a2007428001422088a71005ad8437000020022002200284200284520440000b20022005520440000b1003200310000b0b2b01017f024042004200420084420084520440000b420042c000422088520440000b42c000a721000b20000b4203017f017e017f02404200210120012001200184200184520440000b20012001422088520440000b2001a72102200241c0006a210020002002490440000b0b20000b1f01017f024020004108744180fe0371200041087641ff01717221010b20010b1e01027f02402000100441107421022002200041107610047221010b20010b\") @@ -32,8 +32,8 @@ (if (i32.eqz (i64.eqz (i64.or (i64.or (i64.const 0) (i64.const 0)) (i64.or (local.get $z3) (i64.load (i32.const 0)))))) (then (call $eth.revert (call $to_internal_i32ptr) (call $u256_to_i32_344)))) (local.set $_1 (datasize \"C_3_deployed\")) - (call $eth.codeCopy (call $to_internal_i32ptr) (call $u256_to_i32 (dataoffset \"C_3_deployed\")) (call $u256_to_i32 (local.get $_1))) - (call $eth.finish (call $to_internal_i32ptr) (call $u256_to_i32 (local.get $_1))) + (call $eth.codeCopy (call $to_internal_i32ptr_348) (call $u256_to_i32 (dataoffset \"C_3_deployed\")) (call $u256_to_i32 (local.get $_1))) + (call $eth.finish (call $to_internal_i32ptr_348) (call $u256_to_i32 (local.get $_1))) ) ) @@ -96,11 +96,29 @@ (local.get $r) ) +(func $to_internal_i32ptr_348 + (result i32) + (local $r i32) + (local $v i32) + (block $label__5 + (if (i64.ne (i64.const 0) (i64.or (i64.or (i64.const 0) (i64.const 0)) (i64.const 0))) (then + (unreachable))) + (if (i64.ne (i64.const 0) (i64.shr_u (i64.const 128) (i64.const 32))) (then + (unreachable))) + (local.set $v (i32.wrap_i64 (i64.const 128))) + (local.set $r (i32.add (local.get $v) (i32.const 64))) + (if (i32.lt_u (local.get $r) (local.get $v)) (then + (unreachable))) + + ) + (local.get $r) +) + (func $bswap16 (param $x i32) (result i32) (local $y i32) - (block $label__5 + (block $label__6 (local.set $y (i32.or (i32.and (i32.shl (local.get $x) (i32.const 8)) (i32.const 65280)) (i32.and (i32.shr_u (local.get $x) (i32.const 8)) (i32.const 255)))) ) @@ -112,7 +130,7 @@ (result i32) (local $y i32) (local $hi i32) - (block $label__6 + (block $label__7 (local.set $hi (i32.shl (call $bswap16 (local.get $x)) (i32.const 16))) (local.set $y (i32.or (local.get $hi) (call $bswap16 (i32.shr_u (local.get $x) (i32.const 16))))) @@ -124,7 +142,7 @@ (result i64) (local $y i64) (local $hi i64) - (block $label__7 + (block $label__8 (local.set $hi (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.const 128)))) (i64.const 32))) (local.set $y (i64.or (local.get $hi) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (i64.const 128) (i64.const 32))))))) diff --git a/test/cmdlineTests/standard_irOptimized_requested/output.json b/test/cmdlineTests/standard_irOptimized_requested/output.json index 0612727b2..2aeac9773 100644 --- a/test/cmdlineTests/standard_irOptimized_requested/output.json +++ b/test/cmdlineTests/standard_irOptimized_requested/output.json @@ -10,8 +10,11 @@ object \"C_7\" { mstore(64, 128) if callvalue() { revert(0, 0) } constructor_C_7() - codecopy(0, dataoffset(\"C_7_deployed\"), datasize(\"C_7_deployed\")) - return(0, datasize(\"C_7_deployed\")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"C_7_deployed\"), datasize(\"C_7_deployed\")) + return(_1, datasize(\"C_7_deployed\")) + function allocate_unbounded() -> memPtr + { memPtr := mload(64) } function constructor_C_7() { } } diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 567aa7efc..5b91fb56f 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -13,9 +13,14 @@ object \"C_7\" { constructor_C_7() - codecopy(0, dataoffset(\"C_7_deployed\"), datasize(\"C_7_deployed\")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"C_7_deployed\"), datasize(\"C_7_deployed\")) - return(0, datasize(\"C_7_deployed\")) + return(_1, datasize(\"C_7_deployed\")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_C_7() { diff --git a/test/cmdlineTests/standard_viair_requested/output.json b/test/cmdlineTests/standard_viair_requested/output.json index 74c47d6a0..ca3054c46 100644 --- a/test/cmdlineTests/standard_viair_requested/output.json +++ b/test/cmdlineTests/standard_viair_requested/output.json @@ -13,9 +13,14 @@ object \"C_3\" { constructor_C_3() - codecopy(0, dataoffset(\"C_3_deployed\"), datasize(\"C_3_deployed\")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"C_3_deployed\"), datasize(\"C_3_deployed\")) - return(0, datasize(\"C_3_deployed\")) + return(_1, datasize(\"C_3_deployed\")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_C_3() { @@ -64,9 +69,14 @@ object \"D_16\" { constructor_D_16() - codecopy(0, dataoffset(\"D_16_deployed\"), datasize(\"D_16_deployed\")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"D_16_deployed\"), datasize(\"D_16_deployed\")) - return(0, datasize(\"D_16_deployed\")) + return(_1, datasize(\"D_16_deployed\")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_D_16() { @@ -163,9 +173,14 @@ object \"D_16\" { constructor_C_3() - codecopy(0, dataoffset(\"C_3_deployed\"), datasize(\"C_3_deployed\")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"C_3_deployed\"), datasize(\"C_3_deployed\")) - return(0, datasize(\"C_3_deployed\")) + return(_1, datasize(\"C_3_deployed\")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_C_3() { diff --git a/test/cmdlineTests/viair_abicoder_v1/output b/test/cmdlineTests/viair_abicoder_v1/output index af0c29b36..37e957259 100644 --- a/test/cmdlineTests/viair_abicoder_v1/output +++ b/test/cmdlineTests/viair_abicoder_v1/output @@ -14,9 +14,14 @@ object "test_11" { constructor_test_11() - codecopy(0, dataoffset("test_11_deployed"), datasize("test_11_deployed")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("test_11_deployed"), datasize("test_11_deployed")) - return(0, datasize("test_11_deployed")) + return(_1, datasize("test_11_deployed")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_test_11() { diff --git a/test/cmdlineTests/viair_subobjects/output b/test/cmdlineTests/viair_subobjects/output index e53814796..3847e7df6 100644 --- a/test/cmdlineTests/viair_subobjects/output +++ b/test/cmdlineTests/viair_subobjects/output @@ -1,7 +1,7 @@ ======= viair_subobjects/input.sol:C ======= Binary: -60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd +60806040523415600f5760006000fd5b600a80601e608039806080f350fe608060405260006000fd Binary of the runtime part: 608060405260006000fd Optimized IR: @@ -18,8 +18,8 @@ object "C_3" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") - codecopy(0, dataoffset("C_3_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("C_3_deployed"), _1) + return(128, _1) } } object "C_3_deployed" { @@ -35,9 +35,9 @@ object "C_3" { ======= viair_subobjects/input.sol:D ======= Binary: -608060405234156100105760006000fd5b60ba80610020600039806000f350fe6080604052600436101515608b576000803560e01c6326121ff0141560895734156027578081fd5b80600319360112156036578081fd5b6028806080016080811067ffffffffffffffff82111715606457634e487b7160e01b83526041600452602483fd5b508061009260803980608083f015156082576040513d83823e3d81fd505b5080604051f35b505b60006000fdfe60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd +608060405234156100105760006000fd5b60ba80610020608039806080f350fe6080604052600436101515608b576000803560e01c6326121ff0141560895734156027578081fd5b80600319360112156036578081fd5b6028806080016080811067ffffffffffffffff82111715606457634e487b7160e01b83526041600452602483fd5b508061009260803980608083f015156082576040513d83823e3d81fd505b5080604051f35b505b60006000fdfe60806040523415600f5760006000fd5b600a80601e608039806080f350fe608060405260006000fd Binary of the runtime part: -6080604052600436101515608b576000803560e01c6326121ff0141560895734156027578081fd5b80600319360112156036578081fd5b6028806080016080811067ffffffffffffffff82111715606457634e487b7160e01b83526041600452602483fd5b508061009260803980608083f015156082576040513d83823e3d81fd505b5080604051f35b505b60006000fdfe60806040523415600f5760006000fd5b600a80601e600039806000f350fe608060405260006000fd +6080604052600436101515608b576000803560e01c6326121ff0141560895734156027578081fd5b80600319360112156036578081fd5b6028806080016080811067ffffffffffffffff82111715606457634e487b7160e01b83526041600452602483fd5b508061009260803980608083f015156082576040513d83823e3d81fd505b5080604051f35b505b60006000fdfe60806040523415600f5760006000fd5b600a80601e608039806080f350fe608060405260006000fd Optimized IR: /******************************************************* * WARNING * @@ -52,8 +52,8 @@ object "D_16" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("D_16_deployed") - codecopy(0, dataoffset("D_16_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("D_16_deployed"), _1) + return(128, _1) } } object "D_16_deployed" { @@ -94,8 +94,8 @@ object "D_16" { mstore(64, 128) if callvalue() { revert(0, 0) } let _1 := datasize("C_3_deployed") - codecopy(0, dataoffset("C_3_deployed"), _1) - return(0, _1) + codecopy(128, dataoffset("C_3_deployed"), _1) + return(128, _1) } } object "C_3_deployed" { diff --git a/test/cmdlineTests/yul_optimizer_steps/output b/test/cmdlineTests/yul_optimizer_steps/output index aae106d4a..fe6a4be49 100644 --- a/test/cmdlineTests/yul_optimizer_steps/output +++ b/test/cmdlineTests/yul_optimizer_steps/output @@ -11,9 +11,12 @@ object "C_7" { { mstore(64, 128) if callvalue() { revert(0, 0) } - codecopy(0, dataoffset("C_7_deployed"), datasize("C_7_deployed")) - return(0, datasize("C_7_deployed")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("C_7_deployed"), datasize("C_7_deployed")) + return(_1, datasize("C_7_deployed")) } + function allocate_unbounded() -> memPtr + { memPtr := mload(64) } } object "C_7_deployed" { code { diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index 012993def..28a63d8c3 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -13,9 +13,14 @@ object \"C_11\" { constructor_C_11() - codecopy(0, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) - return(0, datasize(\"C_11_deployed\")) + return(_1, datasize(\"C_11_deployed\")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_C_11() { diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 5fb77fe75..eb6e8f48c 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -13,9 +13,14 @@ object \"C_11\" { constructor_C_11() - codecopy(0, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) - return(0, datasize(\"C_11_deployed\")) + return(_1, datasize(\"C_11_deployed\")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_C_11() { diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index a72e4db51..87d0f171c 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -13,9 +13,14 @@ object \"C_11\" { constructor_C_11() - codecopy(0, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) - return(0, datasize(\"C_11_deployed\")) + return(_1, datasize(\"C_11_deployed\")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_C_11() { diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 5b0cdc891..25bc6ac1d 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -13,9 +13,14 @@ object \"C_11\" { constructor_C_11() - codecopy(0, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) - return(0, datasize(\"C_11_deployed\")) + return(_1, datasize(\"C_11_deployed\")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_C_11() { diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index aa0d349ba..7bead0aec 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -13,9 +13,14 @@ object \"C_11\" { constructor_C_11() - codecopy(0, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) + let _1 := allocate_unbounded() + codecopy(_1, dataoffset(\"C_11_deployed\"), datasize(\"C_11_deployed\")) - return(0, datasize(\"C_11_deployed\")) + return(_1, datasize(\"C_11_deployed\")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } function constructor_C_11() { From 09283a6db45221c5bb187e346c0f2214e8612e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 29 Apr 2021 18:29:21 +0200 Subject: [PATCH 025/235] Switch from archlinux/base to archlinux:base in CI - archlinux/base seems to be gone - archlinux:base comes from a different repo but seems to also be an official Arch Linux image --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f102a5659..fa2b6da74 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -571,7 +571,7 @@ jobs: b_archlinux: docker: - - image: archlinux/base + - image: archlinux:base environment: TERM: xterm MAKEFLAGS: -j 3 @@ -710,7 +710,7 @@ jobs: t_archlinux_soltest: &t_archlinux_soltest docker: - - image: archlinux/base + - image: archlinux:base environment: EVM: constantinople OPTIMIZE: 0 From 563160e5b364d57e7a00a2da413929013c3e74b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 29 Apr 2021 18:52:47 +0200 Subject: [PATCH 026/235] Hard-code archlinux image version to base-20210131.0.14634 to work around runc/glibc bug --- .circleci/config.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fa2b6da74..ac23af569 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -571,7 +571,9 @@ jobs: b_archlinux: docker: - - image: archlinux:base + # FIXME: Newer releases won't work until CircleCI updates its host machines. + # See https://github.com/ethereum/solidity/pull/11332 + - image: archlinux:base-20210131.0.14634 environment: TERM: xterm MAKEFLAGS: -j 3 @@ -710,7 +712,9 @@ jobs: t_archlinux_soltest: &t_archlinux_soltest docker: - - image: archlinux:base + # FIXME: Newer releases won't work until CircleCI updates its host machines. + # See https://github.com/ethereum/solidity/pull/11332 + - image: archlinux:base-20210131.0.14634 environment: EVM: constantinople OPTIMIZE: 0 From 68f0f36a2d78e2c653fbfc500164e34a35f43adb Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Fri, 30 Apr 2021 22:13:08 -0500 Subject: [PATCH 027/235] [soltest] Improve Builtin Initialization. --- test/ExecutionFramework.cpp | 6 +-- test/ExecutionFramework.h | 6 +-- test/libsolidity/SemanticTest.cpp | 63 +++++++++++++++++-------------- test/libsolidity/SemanticTest.h | 5 +-- 4 files changed, 42 insertions(+), 38 deletions(-) diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index 1f5bece90..b2a549d88 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -233,7 +233,7 @@ h160 ExecutionFramework::account(size_t _idx) return h160(h256(u256{"0x1212121212121212121212121212120000000012"} + _idx * 0x1000), h160::AlignRight); } -bool ExecutionFramework::addressHasCode(h160 const& _addr) +bool ExecutionFramework::addressHasCode(h160 const& _addr) const { return m_evmcHost->get_code_size(EVMHost::convertToEVMC(_addr)) != 0; } @@ -266,12 +266,12 @@ bytes ExecutionFramework::logData(size_t _logIdx) const return {data.begin(), data.end()}; } -u256 ExecutionFramework::balanceAt(h160 const& _addr) +u256 ExecutionFramework::balanceAt(h160 const& _addr) const { return u256(EVMHost::convertFromEVMC(m_evmcHost->get_balance(EVMHost::convertToEVMC(_addr)))); } -bool ExecutionFramework::storageEmpty(h160 const& _addr) +bool ExecutionFramework::storageEmpty(h160 const& _addr) const { const auto it = m_evmcHost->accounts.find(EVMHost::convertToEVMC(_addr)); if (it != m_evmcHost->accounts.end()) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index c1d82856a..602ba2337 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -268,9 +268,9 @@ protected: /// @returns the (potentially newly created) _ith address. util::h160 account(size_t _i); - u256 balanceAt(util::h160 const& _addr); - bool storageEmpty(util::h160 const& _addr); - bool addressHasCode(util::h160 const& _addr); + u256 balanceAt(util::h160 const& _addr) const; + bool storageEmpty(util::h160 const& _addr) const; + bool addressHasCode(util::h160 const& _addr) const; size_t numLogs() const; size_t numLogTopics(size_t _logIdx) const; diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 004459277..b56e25703 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -40,12 +40,10 @@ using namespace solidity::langutil; using namespace solidity::util; using namespace solidity::util::formatting; using namespace solidity::frontend::test; -using namespace boost; using namespace boost::algorithm; using namespace boost::unit_test; namespace fs = boost::filesystem; - SemanticTest::SemanticTest( string const& _filename, langutil::EVMVersion _evmVersion, @@ -59,13 +57,12 @@ SemanticTest::SemanticTest( EVMVersionRestrictedTestCase(_filename), m_sources(m_reader.sources()), m_lineOffset(m_reader.lineNumber()), + m_builtins(makeBuiltins()), m_enforceViaYul(_enforceViaYul), m_enforceCompileToEwasm(_enforceCompileToEwasm), m_enforceGasCost(_enforceGasCost), - m_enforceGasCostMinValue(std::move(_enforceGasCostMinValue)) + m_enforceGasCostMinValue(move(_enforceGasCostMinValue)) { - initializeBuiltins(); - static set const compileViaYulAllowedValues{"also", "true", "false", "default"}; static set const yulRunTriggers{"also", "true"}; static set const legacyRunTriggers{"also", "false", "default"}; @@ -118,29 +115,37 @@ SemanticTest::SemanticTest( } } -void SemanticTest::initializeBuiltins() +map SemanticTest::makeBuiltins() const { - solAssert(m_builtins.count("smokeTest") == 0, ""); - m_builtins["smokeTest"] = [](FunctionCall const&) -> std::optional - { - return util::toBigEndian(u256(0x1234)); - }; - soltestAssert(m_builtins.count("balance") == 0, ""); - m_builtins["balance"] = [this](FunctionCall const& _call) -> std::optional - { - soltestAssert(_call.arguments.parameters.size() <= 1, "Account address expected."); - h160 address; - if (_call.arguments.parameters.size() == 1) - address = h160(_call.arguments.parameters.at(0).rawString); - else - address = m_contractAddress; - return util::toBigEndian(SolidityExecutionFramework::balanceAt(address)); - }; - soltestAssert(m_builtins.count("storageEmpty") == 0, ""); - m_builtins["storageEmpty"] = [this](FunctionCall const& _call) -> std::optional - { - soltestAssert(_call.arguments.parameters.empty(), "No arguments expected."); - return toBigEndian(u256(storageEmpty(m_contractAddress) ? 1 : 0)); + return { + { + "smokeTest", + [](FunctionCall const&) -> optional + { + return util::toBigEndian(u256(0x1234)); + } + }, + { + "balance", + [this](FunctionCall const& _call) -> optional + { + soltestAssert(_call.arguments.parameters.size() <= 1, "Account address expected."); + h160 address; + if (_call.arguments.parameters.size() == 1) + address = h160(_call.arguments.parameters.at(0).rawString); + else + address = m_contractAddress; + return util::toBigEndian(balanceAt(address)); + } + }, + { + "storageEmpty", + [this](FunctionCall const& _call) -> optional + { + soltestAssert(_call.arguments.parameters.empty(), "No arguments expected."); + return toBigEndian(u256(storageEmpty(m_contractAddress) ? 1 : 0)); + } + } }; } @@ -257,7 +262,7 @@ TestCase::TestResult SemanticTest::runTest( output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value); else if (test.call().kind == FunctionCall::Kind::Builtin) { - std::optional builtinOutput = m_builtins.at(test.call().signature)(test.call()); + optional builtinOutput = m_builtins.at(test.call().signature)(test.call()); if (builtinOutput.has_value()) { m_transactionSuccessful = true; @@ -296,7 +301,7 @@ TestCase::TestResult SemanticTest::runTest( success = false; test.setFailure(!m_transactionSuccessful); - test.setRawBytes(std::move(output)); + test.setRawBytes(move(output)); test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName(m_sources.mainSourceFile))); } } diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index 873357f38..97623cb1c 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -80,10 +80,11 @@ public: private: TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _isYulRun, bool _isEwasmRun); bool checkGasCostExpectation(TestFunctionCall& io_test, bool _compileViaYul) const; - void initializeBuiltins(); + std::map makeBuiltins() const; SourceMap m_sources; std::size_t m_lineOffset; std::vector m_tests; + std::map const m_builtins; bool m_testCaseWantsYulRun = false; bool m_testCaseWantsEwasmRun = false; bool m_testCaseWantsLegacyRun = true; @@ -93,8 +94,6 @@ private: bool m_allowNonExistingFunctions = false; bool m_canEnableYulRun = false; bool m_canEnableEwasmRun = false; - std::map m_builtins{}; - bool m_gasCostFailure = false; bool m_enforceGasCost = false; u256 m_enforceGasCostMinValue; From 1642c10f6e975322e7a1e69baad8064a8b259484 Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Thu, 29 Apr 2021 15:35:57 +0200 Subject: [PATCH 028/235] Fix ICE in free functions --- libsolidity/formal/SMTEncoder.cpp | 3 -- .../imports/import_free_functions.sol | 23 +++++++++++++++ .../imports/import_library_2.sol | 28 +++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/smtCheckerTests/imports/import_free_functions.sol create mode 100644 test/libsolidity/smtCheckerTests/imports/import_library_2.sol diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index a25a5d373..bc53c7126 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -2960,9 +2960,6 @@ vector SMTEncoder::symbolicArguments(FunctionCall const& _f void SMTEncoder::collectFreeFunctions(set const& _sources) { - if (!m_freeFunctions.empty()) - return; - for (auto source: _sources) for (auto node: source->nodes()) if (auto function = dynamic_cast(node.get())) diff --git a/test/libsolidity/smtCheckerTests/imports/import_free_functions.sol b/test/libsolidity/smtCheckerTests/imports/import_free_functions.sol new file mode 100644 index 000000000..4eec2c76a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/import_free_functions.sol @@ -0,0 +1,23 @@ +==== Source: Address.sol ==== +pragma solidity ^0.8.0; +function s() pure {} +==== Source: ERC20.sol ==== +pragma solidity ^0.8.0; + +import "./Address.sol"; + +function sub(uint256 a, uint256 b) pure returns (uint256) { + return a - b; +} + +contract ERC20 { + mapping (address => uint256) private _balances; + + function transferFrom(uint256 amount) public view { + sub(_balances[msg.sender], amount); + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 3944: (ERC20.sol:121-126): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\namount = 1\n\nTransaction trace:\nERC20.constructor()\nERC20.transferFrom(1)\n ERC20.sol:sub(0, 1) -- internal call diff --git a/test/libsolidity/smtCheckerTests/imports/import_library_2.sol b/test/libsolidity/smtCheckerTests/imports/import_library_2.sol new file mode 100644 index 000000000..d5c42dad6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/imports/import_library_2.sol @@ -0,0 +1,28 @@ +==== Source: Address.sol ==== +pragma solidity ^0.8.0; +library Address { function s() internal pure {} } +==== Source: ERC20.sol ==== +pragma solidity ^0.8.0; + +import "./Address.sol"; + +library SafeMath { + function sub(uint256 a, uint256 b) internal pure returns (uint256) { + return a - b; + } +} + +contract ERC20 { + using SafeMath for uint256; + using Address for address; + + mapping (address => uint256) private _balances; + + function transferFrom(uint256 amount) public view { + _balances[msg.sender].sub(amount); + } +} +// ==== +// SMTEngine: all +// ---- +// Warning 3944: (ERC20.sol:157-162): CHC: Underflow (resulting value less than 0) happens here.\nCounterexample:\n\namount = 1\n\nTransaction trace:\nERC20.constructor()\nERC20.transferFrom(1)\n SafeMath.sub(0, 1) -- internal call From d721bbe504a29c3e95d5d277758c161e2b7c8d9a Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 3 May 2021 12:19:46 +0200 Subject: [PATCH 029/235] Respect memory model in forwarding revert inside catch. --- libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 13 +------------ libsolidity/codegen/ir/IRGeneratorForStatements.h | 3 --- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index e59fde079..fc33065ef 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -3093,7 +3093,7 @@ void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement) if (_tryStatement.fallbackClause()) handleCatchFallback(*_tryStatement.fallbackClause()); else - rethrow(); + m_code << m_utils.forwardingRevertFunction() << "()\n"; m_code << "}\n"; } @@ -3115,17 +3115,6 @@ void IRGeneratorForStatements::handleCatchFallback(TryCatchClause const& _fallba _fallback.accept(*this); } -void IRGeneratorForStatements::rethrow() -{ - if (m_context.evmVersion().supportsReturndata()) - m_code << R"( - returndatacopy(0, 0, returndatasize()) - revert(0, returndatasize()) - )"s; - else - m_code << "revert(0, 0) // rethrow\n"s; -} - void IRGeneratorForStatements::revertWithError( string const& _signature, vector const& _parameterTypes, diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 90504aa7f..f8a581b5f 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -103,9 +103,6 @@ private: void handleCatch(TryStatement const& _tryStatement); void handleCatchFallback(TryCatchClause const& _fallback); - /// Generates code to rethrow an exception. - void rethrow(); - /// Generates code to revert with an error. The error arguments are assumed to /// be already evaluated and available in local IRVariables, but not yet /// converted. From af1e2bdad56746dc216b4d75670301dd1464b184 Mon Sep 17 00:00:00 2001 From: Franziska Heintel <41991517+franzihei@users.noreply.github.com> Date: Mon, 3 May 2021 14:18:26 +0200 Subject: [PATCH 030/235] updating post-release comms tasks --- ReleaseChecklist.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index fbc66279b..7fe7c361c 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -71,5 +71,7 @@ ### Post-release - [ ] Publish the blog post. - [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry. - - [ ] Announce on Twitter and Reddit. + - [ ] Announce on Twitter, including links to the release and the blog post. + - [ ] Share announcement on Reddit and Solidity forum. + - [ ] Update the release information section on [soliditylang.org](https://github.com/ethereum/solidity-portal). - [ ] Lean back, wait for bug reports and repeat from step 1 :) From 62355aead3faa625801943cbfbb4dedb2514efc2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 15 Mar 2021 16:45:00 +0100 Subject: [PATCH 031/235] Respect memory model for revert. --- libsolidity/codegen/ABIFunctions.cpp | 70 +++++++------- libsolidity/codegen/ABIFunctions.h | 4 +- libsolidity/codegen/CompilerContext.cpp | 6 +- libsolidity/codegen/CompilerContext.h | 2 +- libsolidity/codegen/YulUtilFunctions.cpp | 94 ++++++++++--------- libsolidity/codegen/YulUtilFunctions.h | 14 ++- .../codegen/ir/IRGenerationContext.cpp | 5 - libsolidity/codegen/ir/IRGenerationContext.h | 4 - libsolidity/codegen/ir/IRGenerator.cpp | 15 ++- .../codegen/ir/IRGeneratorForStatements.cpp | 4 +- 10 files changed, 114 insertions(+), 104 deletions(-) diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index ffced74c5..d964c1f14 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -205,12 +205,12 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) Whiskers templ(R"( function (headStart, dataEnd) { - if slt(sub(dataEnd, headStart), ) { } + if slt(sub(dataEnd, headStart), ) { () } } )"); templ("functionName", functionName); - templ("revertString", revertReasonIfDebug("ABI decoding: tuple data too short")); + templ("revertString", revertReasonIfDebugFunction("ABI decoding: tuple data too short")); templ("minimumSize", to_string(headSize(decodingTypes))); string decodeElements; @@ -235,7 +235,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) { let offset := (add(headStart, )) - if gt(offset, 0xffffffffffffffff) { } + if gt(offset, 0xffffffffffffffff) { () } let offset := @@ -244,7 +244,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) )"); elementTempl("dynamic", decodingTypes[i]->isDynamicallyEncoded()); // TODO add test - elementTempl("revertString", revertReasonIfDebug("ABI decoding: invalid tuple offset")); + elementTempl("revertString", revertReasonIfDebugFunction("ABI decoding: invalid tuple offset")); elementTempl("load", _fromMemory ? "mload" : "calldataload"); elementTempl("values", boost::algorithm::join(valueNamesLocal, ", ")); elementTempl("pos", to_string(headPos)); @@ -487,12 +487,12 @@ string ABIFunctions::abiEncodingFunctionCalldataArrayWithoutCleanup( else templ("scaleLengthByStride", Whiskers(R"( - if gt(length, ) { } + if gt(length, ) { () } length := mul(length, ) )") ("stride", toCompactHexWithPrefix(fromArrayType.calldataStride())) ("maxLength", toCompactHexWithPrefix(u256(-1) / fromArrayType.calldataStride())) - ("revertString", revertReasonIfDebug("ABI encoding: array data too long")) + ("revertString", revertReasonIfDebugFunction("ABI encoding: array data too long")) .render() // TODO add revert test ); @@ -1148,14 +1148,14 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from R"( // function (offset, end) -> array { - if iszero(slt(add(offset, 0x1f), end)) { } + if iszero(slt(add(offset, 0x1f), end)) { () } let length := array := (, length, end) } )" ); // TODO add test - templ("revertString", revertReasonIfDebug("ABI decoding: invalid calldata array offset")); + templ("revertString", revertReasonIfDebugFunction("ABI decoding: invalid calldata array offset")); templ("functionName", functionName); templ("readableTypeName", _type.toString(true)); templ("retrieveLength", _type.isDynamicallySized() ? (load + "(offset)") : toCompactHexWithPrefix(_type.length())); @@ -1188,13 +1188,13 @@ string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _t let src := offset if gt(add(src, mul(length, )), end) { - + () } for { let i := 0 } lt(i, length) { i := add(i, 1) } { let innerOffset := (src) - if gt(innerOffset, 0xffffffffffffffff) { } + if gt(innerOffset, 0xffffffffffffffff) { () } let elementPos := add(offset, innerOffset) let elementPos := src @@ -1215,9 +1215,9 @@ string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _t templ("dynamicBase", _type.baseType()->isDynamicallyEncoded()); templ( "revertInvalidStride", - revertReasonIfDebug("ABI decoding: invalid calldata array stride") + revertReasonIfDebugFunction("ABI decoding: invalid calldata array stride") ); - templ("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid calldata array offset")); + templ("revertStringOffset", revertReasonIfDebugFunction("ABI decoding: invalid calldata array offset")); templ("decodingFun", abiDecodingFunction(*_type.baseType(), _fromMemory, false)); return templ.render(); }); @@ -1241,15 +1241,15 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) w = Whiskers(R"( // function (offset, end) -> arrayPos, length { - if iszero(slt(add(offset, 0x1f), end)) { } + if iszero(slt(add(offset, 0x1f), end)) { () } length := calldataload(offset) - if gt(length, 0xffffffffffffffff) { } + if gt(length, 0xffffffffffffffff) { () } arrayPos := add(offset, 0x20) - if gt(add(arrayPos, mul(length, )), end) { } + if gt(add(arrayPos, mul(length, )), end) { () } } )"); - w("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid calldata array offset")); - w("revertStringLength", revertReasonIfDebug("ABI decoding: invalid calldata array length")); + w("revertStringOffset", revertReasonIfDebugFunction("ABI decoding: invalid calldata array offset")); + w("revertStringLength", revertReasonIfDebugFunction("ABI decoding: invalid calldata array length")); } else { @@ -1257,12 +1257,12 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) // function (offset, end) -> arrayPos { arrayPos := offset - if gt(add(arrayPos, mul(, )), end) { } + if gt(add(arrayPos, mul(, )), end) { () } } )"); w("length", toCompactHexWithPrefix(_type.length())); } - w("revertStringPos", revertReasonIfDebug("ABI decoding: invalid calldata array stride")); + w("revertStringPos", revertReasonIfDebugFunction("ABI decoding: invalid calldata array stride")); w("functionName", functionName); w("readableTypeName", _type.toString(true)); w("stride", toCompactHexWithPrefix(_type.calldataStride())); @@ -1288,11 +1288,11 @@ string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const array := ((length)) mstore(array, length) let dst := add(array, 0x20) - if gt(add(src, length), end) { } + if gt(add(src, length), end) { () } (src, dst, length) } )"); - templ("revertStringLength", revertReasonIfDebug("ABI decoding: invalid byte array length")); + templ("revertStringLength", revertReasonIfDebugFunction("ABI decoding: invalid byte array length")); templ("functionName", functionName); templ("allocate", m_utils.allocationFunction()); templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type)); @@ -1312,12 +1312,12 @@ string ABIFunctions::abiDecodingFunctionCalldataStruct(StructType const& _type) Whiskers w{R"( // function (offset, end) -> value { - if slt(sub(end, offset), ) { } + if slt(sub(end, offset), ) { () } value := offset } )"}; // TODO add test - w("revertString", revertReasonIfDebug("ABI decoding: struct calldata too short")); + w("revertString", revertReasonIfDebugFunction("ABI decoding: struct calldata too short")); w("functionName", functionName); w("readableTypeName", _type.toString(true)); w("minimumSize", to_string(_type.isDynamicallyEncoded() ? _type.calldataEncodedTailSize() : _type.calldataEncodedSize(true))); @@ -1337,7 +1337,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr Whiskers templ(R"( // function (headStart, end) -> value { - if slt(sub(end, headStart), ) { } + if slt(sub(end, headStart), ) { () } value := () <#members> { @@ -1348,7 +1348,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr } )"); // TODO add test - templ("revertString", revertReasonIfDebug("ABI decoding: struct data too short")); + templ("revertString", revertReasonIfDebugFunction("ABI decoding: struct data too short")); templ("functionName", functionName); templ("readableTypeName", _type.toString(true)); templ("allocate", m_utils.allocationFunction()); @@ -1365,7 +1365,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr Whiskers memberTempl(R"( let offset := (add(headStart, )) - if gt(offset, 0xffffffffffffffff) { } + if gt(offset, 0xffffffffffffffff) { () } let offset := @@ -1373,7 +1373,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr )"); memberTempl("dynamic", decodingType->isDynamicallyEncoded()); // TODO add test - memberTempl("revertString", revertReasonIfDebug("ABI decoding: invalid struct offset")); + memberTempl("revertString", revertReasonIfDebugFunction("ABI decoding: invalid struct offset")); memberTempl("load", _fromMemory ? "mload" : "calldataload"); memberTempl("pos", to_string(headPos)); memberTempl("memoryOffset", toCompactHexWithPrefix(_type.memoryOffsetOfMember(member.name))); @@ -1441,7 +1441,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type) Whiskers w(R"( function (base_ref, ptr) -> { let rel_offset_of_tail := calldataload(ptr) - if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { } + if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { () } value := add(rel_offset_of_tail, base_ref) } @@ -1453,14 +1453,14 @@ string ABIFunctions::calldataAccessFunction(Type const& _type) w("handleLength", Whiskers(R"( length := calldataload(value) value := add(value, 0x20) - if gt(length, 0xffffffffffffffff) { } - if sgt(base_ref, sub(calldatasize(), mul(length, ))) { } + if gt(length, 0xffffffffffffffff) { () } + if sgt(base_ref, sub(calldatasize(), mul(length, ))) { () } )") ("calldataStride", toCompactHexWithPrefix(arrayType->calldataStride())) // TODO add test - ("revertStringLength", revertReasonIfDebug("Invalid calldata access length")) + ("revertStringLength", revertReasonIfDebugFunction("Invalid calldata access length")) // TODO add test - ("revertStringStride", revertReasonIfDebug("Invalid calldata access stride")) + ("revertStringStride", revertReasonIfDebugFunction("Invalid calldata access stride")) .render()); w("return", "value, length"); } @@ -1471,7 +1471,7 @@ string ABIFunctions::calldataAccessFunction(Type const& _type) } w("neededLength", toCompactHexWithPrefix(tailSize)); w("functionName", functionName); - w("revertStringOffset", revertReasonIfDebug("Invalid calldata access offset")); + w("revertStringOffset", revertReasonIfDebugFunction("Invalid calldata access offset")); return w.render(); } else if (_type.isValueType()) @@ -1555,7 +1555,7 @@ size_t ABIFunctions::numVariablesForType(Type const& _type, EncodingOptions cons return _type.sizeOnStack(); } -std::string ABIFunctions::revertReasonIfDebug(std::string const& _message) +std::string ABIFunctions::revertReasonIfDebugFunction(std::string const& _message) { - return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message); + return m_utils.revertReasonIfDebugFunction(_message); } diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 8cd858ba0..07088aa3b 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -273,9 +273,9 @@ private: /// is true), for which it is two. static size_t numVariablesForType(Type const& _type, EncodingOptions const& _options); - /// @returns code that stores @param _message for revert reason + /// @returns the name of a function that uses @param _message for revert reason /// if m_revertStrings is debug. - std::string revertReasonIfDebug(std::string const& _message = ""); + std::string revertReasonIfDebugFunction(std::string const& _message = ""); langutil::EVMVersion m_evmVersion; RevertStrings const m_revertStrings; diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index cddeea472..434d9056e 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -560,7 +560,11 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _ string CompilerContext::revertReasonIfDebug(string const& _message) { - return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message); + return YulUtilFunctions::revertReasonIfDebugBody( + m_revertStrings, + "mload(" + to_string(CompilerUtils::freeMemoryPointer) + ")", + _message + ); } void CompilerContext::updateSourceLocation() diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 1ec620271..bdf9abc7d 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -268,7 +268,7 @@ public: ); /// If m_revertStrings is debug, @returns inline assembly code that - /// stores @param _message in memory position 0 and reverts. + /// stores @param _message at the free memory pointer and reverts. /// Otherwise returns "revert(0, 0)". std::string revertReasonIfDebug(std::string const& _message = ""); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 08dd6bc20..32848a091 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -2220,16 +2220,16 @@ string YulUtilFunctions::calldataArrayIndexRangeAccess(ArrayType const& _type) return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (offset, length, startIndex, endIndex) -> offsetOut, lengthOut { - if gt(startIndex, endIndex) { } - if gt(endIndex, length) { } + if gt(startIndex, endIndex) { () } + if gt(endIndex, length) { () } offsetOut := add(offset, mul(startIndex, )) lengthOut := sub(endIndex, startIndex) } )") ("functionName", functionName) ("stride", to_string(_type.calldataStride())) - ("revertSliceStartAfterEnd", revertReasonIfDebug("Slice starts after end")) - ("revertSliceGreaterThanLength", revertReasonIfDebug("Slice is greater than length")) + ("revertSliceStartAfterEnd", revertReasonIfDebugFunction("Slice starts after end")) + ("revertSliceGreaterThanLength", revertReasonIfDebugFunction("Slice is greater than length")) .render(); }); } @@ -2243,13 +2243,13 @@ string YulUtilFunctions::accessCalldataTailFunction(Type const& _type) return Whiskers(R"( function (base_ref, ptr_to_tail) -> addr, length { let rel_offset_of_tail := calldataload(ptr_to_tail) - if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { } + if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(, 1)))) { () } addr := add(base_ref, rel_offset_of_tail) length := calldataload(addr) - if gt(length, 0xffffffffffffffff) { } + if gt(length, 0xffffffffffffffff) { () } addr := add(addr, 32) - if sgt(addr, sub(calldatasize(), mul(length, ))) { } + if sgt(addr, sub(calldatasize(), mul(length, ))) { () } } )") @@ -2257,9 +2257,9 @@ string YulUtilFunctions::accessCalldataTailFunction(Type const& _type) ("dynamicallySized", _type.isDynamicallySized()) ("neededLength", toCompactHexWithPrefix(_type.calldataEncodedTailSize())) ("calldataStride", toCompactHexWithPrefix(_type.isDynamicallySized() ? dynamic_cast(_type).calldataStride() : 0)) - ("invalidCalldataTailOffset", revertReasonIfDebug("Invalid calldata tail offset")) - ("invalidCalldataTailLength", revertReasonIfDebug("Invalid calldata tail length")) - ("shortCalldataTail", revertReasonIfDebug("Calldata tail too short")) + ("invalidCalldataTailOffset", revertReasonIfDebugFunction("Invalid calldata tail offset")) + ("invalidCalldataTailLength", revertReasonIfDebugFunction("Invalid calldata tail length")) + ("shortCalldataTail", revertReasonIfDebugFunction("Calldata tail too short")) .render(); }); } @@ -4212,42 +4212,52 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC }); } -string YulUtilFunctions::revertReasonIfDebug(RevertStrings revertStrings, string const& _message) +string YulUtilFunctions::revertReasonIfDebugFunction(string const& _message) { - if (revertStrings >= RevertStrings::Debug && !_message.empty()) - { - Whiskers templ(R"({ - mstore(0, ) - mstore(4, 0x20) - mstore(add(4, 0x20), ) - let reasonPos := add(4, 0x40) - <#word> - mstore(add(reasonPos, ), ) - - revert(0, add(reasonPos, )) - })"); - templ("sig", util::selectorFromSignature("Error(string)").str()); - templ("length", to_string(_message.length())); - - size_t words = (_message.length() + 31) / 32; - vector> wordParams(words); - for (size_t i = 0; i < words; ++i) - { - wordParams[i]["offset"] = to_string(i * 32); - wordParams[i]["wordValue"] = formatAsStringOrNumber(_message.substr(32 * i, 32)); - } - templ("word", wordParams); - templ("end", to_string(words * 32)); - - return templ.render(); - } - else - return "revert(0, 0)"; + string functionName = "revert_error_" + util::toHex(util::keccak256(_message).asBytes()); + return m_functionCollector.createFunction(functionName, [&](auto&, auto&) -> string { + return revertReasonIfDebugBody(m_revertStrings, allocateUnboundedFunction() + "()", _message); + }); } -string YulUtilFunctions::revertReasonIfDebug(string const& _message) +string YulUtilFunctions::revertReasonIfDebugBody( + RevertStrings _revertStrings, + string const& _allocation, + string const& _message +) { - return revertReasonIfDebug(m_revertStrings, _message); + if (_revertStrings < RevertStrings::Debug || _message.empty()) + return "revert(0, 0)"; + + Whiskers templ(R"( + let start := + let pos := start + mstore(pos, ) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, ) + pos := add(pos, 0x20) + <#word> + mstore(add(pos, ), ) + + revert(start, ) + )"); + templ("allocate", _allocation); + templ("sig", util::selectorFromSignature("Error(string)").str()); + templ("length", to_string(_message.length())); + + size_t words = (_message.length() + 31) / 32; + vector> wordParams(words); + for (size_t i = 0; i < words; ++i) + { + wordParams[i]["offset"] = to_string(i * 32); + wordParams[i]["wordValue"] = formatAsStringOrNumber(_message.substr(32 * i, 32)); + } + templ("word", wordParams); + templ("overallLength", to_string(4 + 0x20 + 0x20 + words * 32)); + + return templ.render(); } string YulUtilFunctions::panicFunction(util::PanicCode _code) diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index f1eaed9fe..be1f25d24 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -453,12 +453,18 @@ public: /// signature: (slot, offset) -> std::string storageSetToZeroFunction(Type const& _type); - /// If revertStrings is debug, @returns inline assembly code that + /// If revertStrings is debug, @returns the name of a function that /// stores @param _message in memory position 0 and reverts. - /// Otherwise returns "revert(0, 0)". - static std::string revertReasonIfDebug(RevertStrings revertStrings, std::string const& _message = ""); + /// Otherwise returns the name of a function that uses "revert(0, 0)". + std::string revertReasonIfDebugFunction(std::string const& _message = ""); - std::string revertReasonIfDebug(std::string const& _message = ""); + /// @returns the function body of ``revertReasonIfDebug``. + /// Should only be used internally and by the old code generator. + static std::string revertReasonIfDebugBody( + RevertStrings _revertStrings, + std::string const& _allocation, + std::string const& _message + ); /// Reverts with ``Panic(uint256)`` and the given code. std::string panicFunction(util::PanicCode _code); diff --git a/libsolidity/codegen/ir/IRGenerationContext.cpp b/libsolidity/codegen/ir/IRGenerationContext.cpp index 228bef068..8ab6b1b17 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.cpp +++ b/libsolidity/codegen/ir/IRGenerationContext.cpp @@ -177,8 +177,3 @@ ABIFunctions IRGenerationContext::abiFunctions() { return ABIFunctions(m_evmVersion, m_revertStrings, m_functions); } - -std::string IRGenerationContext::revertReasonIfDebug(std::string const& _message) -{ - return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message); -} diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index 9976367d9..0f1e9ae1c 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -143,10 +143,6 @@ public: ABIFunctions abiFunctions(); - /// @returns code that stores @param _message for revert reason - /// if m_revertStrings is debug. - std::string revertReasonIfDebug(std::string const& _message = ""); - RevertStrings revertStrings() const { return m_revertStrings; } std::set& subObjectsCreated() { return m_subObjects; } diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index a1d1522a6..20a434e13 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -839,7 +839,7 @@ string IRGenerator::deployCode(ContractDefinition const& _contract) string IRGenerator::callValueCheck() { - return "if callvalue() { " + m_context.revertReasonIfDebug("Ether sent to non-payable function") + " }"; + return "if callvalue() { " + m_utils.revertReasonIfDebugFunction("Ether sent to non-payable function") + "() }"; } string IRGenerator::dispatchRoutine(ContractDefinition const& _contract) @@ -885,8 +885,8 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract) // we revert. delegatecallCheck = "if iszero(called_via_delegatecall) { " + - m_context.revertReasonIfDebug("Non-view function of library called without DELEGATECALL") + - " }"; + m_utils.revertReasonIfDebugFunction("Non-view function of library called without DELEGATECALL") + + "() }"; } templ["delegatecallCheck"] = delegatecallCheck; templ["callValueCheck"] = (type->isPayable() || _contract.isLibrary()) ? "" : callValueCheck(); @@ -937,12 +937,11 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract) t("fallback", fallbackCode); } else - t( - "fallback", + t("fallback", ( etherReceiver ? - m_context.revertReasonIfDebug("Unknown signature and no fallback defined") : - m_context.revertReasonIfDebug("Contract does not have fallback nor receive functions") - ); + m_utils.revertReasonIfDebugFunction("Unknown signature and no fallback defined") : + m_utils.revertReasonIfDebugFunction("Contract does not have fallback nor receive functions") + ) + "()"); return t.render(); } diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index fc33065ef..1eed9f04d 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -2433,7 +2433,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( } Whiskers templ(R"( - if iszero(extcodesize(
)) { } + if iszero(extcodesize(
)) { () } // storage for arguments and returned data let := () @@ -2458,7 +2458,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( := (, add(, )) } )"); - templ("revertNoCode", m_context.revertReasonIfDebug("Target contract does not contain code")); + templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code")); templ("pos", m_context.newYulVariable()); templ("end", m_context.newYulVariable()); if (_functionCall.annotation().tryCall) From 2e274a0b264f6ab99dbb9a42a6f8fcb4c5901d60 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 3 May 2021 11:26:49 +0200 Subject: [PATCH 032/235] Add commandline test to show debug strings. --- test/cmdlineTests/revert_strings/args | 1 + test/cmdlineTests/revert_strings/input.sol | 7 + test/cmdlineTests/revert_strings/output | 362 +++++++++++++++++++++ 3 files changed, 370 insertions(+) create mode 100644 test/cmdlineTests/revert_strings/args create mode 100644 test/cmdlineTests/revert_strings/input.sol create mode 100644 test/cmdlineTests/revert_strings/output diff --git a/test/cmdlineTests/revert_strings/args b/test/cmdlineTests/revert_strings/args new file mode 100644 index 000000000..be92d13e1 --- /dev/null +++ b/test/cmdlineTests/revert_strings/args @@ -0,0 +1 @@ +--revert-strings debug --ir \ No newline at end of file diff --git a/test/cmdlineTests/revert_strings/input.sol b/test/cmdlineTests/revert_strings/input.sol new file mode 100644 index 000000000..3d4782946 --- /dev/null +++ b/test/cmdlineTests/revert_strings/input.sol @@ -0,0 +1,7 @@ +pragma solidity >=0.0; +// SPDX-License-Identifier: GPL-3.0 +contract C { + enum E { X } + function f(uint[][] memory, E e) public pure { + } +} diff --git a/test/cmdlineTests/revert_strings/output b/test/cmdlineTests/revert_strings/output new file mode 100644 index 000000000..8fd41fba4 --- /dev/null +++ b/test/cmdlineTests/revert_strings/output @@ -0,0 +1,362 @@ +IR: +/******************************************************* + * WARNING * + * Solidity to Yul compilation is still EXPERIMENTAL * + * It can result in LOSS OF FUNDS or worse * + * !USE AT YOUR OWN RISK! * + *******************************************************/ + + +object "C_15" { + code { + mstore(64, 128) + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + + constructor_C_15() + + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("C_15_deployed"), datasize("C_15_deployed")) + + return(_1, datasize("C_15_deployed")) + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function constructor_C_15() { + + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 34) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "Ether sent to non-payable functi") + + mstore(add(pos, 32), "on") + + revert(start, 132) + + } + + } + object "C_15_deployed" { + code { + mstore(64, 128) + + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_224_unsigned(calldataload(0)) + switch selector + + case 0x02e8cd18 + { + // f(uint256[][],uint8) + + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } + let param_0, param_1 := abi_decode_tuple_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptrt_enum$_E_$3(4, calldatasize()) + fun_f_14(param_0, param_1) + let memPos := allocate_unbounded() + let memEnd := abi_encode_tuple__to__fromStack(memPos ) + return(memPos, sub(memEnd, memPos)) + } + + default {} + } + if iszero(calldatasize()) { } + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + + // uint256[][] + function abi_decode_available_length_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(offset, length, end) -> array { + array := allocate_memory(array_allocation_size_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(length)) + let dst := array + + mstore(array, length) + dst := add(array, 0x20) + + let src := offset + if gt(add(src, mul(length, 0x20)), end) { + revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() + } + for { let i := 0 } lt(i, length) { i := add(i, 1) } + { + + let innerOffset := calldataload(src) + if gt(innerOffset, 0xffffffffffffffff) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } + let elementPos := add(offset, innerOffset) + + mstore(dst, abi_decode_t_array$_t_uint256_$dyn_memory_ptr(elementPos, end)) + dst := add(dst, 0x20) + src := add(src, 0x20) + } + } + + // uint256[] + function abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(offset, length, end) -> array { + array := allocate_memory(array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length)) + let dst := array + + mstore(array, length) + dst := add(array, 0x20) + + let src := offset + if gt(add(src, mul(length, 0x20)), end) { + revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() + } + for { let i := 0 } lt(i, length) { i := add(i, 1) } + { + + let elementPos := src + + mstore(dst, abi_decode_t_uint256(elementPos, end)) + dst := add(dst, 0x20) + src := add(src, 0x20) + } + } + + // uint256[][] + function abi_decode_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(offset, end) -> array { + if iszero(slt(add(offset, 0x1f), end)) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } + let length := calldataload(offset) + array := abi_decode_available_length_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(add(offset, 0x20), length, end) + } + + // uint256[] + function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset, end) -> array { + if iszero(slt(add(offset, 0x1f), end)) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } + let length := calldataload(offset) + array := abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(add(offset, 0x20), length, end) + } + + function abi_decode_t_enum$_E_$3(offset, end) -> value { + value := calldataload(offset) + validator_revert_t_enum$_E_$3(value) + } + + function abi_decode_t_uint256(offset, end) -> value { + value := calldataload(offset) + validator_revert_t_uint256(value) + } + + function abi_decode_tuple_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptrt_enum$_E_$3(headStart, dataEnd) -> value0, value1 { + if slt(sub(dataEnd, headStart), 64) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } + + { + + let offset := calldataload(add(headStart, 0)) + if gt(offset, 0xffffffffffffffff) { revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() } + + value0 := abi_decode_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(add(headStart, offset), dataEnd) + } + + { + + let offset := 32 + + value1 := abi_decode_t_enum$_E_$3(add(headStart, offset), dataEnd) + } + + } + + function abi_encode_tuple__to__fromStack(headStart ) -> tail { + tail := add(headStart, 0) + + } + + function allocate_memory(size) -> memPtr { + memPtr := allocate_unbounded() + finalize_allocation(memPtr, size) + } + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function array_allocation_size_t_array$_t_array$_t_uint256_$dyn_memory_ptr_$dyn_memory_ptr(length) -> size { + // Make sure we can allocate memory without overflow + if gt(length, 0xffffffffffffffff) { panic_error_0x41() } + + size := mul(length, 0x20) + + // add length slot + size := add(size, 0x20) + + } + + function array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length) -> size { + // Make sure we can allocate memory without overflow + if gt(length, 0xffffffffffffffff) { panic_error_0x41() } + + size := mul(length, 0x20) + + // add length slot + size := add(size, 0x20) + + } + + function cleanup_t_uint256(value) -> cleaned { + cleaned := value + } + + function finalize_allocation(memPtr, size) { + let newFreePtr := add(memPtr, round_up_to_mul_of_32(size)) + // protect against overflow + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { panic_error_0x41() } + mstore(64, newFreePtr) + } + + function fun_f_14(var__7_mpos, var_e_10) { + + } + + function panic_error_0x41() { + mstore(0, 35408467139433450592217433187231851964531694900788300625387963629091585785856) + mstore(4, 0x41) + revert(0, 0x24) + } + + function revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 43) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "ABI decoding: invalid calldata a") + + mstore(add(pos, 32), "rray offset") + + revert(start, 132) + + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 53) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "Contract does not have fallback ") + + mstore(add(pos, 32), "nor receive functions") + + revert(start, 132) + + } + + function revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 43) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "ABI decoding: invalid calldata a") + + mstore(add(pos, 32), "rray stride") + + revert(start, 132) + + } + + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 34) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "ABI decoding: invalid tuple offs") + + mstore(add(pos, 32), "et") + + revert(start, 132) + + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 34) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "Ether sent to non-payable functi") + + mstore(add(pos, 32), "on") + + revert(start, 132) + + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + + let start := allocate_unbounded() + let pos := start + mstore(pos, 3963877391197344453575983046348115674221700746820753546331534351508065746944) + pos := add(pos, 4) + mstore(pos, 0x20) + pos := add(pos, 0x20) + mstore(pos, 34) + pos := add(pos, 0x20) + + mstore(add(pos, 0), "ABI decoding: tuple data too sho") + + mstore(add(pos, 32), "rt") + + revert(start, 132) + + } + + function round_up_to_mul_of_32(value) -> result { + result := and(add(value, 31), not(31)) + } + + function shift_right_224_unsigned(value) -> newValue { + newValue := + + shr(224, value) + + } + + function validator_revert_t_enum$_E_$3(value) { + if iszero(lt(value, 1)) { revert(0, 0) } + } + + function validator_revert_t_uint256(value) { + if iszero(eq(value, cleanup_t_uint256(value))) { revert(0, 0) } + } + + } + + } + +} From aded56b5b981a5c38eb9195d55b217ccf6586784 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 29 Apr 2021 16:26:21 +0200 Subject: [PATCH 033/235] Update tests --- .../combined_json_generated_sources/output | 558 ++++++++++++------ test/cmdlineTests/exp_base_literal/output | 28 +- test/cmdlineTests/name_simplifier/output | 4 +- .../standard_generatedSources/output.json | 26 +- .../output.json | 25 +- .../standard_ir_requested/output.json | 24 +- .../standard_viair_requested/output.json | 56 +- test/cmdlineTests/viair_abicoder_v1/output | 24 +- test/cmdlineTests/yul_optimizer_steps/output | 11 +- .../yul_string_format_ascii/output.json | 24 +- .../output.json | 24 +- .../output.json | 24 +- .../yul_string_format_ascii_long/output.json | 24 +- .../yul_string_format_hex/output.json | 24 +- test/libsolidity/gasTests/abiv2.sol | 6 +- test/libsolidity/gasTests/abiv2_optimised.sol | 4 +- test/libsolidity/gasTests/dispatch_large.sol | 4 +- test/libsolidity/gasTests/dispatch_medium.sol | 4 +- test/libsolidity/gasTests/dispatch_small.sol | 4 +- test/libsolidity/gasTests/exp.sol | 4 +- .../copying/array_copy_including_array.sol | 2 +- .../array/fixed_arrays_as_return_type.sol | 2 +- .../array/function_array_cross_calls.sol | 2 +- .../constructor/arrays_in_constructors.sol | 2 +- .../bytes_in_constructors_packer.sol | 2 +- .../functionTypes/store_function.sol | 2 +- .../address_overload_resolution.sol | 4 +- ...d_function_calldata_calldata_interface.sol | 2 +- ...ted_function_calldata_memory_interface.sol | 2 +- .../structs/memory_structs_nested_load.sol | 4 +- 30 files changed, 669 insertions(+), 257 deletions(-) diff --git a/test/cmdlineTests/combined_json_generated_sources/output b/test/cmdlineTests/combined_json_generated_sources/output index cd8272f8e..9165832ca 100644 --- a/test/cmdlineTests/combined_json_generated_sources/output +++ b/test/cmdlineTests/combined_json_generated_sources/output @@ -10,54 +10,38 @@ "ast": { "nodeType": "YulBlock", - "src": "0:825:1", + "src": "0:1856:1", "statements": [ { "body": { "nodeType": "YulBlock", - "src": "114:277:1", + "src": "114:478:1", "statements": [ { "body": { "nodeType": "YulBlock", - "src": "163:16:1", + "src": "163:83:1", "statements": [ { "expression": { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "172:1:1", - "type": "", - "value": "0" - }, - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "175:1:1", - "type": "", - "value": "0" - } - ], + "arguments": [], "functionName": { - "name": "revert", + "name": "revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d", "nodeType": "YulIdentifier", - "src": "165:6:1" + "src": "165:77:1" }, "nodeType": "YulFunctionCall", - "src": "165:12:1" + "src": "165:79:1" }, "nodeType": "YulExpressionStatement", - "src": "165:12:1" + "src": "165:79:1" } ] }, @@ -123,7 +107,7 @@ }, { "nodeType": "YulAssignment", - "src": "188:30:1", + "src": "255:30:1", "value": { "arguments": @@ -131,24 +115,24 @@ { "name": "offset", "nodeType": "YulIdentifier", - "src": "211:6:1" + "src": "278:6:1" } ], "functionName": { "name": "calldataload", "nodeType": "YulIdentifier", - "src": "198:12:1" + "src": "265:12:1" }, "nodeType": "YulFunctionCall", - "src": "198:20:1" + "src": "265:20:1" }, "variableNames": [ { "name": "length", "nodeType": "YulIdentifier", - "src": "188:6:1" + "src": "255:6:1" } ] }, @@ -156,40 +140,24 @@ "body": { "nodeType": "YulBlock", - "src": "261:16:1", + "src": "328:83:1", "statements": [ { "expression": { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "270:1:1", - "type": "", - "value": "0" - }, - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "273:1:1", - "type": "", - "value": "0" - } - ], + "arguments": [], "functionName": { - "name": "revert", + "name": "revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490", "nodeType": "YulIdentifier", - "src": "263:6:1" + "src": "330:77:1" }, "nodeType": "YulFunctionCall", - "src": "263:12:1" + "src": "330:79:1" }, "nodeType": "YulExpressionStatement", - "src": "263:12:1" + "src": "330:79:1" } ] }, @@ -200,12 +168,12 @@ { "name": "length", "nodeType": "YulIdentifier", - "src": "233:6:1" + "src": "300:6:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "241:18:1", + "src": "308:18:1", "type": "", "value": "0xffffffffffffffff" } @@ -214,17 +182,17 @@ { "name": "gt", "nodeType": "YulIdentifier", - "src": "230:2:1" + "src": "297:2:1" }, "nodeType": "YulFunctionCall", - "src": "230:30:1" + "src": "297:30:1" }, "nodeType": "YulIf", - "src": "227:2:1" + "src": "294:2:1" }, { "nodeType": "YulAssignment", - "src": "286:29:1", + "src": "420:29:1", "value": { "arguments": @@ -232,12 +200,12 @@ { "name": "offset", "nodeType": "YulIdentifier", - "src": "302:6:1" + "src": "436:6:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "310:4:1", + "src": "444:4:1", "type": "", "value": "0x20" } @@ -246,17 +214,17 @@ { "name": "add", "nodeType": "YulIdentifier", - "src": "298:3:1" + "src": "432:3:1" }, "nodeType": "YulFunctionCall", - "src": "298:17:1" + "src": "432:17:1" }, "variableNames": [ { "name": "arrayPos", "nodeType": "YulIdentifier", - "src": "286:8:1" + "src": "420:8:1" } ] }, @@ -264,40 +232,24 @@ "body": { "nodeType": "YulBlock", - "src": "369:16:1", + "src": "503:83:1", "statements": [ { "expression": { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "378:1:1", - "type": "", - "value": "0" - }, - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "381:1:1", - "type": "", - "value": "0" - } - ], + "arguments": [], "functionName": { - "name": "revert", + "name": "revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef", "nodeType": "YulIdentifier", - "src": "371:6:1" + "src": "505:77:1" }, "nodeType": "YulFunctionCall", - "src": "371:12:1" + "src": "505:79:1" }, "nodeType": "YulExpressionStatement", - "src": "371:12:1" + "src": "505:79:1" } ] }, @@ -311,7 +263,7 @@ { "name": "arrayPos", "nodeType": "YulIdentifier", - "src": "334:8:1" + "src": "468:8:1" }, { "arguments": @@ -319,12 +271,12 @@ { "name": "length", "nodeType": "YulIdentifier", - "src": "348:6:1" + "src": "482:6:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "356:4:1", + "src": "490:4:1", "type": "", "value": "0x20" } @@ -333,38 +285,38 @@ { "name": "mul", "nodeType": "YulIdentifier", - "src": "344:3:1" + "src": "478:3:1" }, "nodeType": "YulFunctionCall", - "src": "344:17:1" + "src": "478:17:1" } ], "functionName": { "name": "add", "nodeType": "YulIdentifier", - "src": "330:3:1" + "src": "464:3:1" }, "nodeType": "YulFunctionCall", - "src": "330:32:1" + "src": "464:32:1" }, { "name": "end", "nodeType": "YulIdentifier", - "src": "364:3:1" + "src": "498:3:1" } ], "functionName": { "name": "gt", "nodeType": "YulIdentifier", - "src": "327:2:1" + "src": "461:2:1" }, "nodeType": "YulFunctionCall", - "src": "327:41:1" + "src": "461:41:1" }, "nodeType": "YulIf", - "src": "324:2:1" + "src": "458:2:1" } ] }, @@ -400,53 +352,37 @@ "type": "" } ], - "src": "24:367:1" + "src": "24:568:1" }, { "body": { "nodeType": "YulBlock", - "src": "498:324:1", + "src": "699:458:1", "statements": [ { "body": { "nodeType": "YulBlock", - "src": "544:16:1", + "src": "745:83:1", "statements": [ { "expression": { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "553:1:1", - "type": "", - "value": "0" - }, - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "556:1:1", - "type": "", - "value": "0" - } - ], + "arguments": [], "functionName": { - "name": "revert", + "name": "revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b", "nodeType": "YulIdentifier", - "src": "546:6:1" + "src": "747:77:1" }, "nodeType": "YulFunctionCall", - "src": "546:12:1" + "src": "747:79:1" }, "nodeType": "YulExpressionStatement", - "src": "546:12:1" + "src": "747:79:1" } ] }, @@ -460,27 +396,27 @@ { "name": "dataEnd", "nodeType": "YulIdentifier", - "src": "519:7:1" + "src": "720:7:1" }, { "name": "headStart", "nodeType": "YulIdentifier", - "src": "528:9:1" + "src": "729:9:1" } ], "functionName": { "name": "sub", "nodeType": "YulIdentifier", - "src": "515:3:1" + "src": "716:3:1" }, "nodeType": "YulFunctionCall", - "src": "515:23:1" + "src": "716:23:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "540:2:1", + "src": "741:2:1", "type": "", "value": "32" } @@ -489,22 +425,22 @@ { "name": "slt", "nodeType": "YulIdentifier", - "src": "511:3:1" + "src": "712:3:1" }, "nodeType": "YulFunctionCall", - "src": "511:32:1" + "src": "712:32:1" }, "nodeType": "YulIf", - "src": "508:2:1" + "src": "709:2:1" }, { "nodeType": "YulBlock", - "src": "570:245:1", + "src": "838:312:1", "statements": [ { "nodeType": "YulVariableDeclaration", - "src": "585:45:1", + "src": "853:45:1", "value": { "arguments": @@ -515,12 +451,12 @@ { "name": "headStart", "nodeType": "YulIdentifier", - "src": "616:9:1" + "src": "884:9:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "627:1:1", + "src": "895:1:1", "type": "", "value": "0" } @@ -529,27 +465,27 @@ { "name": "add", "nodeType": "YulIdentifier", - "src": "612:3:1" + "src": "880:3:1" }, "nodeType": "YulFunctionCall", - "src": "612:17:1" + "src": "880:17:1" } ], "functionName": { "name": "calldataload", "nodeType": "YulIdentifier", - "src": "599:12:1" + "src": "867:12:1" }, "nodeType": "YulFunctionCall", - "src": "599:31:1" + "src": "867:31:1" }, "variables": [ { "name": "offset", "nodeType": "YulTypedName", - "src": "589:6:1", + "src": "857:6:1", "type": "" } ] @@ -558,40 +494,24 @@ "body": { "nodeType": "YulBlock", - "src": "677:16:1", + "src": "945:83:1", "statements": [ { "expression": { - "arguments": - [ - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "686:1:1", - "type": "", - "value": "0" - }, - { - "kind": "number", - "nodeType": "YulLiteral", - "src": "689:1:1", - "type": "", - "value": "0" - } - ], + "arguments": [], "functionName": { - "name": "revert", + "name": "revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db", "nodeType": "YulIdentifier", - "src": "679:6:1" + "src": "947:77:1" }, "nodeType": "YulFunctionCall", - "src": "679:12:1" + "src": "947:79:1" }, "nodeType": "YulExpressionStatement", - "src": "679:12:1" + "src": "947:79:1" } ] }, @@ -602,12 +522,12 @@ { "name": "offset", "nodeType": "YulIdentifier", - "src": "649:6:1" + "src": "917:6:1" }, { "kind": "number", "nodeType": "YulLiteral", - "src": "657:18:1", + "src": "925:18:1", "type": "", "value": "0xffffffffffffffff" } @@ -616,17 +536,17 @@ { "name": "gt", "nodeType": "YulIdentifier", - "src": "646:2:1" + "src": "914:2:1" }, "nodeType": "YulFunctionCall", - "src": "646:30:1" + "src": "914:30:1" }, "nodeType": "YulIf", - "src": "643:2:1" + "src": "911:2:1" }, { "nodeType": "YulAssignment", - "src": "707:98:1", + "src": "1042:98:1", "value": { "arguments": @@ -637,49 +557,49 @@ { "name": "headStart", "nodeType": "YulIdentifier", - "src": "777:9:1" + "src": "1112:9:1" }, { "name": "offset", "nodeType": "YulIdentifier", - "src": "788:6:1" + "src": "1123:6:1" } ], "functionName": { "name": "add", "nodeType": "YulIdentifier", - "src": "773:3:1" + "src": "1108:3:1" }, "nodeType": "YulFunctionCall", - "src": "773:22:1" + "src": "1108:22:1" }, { "name": "dataEnd", "nodeType": "YulIdentifier", - "src": "797:7:1" + "src": "1132:7:1" } ], "functionName": { "name": "abi_decode_t_array$_t_uint256_$dyn_calldata_ptr", "nodeType": "YulIdentifier", - "src": "725:47:1" + "src": "1060:47:1" }, "nodeType": "YulFunctionCall", - "src": "725:80:1" + "src": "1060:80:1" }, "variableNames": [ { "name": "value0", "nodeType": "YulIdentifier", - "src": "707:6:1" + "src": "1042:6:1" }, { "name": "value1", "nodeType": "YulIdentifier", - "src": "715:6:1" + "src": "1050:6:1" } ] } @@ -694,13 +614,13 @@ { "name": "headStart", "nodeType": "YulTypedName", - "src": "460:9:1", + "src": "661:9:1", "type": "" }, { "name": "dataEnd", "nodeType": "YulTypedName", - "src": "471:7:1", + "src": "672:7:1", "type": "" } ], @@ -709,21 +629,301 @@ { "name": "value0", "nodeType": "YulTypedName", - "src": "483:6:1", + "src": "684:6:1", "type": "" }, { "name": "value1", "nodeType": "YulTypedName", - "src": "491:6:1", + "src": "692:6:1", "type": "" } ], - "src": "397:425:1" + "src": "598:559:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "1203:35:1", + "statements": + [ + { + "nodeType": "YulAssignment", + "src": "1213:19:1", + "value": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1229:2:1", + "type": "", + "value": "64" + } + ], + "functionName": + { + "name": "mload", + "nodeType": "YulIdentifier", + "src": "1223:5:1" + }, + "nodeType": "YulFunctionCall", + "src": "1223:9:1" + }, + "variableNames": + [ + { + "name": "memPtr", + "nodeType": "YulIdentifier", + "src": "1213:6:1" + } + ] + } + ] + }, + "name": "allocate_unbounded", + "nodeType": "YulFunctionDefinition", + "returnVariables": + [ + { + "name": "memPtr", + "nodeType": "YulTypedName", + "src": "1196:6:1", + "type": "" + } + ], + "src": "1163:75:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "1333:28:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1350:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1353:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "1343:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "1343:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "1343:12:1" + } + ] + }, + "name": "revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490", + "nodeType": "YulFunctionDefinition", + "src": "1244:117:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "1456:28:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1473:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1476:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "1466:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "1466:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "1466:12:1" + } + ] + }, + "name": "revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d", + "nodeType": "YulFunctionDefinition", + "src": "1367:117:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "1579:28:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1596:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1599:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "1589:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "1589:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "1589:12:1" + } + ] + }, + "name": "revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef", + "nodeType": "YulFunctionDefinition", + "src": "1490:117:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "1702:28:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1719:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1722:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "1712:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "1712:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "1712:12:1" + } + ] + }, + "name": "revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db", + "nodeType": "YulFunctionDefinition", + "src": "1613:117:1" + }, + { + "body": + { + "nodeType": "YulBlock", + "src": "1825:28:1", + "statements": + [ + { + "expression": + { + "arguments": + [ + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1842:1:1", + "type": "", + "value": "0" + }, + { + "kind": "number", + "nodeType": "YulLiteral", + "src": "1845:1:1", + "type": "", + "value": "0" + } + ], + "functionName": + { + "name": "revert", + "nodeType": "YulIdentifier", + "src": "1835:6:1" + }, + "nodeType": "YulFunctionCall", + "src": "1835:12:1" + }, + "nodeType": "YulExpressionStatement", + "src": "1835:12:1" + } + ] + }, + "name": "revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b", + "nodeType": "YulFunctionDefinition", + "src": "1736:117:1" } ] }, - "contents": "{\n\n // uint256[]\n function abi_decode_t_array$_t_uint256_$dyn_calldata_ptr(offset, end) -> arrayPos, length {\n if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) }\n length := calldataload(offset)\n if gt(length, 0xffffffffffffffff) { revert(0, 0) }\n arrayPos := add(offset, 0x20)\n if gt(add(arrayPos, mul(length, 0x20)), end) { revert(0, 0) }\n }\n\n function abi_decode_tuple_t_array$_t_uint256_$dyn_calldata_ptr(headStart, dataEnd) -> value0, value1 {\n if slt(sub(dataEnd, headStart), 32) { revert(0, 0) }\n\n {\n\n let offset := calldataload(add(headStart, 0))\n if gt(offset, 0xffffffffffffffff) { revert(0, 0) }\n\n value0, value1 := abi_decode_t_array$_t_uint256_$dyn_calldata_ptr(add(headStart, offset), dataEnd)\n }\n\n }\n\n}\n", + "contents": "{\n\n // uint256[]\n function abi_decode_t_array$_t_uint256_$dyn_calldata_ptr(offset, end) -> arrayPos, length {\n if iszero(slt(add(offset, 0x1f), end)) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() }\n length := calldataload(offset)\n if gt(length, 0xffffffffffffffff) { revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490() }\n arrayPos := add(offset, 0x20)\n if gt(add(arrayPos, mul(length, 0x20)), end) { revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() }\n }\n\n function abi_decode_tuple_t_array$_t_uint256_$dyn_calldata_ptr(headStart, dataEnd) -> value0, value1 {\n if slt(sub(dataEnd, headStart), 32) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() }\n\n {\n\n let offset := calldataload(add(headStart, 0))\n if gt(offset, 0xffffffffffffffff) { revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() }\n\n value0, value1 := abi_decode_t_array$_t_uint256_$dyn_calldata_ptr(add(headStart, offset), dataEnd)\n }\n\n }\n\n function allocate_unbounded() -> memPtr {\n memPtr := mload(64)\n }\n\n function revert_error_15abf5612cd996bc235ba1e55a4a30ac60e6bb601ff7ba4ad3f179b6be8d0490() {\n revert(0, 0)\n }\n\n function revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() {\n revert(0, 0)\n }\n\n function revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() {\n revert(0, 0)\n }\n\n function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() {\n revert(0, 0)\n }\n\n function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() {\n revert(0, 0)\n }\n\n}\n", "id": 1, "language": "Yul", "name": "#utility.yul" diff --git a/test/cmdlineTests/exp_base_literal/output b/test/cmdlineTests/exp_base_literal/output index 698180169..14b1a616a 100644 --- a/test/cmdlineTests/exp_base_literal/output +++ b/test/cmdlineTests/exp_base_literal/output @@ -10,7 +10,7 @@ IR: object "C_81" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_C_81() @@ -27,6 +27,10 @@ object "C_81" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object "C_81_deployed" { code { @@ -41,7 +45,7 @@ object "C_81" { { // f(uint256,uint256,uint256,uint256) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } let param_0, param_1, param_2, param_3 := abi_decode_tuple_t_uint256t_uint256t_uint256t_uint256(4, calldatasize()) let ret_0, ret_1, ret_2, ret_3 := fun_f_80(param_0, param_1, param_2, param_3) let memPos := allocate_unbounded() @@ -52,7 +56,7 @@ object "C_81" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() function abi_decode_t_uint256(offset, end) -> value { value := calldataload(offset) @@ -60,7 +64,7 @@ object "C_81" { } function abi_decode_tuple_t_uint256t_uint256t_uint256t_uint256(headStart, dataEnd) -> value0, value1, value2, value3 { - if slt(sub(dataEnd, headStart), 128) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 128) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } { @@ -290,6 +294,22 @@ object "C_81" { revert(0, 0x24) } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + revert(0, 0) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function shift_right_224_unsigned(value) -> newValue { newValue := diff --git a/test/cmdlineTests/name_simplifier/output b/test/cmdlineTests/name_simplifier/output index 526e5ce0f..96f2ec391 100644 --- a/test/cmdlineTests/name_simplifier/output +++ b/test/cmdlineTests/name_simplifier/output @@ -45,7 +45,7 @@ object "C_59" { for { } lt(i, _4) { i := add(i, 1) } { if slt(sub(calldatasize(), src), _2) { revert(_1, _1) } - let value := allocate_memory_1238() + let value := allocate_memory_1228() mstore(value, calldataload(src)) mstore(dst, value) dst := add(dst, _2) @@ -76,7 +76,7 @@ object "C_59" { } tail := add(add(headStart, and(add(length, 31), not(31))), 96) } - function allocate_memory_1238() -> memPtr + function allocate_memory_1228() -> memPtr { memPtr := mload(64) let newFreePtr := add(memPtr, 32) diff --git a/test/cmdlineTests/standard_generatedSources/output.json b/test/cmdlineTests/standard_generatedSources/output.json index 0a9f12669..507bc25ed 100644 --- a/test/cmdlineTests/standard_generatedSources/output.json +++ b/test/cmdlineTests/standard_generatedSources/output.json @@ -1,4 +1,4 @@ -{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:3241:1","statements":[{"body":{"nodeType":"YulBlock","src":"126:553:1","statements":[{"nodeType":"YulAssignment","src":"136:90:1","value":{"arguments":[{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"218:6:1"}],"functionName":{"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"161:56:1"},"nodeType":"YulFunctionCall","src":"161:64:1"}],"functionName":{"name":"allocate_memory","nodeType":"YulIdentifier","src":"145:15:1"},"nodeType":"YulFunctionCall","src":"145:81:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"136:5:1"}]},{"nodeType":"YulVariableDeclaration","src":"235:16:1","value":{"name":"array","nodeType":"YulIdentifier","src":"246:5:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"239:3:1","type":""}]},{"expression":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"268:5:1"},{"name":"length","nodeType":"YulIdentifier","src":"275:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"261:6:1"},"nodeType":"YulFunctionCall","src":"261:21:1"},"nodeType":"YulExpressionStatement","src":"261:21:1"},{"nodeType":"YulAssignment","src":"291:23:1","value":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"302:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"309:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"298:3:1"},"nodeType":"YulFunctionCall","src":"298:16:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"291:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"324:17:1","value":{"name":"offset","nodeType":"YulIdentifier","src":"335:6:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"328:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"390:36:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"411:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"414:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"404:6:1"},"nodeType":"YulFunctionCall","src":"404:12:1"},"nodeType":"YulExpressionStatement","src":"404:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"360:3:1"},{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"369:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"377:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"365:3:1"},"nodeType":"YulFunctionCall","src":"365:17:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"356:3:1"},"nodeType":"YulFunctionCall","src":"356:27:1"},{"name":"end","nodeType":"YulIdentifier","src":"385:3:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"353:2:1"},"nodeType":"YulFunctionCall","src":"353:36:1"},"nodeType":"YulIf","src":"350:2:1"},{"body":{"nodeType":"YulBlock","src":"495:178:1","statements":[{"nodeType":"YulVariableDeclaration","src":"510:21:1","value":{"name":"src","nodeType":"YulIdentifier","src":"528:3:1"},"variables":[{"name":"elementPos","nodeType":"YulTypedName","src":"514:10:1","type":""}]},{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"552:3:1"},{"arguments":[{"name":"elementPos","nodeType":"YulIdentifier","src":"578:10:1"},{"name":"end","nodeType":"YulIdentifier","src":"590:3:1"}],"functionName":{"name":"abi_decode_t_uint256","nodeType":"YulIdentifier","src":"557:20:1"},"nodeType":"YulFunctionCall","src":"557:37:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"545:6:1"},"nodeType":"YulFunctionCall","src":"545:50:1"},"nodeType":"YulExpressionStatement","src":"545:50:1"},{"nodeType":"YulAssignment","src":"608:21:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"619:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"624:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"615:3:1"},"nodeType":"YulFunctionCall","src":"615:14:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"608:3:1"}]},{"nodeType":"YulAssignment","src":"642:21:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"653:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"658:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"649:3:1"},"nodeType":"YulFunctionCall","src":"649:14:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"642:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"457:1:1"},{"name":"length","nodeType":"YulIdentifier","src":"460:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"454:2:1"},"nodeType":"YulFunctionCall","src":"454:13:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"468:18:1","statements":[{"nodeType":"YulAssignment","src":"470:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"479:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"482:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"475:3:1"},"nodeType":"YulFunctionCall","src":"475:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"470:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"439:14:1","statements":[{"nodeType":"YulVariableDeclaration","src":"441:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"450:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"445:1:1","type":""}]}]},"src":"435:238:1"}]},"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"96:6:1","type":""},{"name":"length","nodeType":"YulTypedName","src":"104:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"112:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"120:5:1","type":""}],"src":"24:655:1"},{"body":{"nodeType":"YulBlock","src":"779:226:1","statements":[{"body":{"nodeType":"YulBlock","src":"828:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"837:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"840:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"830:6:1"},"nodeType":"YulFunctionCall","src":"830:12:1"},"nodeType":"YulExpressionStatement","src":"830:12:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"807:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"815:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"803:3:1"},"nodeType":"YulFunctionCall","src":"803:17:1"},{"name":"end","nodeType":"YulIdentifier","src":"822:3:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"799:3:1"},"nodeType":"YulFunctionCall","src":"799:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"792:6:1"},"nodeType":"YulFunctionCall","src":"792:35:1"},"nodeType":"YulIf","src":"789:2:1"},{"nodeType":"YulVariableDeclaration","src":"853:34:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"880:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"867:12:1"},"nodeType":"YulFunctionCall","src":"867:20:1"},"variables":[{"name":"length","nodeType":"YulTypedName","src":"857:6:1","type":""}]},{"nodeType":"YulAssignment","src":"896:103:1","value":{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"972:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"980:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"968:3:1"},"nodeType":"YulFunctionCall","src":"968:17:1"},{"name":"length","nodeType":"YulIdentifier","src":"987:6:1"},{"name":"end","nodeType":"YulIdentifier","src":"995:3:1"}],"functionName":{"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"905:62:1"},"nodeType":"YulFunctionCall","src":"905:94:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"896:5:1"}]}]},"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"757:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"765:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"773:5:1","type":""}],"src":"702:303:1"},{"body":{"nodeType":"YulBlock","src":"1063:87:1","statements":[{"nodeType":"YulAssignment","src":"1073:29:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1095:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1082:12:1"},"nodeType":"YulFunctionCall","src":"1082:20:1"},"variableNames":[{"name":"value","nodeType":"YulIdentifier","src":"1073:5:1"}]},{"expression":{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1138:5:1"}],"functionName":{"name":"validator_revert_t_uint256","nodeType":"YulIdentifier","src":"1111:26:1"},"nodeType":"YulFunctionCall","src":"1111:33:1"},"nodeType":"YulExpressionStatement","src":"1111:33:1"}]},"name":"abi_decode_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"1041:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"1049:3:1","type":""}],"returnVariables":[{"name":"value","nodeType":"YulTypedName","src":"1057:5:1","type":""}],"src":"1011:139:1"},{"body":{"nodeType":"YulBlock","src":"1247:314:1","statements":[{"body":{"nodeType":"YulBlock","src":"1293:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1302:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1305:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1295:6:1"},"nodeType":"YulFunctionCall","src":"1295:12:1"},"nodeType":"YulExpressionStatement","src":"1295:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"1268:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"1277:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"1264:3:1"},"nodeType":"YulFunctionCall","src":"1264:23:1"},{"kind":"number","nodeType":"YulLiteral","src":"1289:2:1","type":"","value":"32"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"1260:3:1"},"nodeType":"YulFunctionCall","src":"1260:32:1"},"nodeType":"YulIf","src":"1257:2:1"},{"nodeType":"YulBlock","src":"1319:235:1","statements":[{"nodeType":"YulVariableDeclaration","src":"1334:45:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1365:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1376:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1361:3:1"},"nodeType":"YulFunctionCall","src":"1361:17:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1348:12:1"},"nodeType":"YulFunctionCall","src":"1348:31:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"1338:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1426:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"1435:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"1438:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"1428:6:1"},"nodeType":"YulFunctionCall","src":"1428:12:1"},"nodeType":"YulExpressionStatement","src":"1428:12:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1398:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1406:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"1395:2:1"},"nodeType":"YulFunctionCall","src":"1395:30:1"},"nodeType":"YulIf","src":"1392:2:1"},{"nodeType":"YulAssignment","src":"1456:88:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1516:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"1527:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1512:3:1"},"nodeType":"YulFunctionCall","src":"1512:22:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"1536:7:1"}],"functionName":{"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1466:45:1"},"nodeType":"YulFunctionCall","src":"1466:78:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1456:6:1"}]}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1217:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"1228:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"1240:6:1","type":""}],"src":"1156:405:1"},{"body":{"nodeType":"YulBlock","src":"1632:53:1","statements":[{"expression":{"arguments":[{"name":"pos","nodeType":"YulIdentifier","src":"1649:3:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1672:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"1654:17:1"},"nodeType":"YulFunctionCall","src":"1654:24:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1642:6:1"},"nodeType":"YulFunctionCall","src":"1642:37:1"},"nodeType":"YulExpressionStatement","src":"1642:37:1"}]},"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"1620:5:1","type":""},{"name":"pos","nodeType":"YulTypedName","src":"1627:3:1","type":""}],"src":"1567:118:1"},{"body":{"nodeType":"YulBlock","src":"1789:124:1","statements":[{"nodeType":"YulAssignment","src":"1799:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1811:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1822:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1807:3:1"},"nodeType":"YulFunctionCall","src":"1807:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"1799:4:1"}]},{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"1879:6:1"},{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1892:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1903:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1888:3:1"},"nodeType":"YulFunctionCall","src":"1888:17:1"}],"functionName":{"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulIdentifier","src":"1835:43:1"},"nodeType":"YulFunctionCall","src":"1835:71:1"},"nodeType":"YulExpressionStatement","src":"1835:71:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1761:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"1773:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"1784:4:1","type":""}],"src":"1691:222:1"},{"body":{"nodeType":"YulBlock","src":"1960:88:1","statements":[{"nodeType":"YulAssignment","src":"1970:30:1","value":{"arguments":[],"functionName":{"name":"allocate_unbounded","nodeType":"YulIdentifier","src":"1980:18:1"},"nodeType":"YulFunctionCall","src":"1980:20:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"1970:6:1"}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2029:6:1"},{"name":"size","nodeType":"YulIdentifier","src":"2037:4:1"}],"functionName":{"name":"finalize_allocation","nodeType":"YulIdentifier","src":"2009:19:1"},"nodeType":"YulFunctionCall","src":"2009:33:1"},"nodeType":"YulExpressionStatement","src":"2009:33:1"}]},"name":"allocate_memory","nodeType":"YulFunctionDefinition","parameters":[{"name":"size","nodeType":"YulTypedName","src":"1944:4:1","type":""}],"returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"1953:6:1","type":""}],"src":"1919:129:1"},{"body":{"nodeType":"YulBlock","src":"2094:35:1","statements":[{"nodeType":"YulAssignment","src":"2104:19:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2120:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"2114:5:1"},"nodeType":"YulFunctionCall","src":"2114:9:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2104:6:1"}]}]},"name":"allocate_unbounded","nodeType":"YulFunctionDefinition","returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2087:6:1","type":""}],"src":"2054:75:1"},{"body":{"nodeType":"YulBlock","src":"2217:229:1","statements":[{"body":{"nodeType":"YulBlock","src":"2322:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"2324:16:1"},"nodeType":"YulFunctionCall","src":"2324:18:1"},"nodeType":"YulExpressionStatement","src":"2324:18:1"}]},"condition":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2294:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2302:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2291:2:1"},"nodeType":"YulFunctionCall","src":"2291:30:1"},"nodeType":"YulIf","src":"2288:2:1"},{"nodeType":"YulAssignment","src":"2354:25:1","value":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2366:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2374:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"2362:3:1"},"nodeType":"YulFunctionCall","src":"2362:17:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2354:4:1"}]},{"nodeType":"YulAssignment","src":"2416:23:1","value":{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2428:4:1"},{"kind":"number","nodeType":"YulLiteral","src":"2434:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2424:3:1"},"nodeType":"YulFunctionCall","src":"2424:15:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2416:4:1"}]}]},"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"length","nodeType":"YulTypedName","src":"2201:6:1","type":""}],"returnVariables":[{"name":"size","nodeType":"YulTypedName","src":"2212:4:1","type":""}],"src":"2135:311:1"},{"body":{"nodeType":"YulBlock","src":"2497:32:1","statements":[{"nodeType":"YulAssignment","src":"2507:16:1","value":{"name":"value","nodeType":"YulIdentifier","src":"2518:5:1"},"variableNames":[{"name":"cleaned","nodeType":"YulIdentifier","src":"2507:7:1"}]}]},"name":"cleanup_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"2479:5:1","type":""}],"returnVariables":[{"name":"cleaned","nodeType":"YulTypedName","src":"2489:7:1","type":""}],"src":"2452:77:1"},{"body":{"nodeType":"YulBlock","src":"2578:238:1","statements":[{"nodeType":"YulVariableDeclaration","src":"2588:58:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2610:6:1"},{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2640:4:1"}],"functionName":{"name":"round_up_to_mul_of_32","nodeType":"YulIdentifier","src":"2618:21:1"},"nodeType":"YulFunctionCall","src":"2618:27:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2606:3:1"},"nodeType":"YulFunctionCall","src":"2606:40:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"2592:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"2757:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"2759:16:1"},"nodeType":"YulFunctionCall","src":"2759:18:1"},"nodeType":"YulExpressionStatement","src":"2759:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2700:10:1"},{"kind":"number","nodeType":"YulLiteral","src":"2712:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2697:2:1"},"nodeType":"YulFunctionCall","src":"2697:34:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2736:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"2748:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"2733:2:1"},"nodeType":"YulFunctionCall","src":"2733:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"2694:2:1"},"nodeType":"YulFunctionCall","src":"2694:62:1"},"nodeType":"YulIf","src":"2691:2:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2795:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2799:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"2788:6:1"},"nodeType":"YulFunctionCall","src":"2788:22:1"},"nodeType":"YulExpressionStatement","src":"2788:22:1"}]},"name":"finalize_allocation","nodeType":"YulFunctionDefinition","parameters":[{"name":"memPtr","nodeType":"YulTypedName","src":"2564:6:1","type":""},{"name":"size","nodeType":"YulTypedName","src":"2572:4:1","type":""}],"src":"2535:281:1"},{"body":{"nodeType":"YulBlock","src":"2850:152:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2867:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2870:77:1","type":"","value":"35408467139433450592217433187231851964531694900788300625387963629091585785856"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"2860:6:1"},"nodeType":"YulFunctionCall","src":"2860:88:1"},"nodeType":"YulExpressionStatement","src":"2860:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2964:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"2967:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"2957:6:1"},"nodeType":"YulFunctionCall","src":"2957:15:1"},"nodeType":"YulExpressionStatement","src":"2957:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2988:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"2991:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"2981:6:1"},"nodeType":"YulFunctionCall","src":"2981:15:1"},"nodeType":"YulExpressionStatement","src":"2981:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"2822:180:1"},{"body":{"nodeType":"YulBlock","src":"3056:54:1","statements":[{"nodeType":"YulAssignment","src":"3066:38:1","value":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3084:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"3091:2:1","type":"","value":"31"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3080:3:1"},"nodeType":"YulFunctionCall","src":"3080:14:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3100:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"3096:3:1"},"nodeType":"YulFunctionCall","src":"3096:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"3076:3:1"},"nodeType":"YulFunctionCall","src":"3076:28:1"},"variableNames":[{"name":"result","nodeType":"YulIdentifier","src":"3066:6:1"}]}]},"name":"round_up_to_mul_of_32","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3039:5:1","type":""}],"returnVariables":[{"name":"result","nodeType":"YulTypedName","src":"3049:6:1","type":""}],"src":"3008:102:1"},{"body":{"nodeType":"YulBlock","src":"3159:79:1","statements":[{"body":{"nodeType":"YulBlock","src":"3216:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3225:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3228:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3218:6:1"},"nodeType":"YulFunctionCall","src":"3218:12:1"},"nodeType":"YulExpressionStatement","src":"3218:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3182:5:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3207:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"3189:17:1"},"nodeType":"YulFunctionCall","src":"3189:24:1"}],"functionName":{"name":"eq","nodeType":"YulIdentifier","src":"3179:2:1"},"nodeType":"YulFunctionCall","src":"3179:35:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"3172:6:1"},"nodeType":"YulFunctionCall","src":"3172:43:1"},"nodeType":"YulIf","src":"3169:2:1"}]},"name":"validator_revert_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3152:5:1","type":""}],"src":"3116:122:1"}]},"contents":"{ +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"generatedSources":[],"object":""},"deployedBytecode":{"generatedSources":[{"ast":{"nodeType":"YulBlock","src":"0:4001:1","statements":[{"body":{"nodeType":"YulBlock","src":"126:620:1","statements":[{"nodeType":"YulAssignment","src":"136:90:1","value":{"arguments":[{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"218:6:1"}],"functionName":{"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"161:56:1"},"nodeType":"YulFunctionCall","src":"161:64:1"}],"functionName":{"name":"allocate_memory","nodeType":"YulIdentifier","src":"145:15:1"},"nodeType":"YulFunctionCall","src":"145:81:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"136:5:1"}]},{"nodeType":"YulVariableDeclaration","src":"235:16:1","value":{"name":"array","nodeType":"YulIdentifier","src":"246:5:1"},"variables":[{"name":"dst","nodeType":"YulTypedName","src":"239:3:1","type":""}]},{"expression":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"268:5:1"},{"name":"length","nodeType":"YulIdentifier","src":"275:6:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"261:6:1"},"nodeType":"YulFunctionCall","src":"261:21:1"},"nodeType":"YulExpressionStatement","src":"261:21:1"},{"nodeType":"YulAssignment","src":"291:23:1","value":{"arguments":[{"name":"array","nodeType":"YulIdentifier","src":"302:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"309:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"298:3:1"},"nodeType":"YulFunctionCall","src":"298:16:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"291:3:1"}]},{"nodeType":"YulVariableDeclaration","src":"324:17:1","value":{"name":"offset","nodeType":"YulIdentifier","src":"335:6:1"},"variables":[{"name":"src","nodeType":"YulTypedName","src":"328:3:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"390:103:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulIdentifier","src":"404:77:1"},"nodeType":"YulFunctionCall","src":"404:79:1"},"nodeType":"YulExpressionStatement","src":"404:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"360:3:1"},{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"369:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"377:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"365:3:1"},"nodeType":"YulFunctionCall","src":"365:17:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"356:3:1"},"nodeType":"YulFunctionCall","src":"356:27:1"},{"name":"end","nodeType":"YulIdentifier","src":"385:3:1"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"353:2:1"},"nodeType":"YulFunctionCall","src":"353:36:1"},"nodeType":"YulIf","src":"350:2:1"},{"body":{"nodeType":"YulBlock","src":"562:178:1","statements":[{"nodeType":"YulVariableDeclaration","src":"577:21:1","value":{"name":"src","nodeType":"YulIdentifier","src":"595:3:1"},"variables":[{"name":"elementPos","nodeType":"YulTypedName","src":"581:10:1","type":""}]},{"expression":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"619:3:1"},{"arguments":[{"name":"elementPos","nodeType":"YulIdentifier","src":"645:10:1"},{"name":"end","nodeType":"YulIdentifier","src":"657:3:1"}],"functionName":{"name":"abi_decode_t_uint256","nodeType":"YulIdentifier","src":"624:20:1"},"nodeType":"YulFunctionCall","src":"624:37:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"612:6:1"},"nodeType":"YulFunctionCall","src":"612:50:1"},"nodeType":"YulExpressionStatement","src":"612:50:1"},{"nodeType":"YulAssignment","src":"675:21:1","value":{"arguments":[{"name":"dst","nodeType":"YulIdentifier","src":"686:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"691:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"682:3:1"},"nodeType":"YulFunctionCall","src":"682:14:1"},"variableNames":[{"name":"dst","nodeType":"YulIdentifier","src":"675:3:1"}]},{"nodeType":"YulAssignment","src":"709:21:1","value":{"arguments":[{"name":"src","nodeType":"YulIdentifier","src":"720:3:1"},{"kind":"number","nodeType":"YulLiteral","src":"725:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"716:3:1"},"nodeType":"YulFunctionCall","src":"716:14:1"},"variableNames":[{"name":"src","nodeType":"YulIdentifier","src":"709:3:1"}]}]},"condition":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"524:1:1"},{"name":"length","nodeType":"YulIdentifier","src":"527:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"521:2:1"},"nodeType":"YulFunctionCall","src":"521:13:1"},"nodeType":"YulForLoop","post":{"nodeType":"YulBlock","src":"535:18:1","statements":[{"nodeType":"YulAssignment","src":"537:14:1","value":{"arguments":[{"name":"i","nodeType":"YulIdentifier","src":"546:1:1"},{"kind":"number","nodeType":"YulLiteral","src":"549:1:1","type":"","value":"1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"542:3:1"},"nodeType":"YulFunctionCall","src":"542:9:1"},"variableNames":[{"name":"i","nodeType":"YulIdentifier","src":"537:1:1"}]}]},"pre":{"nodeType":"YulBlock","src":"506:14:1","statements":[{"nodeType":"YulVariableDeclaration","src":"508:10:1","value":{"kind":"number","nodeType":"YulLiteral","src":"517:1:1","type":"","value":"0"},"variables":[{"name":"i","nodeType":"YulTypedName","src":"512:1:1","type":""}]}]},"src":"502:238:1"}]},"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"96:6:1","type":""},{"name":"length","nodeType":"YulTypedName","src":"104:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"112:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"120:5:1","type":""}],"src":"24:722:1"},{"body":{"nodeType":"YulBlock","src":"846:293:1","statements":[{"body":{"nodeType":"YulBlock","src":"895:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulIdentifier","src":"897:77:1"},"nodeType":"YulFunctionCall","src":"897:79:1"},"nodeType":"YulExpressionStatement","src":"897:79:1"}]},"condition":{"arguments":[{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"874:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"882:4:1","type":"","value":"0x1f"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"870:3:1"},"nodeType":"YulFunctionCall","src":"870:17:1"},{"name":"end","nodeType":"YulIdentifier","src":"889:3:1"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"866:3:1"},"nodeType":"YulFunctionCall","src":"866:27:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"859:6:1"},"nodeType":"YulFunctionCall","src":"859:35:1"},"nodeType":"YulIf","src":"856:2:1"},{"nodeType":"YulVariableDeclaration","src":"987:34:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1014:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1001:12:1"},"nodeType":"YulFunctionCall","src":"1001:20:1"},"variables":[{"name":"length","nodeType":"YulTypedName","src":"991:6:1","type":""}]},{"nodeType":"YulAssignment","src":"1030:103:1","value":{"arguments":[{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1106:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1114:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1102:3:1"},"nodeType":"YulFunctionCall","src":"1102:17:1"},{"name":"length","nodeType":"YulIdentifier","src":"1121:6:1"},{"name":"end","nodeType":"YulIdentifier","src":"1129:3:1"}],"functionName":{"name":"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1039:62:1"},"nodeType":"YulFunctionCall","src":"1039:94:1"},"variableNames":[{"name":"array","nodeType":"YulIdentifier","src":"1030:5:1"}]}]},"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"824:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"832:3:1","type":""}],"returnVariables":[{"name":"array","nodeType":"YulTypedName","src":"840:5:1","type":""}],"src":"769:370:1"},{"body":{"nodeType":"YulBlock","src":"1197:87:1","statements":[{"nodeType":"YulAssignment","src":"1207:29:1","value":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1229:6:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1216:12:1"},"nodeType":"YulFunctionCall","src":"1216:20:1"},"variableNames":[{"name":"value","nodeType":"YulIdentifier","src":"1207:5:1"}]},{"expression":{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1272:5:1"}],"functionName":{"name":"validator_revert_t_uint256","nodeType":"YulIdentifier","src":"1245:26:1"},"nodeType":"YulFunctionCall","src":"1245:33:1"},"nodeType":"YulExpressionStatement","src":"1245:33:1"}]},"name":"abi_decode_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"offset","nodeType":"YulTypedName","src":"1175:6:1","type":""},{"name":"end","nodeType":"YulTypedName","src":"1183:3:1","type":""}],"returnVariables":[{"name":"value","nodeType":"YulTypedName","src":"1191:5:1","type":""}],"src":"1145:139:1"},{"body":{"nodeType":"YulBlock","src":"1381:448:1","statements":[{"body":{"nodeType":"YulBlock","src":"1427:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulIdentifier","src":"1429:77:1"},"nodeType":"YulFunctionCall","src":"1429:79:1"},"nodeType":"YulExpressionStatement","src":"1429:79:1"}]},"condition":{"arguments":[{"arguments":[{"name":"dataEnd","nodeType":"YulIdentifier","src":"1402:7:1"},{"name":"headStart","nodeType":"YulIdentifier","src":"1411:9:1"}],"functionName":{"name":"sub","nodeType":"YulIdentifier","src":"1398:3:1"},"nodeType":"YulFunctionCall","src":"1398:23:1"},{"kind":"number","nodeType":"YulLiteral","src":"1423:2:1","type":"","value":"32"}],"functionName":{"name":"slt","nodeType":"YulIdentifier","src":"1394:3:1"},"nodeType":"YulFunctionCall","src":"1394:32:1"},"nodeType":"YulIf","src":"1391:2:1"},{"nodeType":"YulBlock","src":"1520:302:1","statements":[{"nodeType":"YulVariableDeclaration","src":"1535:45:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1566:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"1577:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1562:3:1"},"nodeType":"YulFunctionCall","src":"1562:17:1"}],"functionName":{"name":"calldataload","nodeType":"YulIdentifier","src":"1549:12:1"},"nodeType":"YulFunctionCall","src":"1549:31:1"},"variables":[{"name":"offset","nodeType":"YulTypedName","src":"1539:6:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"1627:83:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulIdentifier","src":"1629:77:1"},"nodeType":"YulFunctionCall","src":"1629:79:1"},"nodeType":"YulExpressionStatement","src":"1629:79:1"}]},"condition":{"arguments":[{"name":"offset","nodeType":"YulIdentifier","src":"1599:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"1607:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"1596:2:1"},"nodeType":"YulFunctionCall","src":"1596:30:1"},"nodeType":"YulIf","src":"1593:2:1"},{"nodeType":"YulAssignment","src":"1724:88:1","value":{"arguments":[{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"1784:9:1"},{"name":"offset","nodeType":"YulIdentifier","src":"1795:6:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"1780:3:1"},"nodeType":"YulFunctionCall","src":"1780:22:1"},{"name":"dataEnd","nodeType":"YulIdentifier","src":"1804:7:1"}],"functionName":{"name":"abi_decode_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulIdentifier","src":"1734:45:1"},"nodeType":"YulFunctionCall","src":"1734:78:1"},"variableNames":[{"name":"value0","nodeType":"YulIdentifier","src":"1724:6:1"}]}]}]},"name":"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"1351:9:1","type":""},{"name":"dataEnd","nodeType":"YulTypedName","src":"1362:7:1","type":""}],"returnVariables":[{"name":"value0","nodeType":"YulTypedName","src":"1374:6:1","type":""}],"src":"1290:539:1"},{"body":{"nodeType":"YulBlock","src":"1900:53:1","statements":[{"expression":{"arguments":[{"name":"pos","nodeType":"YulIdentifier","src":"1917:3:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"1940:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"1922:17:1"},"nodeType":"YulFunctionCall","src":"1922:24:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"1910:6:1"},"nodeType":"YulFunctionCall","src":"1910:37:1"},"nodeType":"YulExpressionStatement","src":"1910:37:1"}]},"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"1888:5:1","type":""},{"name":"pos","nodeType":"YulTypedName","src":"1895:3:1","type":""}],"src":"1835:118:1"},{"body":{"nodeType":"YulBlock","src":"2057:124:1","statements":[{"nodeType":"YulAssignment","src":"2067:26:1","value":{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2079:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2090:2:1","type":"","value":"32"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2075:3:1"},"nodeType":"YulFunctionCall","src":"2075:18:1"},"variableNames":[{"name":"tail","nodeType":"YulIdentifier","src":"2067:4:1"}]},{"expression":{"arguments":[{"name":"value0","nodeType":"YulIdentifier","src":"2147:6:1"},{"arguments":[{"name":"headStart","nodeType":"YulIdentifier","src":"2160:9:1"},{"kind":"number","nodeType":"YulLiteral","src":"2171:1:1","type":"","value":"0"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2156:3:1"},"nodeType":"YulFunctionCall","src":"2156:17:1"}],"functionName":{"name":"abi_encode_t_uint256_to_t_uint256_fromStack","nodeType":"YulIdentifier","src":"2103:43:1"},"nodeType":"YulFunctionCall","src":"2103:71:1"},"nodeType":"YulExpressionStatement","src":"2103:71:1"}]},"name":"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed","nodeType":"YulFunctionDefinition","parameters":[{"name":"headStart","nodeType":"YulTypedName","src":"2029:9:1","type":""},{"name":"value0","nodeType":"YulTypedName","src":"2041:6:1","type":""}],"returnVariables":[{"name":"tail","nodeType":"YulTypedName","src":"2052:4:1","type":""}],"src":"1959:222:1"},{"body":{"nodeType":"YulBlock","src":"2228:88:1","statements":[{"nodeType":"YulAssignment","src":"2238:30:1","value":{"arguments":[],"functionName":{"name":"allocate_unbounded","nodeType":"YulIdentifier","src":"2248:18:1"},"nodeType":"YulFunctionCall","src":"2248:20:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2238:6:1"}]},{"expression":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2297:6:1"},{"name":"size","nodeType":"YulIdentifier","src":"2305:4:1"}],"functionName":{"name":"finalize_allocation","nodeType":"YulIdentifier","src":"2277:19:1"},"nodeType":"YulFunctionCall","src":"2277:33:1"},"nodeType":"YulExpressionStatement","src":"2277:33:1"}]},"name":"allocate_memory","nodeType":"YulFunctionDefinition","parameters":[{"name":"size","nodeType":"YulTypedName","src":"2212:4:1","type":""}],"returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2221:6:1","type":""}],"src":"2187:129:1"},{"body":{"nodeType":"YulBlock","src":"2362:35:1","statements":[{"nodeType":"YulAssignment","src":"2372:19:1","value":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"2388:2:1","type":"","value":"64"}],"functionName":{"name":"mload","nodeType":"YulIdentifier","src":"2382:5:1"},"nodeType":"YulFunctionCall","src":"2382:9:1"},"variableNames":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2372:6:1"}]}]},"name":"allocate_unbounded","nodeType":"YulFunctionDefinition","returnVariables":[{"name":"memPtr","nodeType":"YulTypedName","src":"2355:6:1","type":""}],"src":"2322:75:1"},{"body":{"nodeType":"YulBlock","src":"2485:229:1","statements":[{"body":{"nodeType":"YulBlock","src":"2590:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"2592:16:1"},"nodeType":"YulFunctionCall","src":"2592:18:1"},"nodeType":"YulExpressionStatement","src":"2592:18:1"}]},"condition":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2562:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2570:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2559:2:1"},"nodeType":"YulFunctionCall","src":"2559:30:1"},"nodeType":"YulIf","src":"2556:2:1"},{"nodeType":"YulAssignment","src":"2622:25:1","value":{"arguments":[{"name":"length","nodeType":"YulIdentifier","src":"2634:6:1"},{"kind":"number","nodeType":"YulLiteral","src":"2642:4:1","type":"","value":"0x20"}],"functionName":{"name":"mul","nodeType":"YulIdentifier","src":"2630:3:1"},"nodeType":"YulFunctionCall","src":"2630:17:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2622:4:1"}]},{"nodeType":"YulAssignment","src":"2684:23:1","value":{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2696:4:1"},{"kind":"number","nodeType":"YulLiteral","src":"2702:4:1","type":"","value":"0x20"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2692:3:1"},"nodeType":"YulFunctionCall","src":"2692:15:1"},"variableNames":[{"name":"size","nodeType":"YulIdentifier","src":"2684:4:1"}]}]},"name":"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr","nodeType":"YulFunctionDefinition","parameters":[{"name":"length","nodeType":"YulTypedName","src":"2469:6:1","type":""}],"returnVariables":[{"name":"size","nodeType":"YulTypedName","src":"2480:4:1","type":""}],"src":"2403:311:1"},{"body":{"nodeType":"YulBlock","src":"2765:32:1","statements":[{"nodeType":"YulAssignment","src":"2775:16:1","value":{"name":"value","nodeType":"YulIdentifier","src":"2786:5:1"},"variableNames":[{"name":"cleaned","nodeType":"YulIdentifier","src":"2775:7:1"}]}]},"name":"cleanup_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"2747:5:1","type":""}],"returnVariables":[{"name":"cleaned","nodeType":"YulTypedName","src":"2757:7:1","type":""}],"src":"2720:77:1"},{"body":{"nodeType":"YulBlock","src":"2846:238:1","statements":[{"nodeType":"YulVariableDeclaration","src":"2856:58:1","value":{"arguments":[{"name":"memPtr","nodeType":"YulIdentifier","src":"2878:6:1"},{"arguments":[{"name":"size","nodeType":"YulIdentifier","src":"2908:4:1"}],"functionName":{"name":"round_up_to_mul_of_32","nodeType":"YulIdentifier","src":"2886:21:1"},"nodeType":"YulFunctionCall","src":"2886:27:1"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"2874:3:1"},"nodeType":"YulFunctionCall","src":"2874:40:1"},"variables":[{"name":"newFreePtr","nodeType":"YulTypedName","src":"2860:10:1","type":""}]},{"body":{"nodeType":"YulBlock","src":"3025:22:1","statements":[{"expression":{"arguments":[],"functionName":{"name":"panic_error_0x41","nodeType":"YulIdentifier","src":"3027:16:1"},"nodeType":"YulFunctionCall","src":"3027:18:1"},"nodeType":"YulExpressionStatement","src":"3027:18:1"}]},"condition":{"arguments":[{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"2968:10:1"},{"kind":"number","nodeType":"YulLiteral","src":"2980:18:1","type":"","value":"0xffffffffffffffff"}],"functionName":{"name":"gt","nodeType":"YulIdentifier","src":"2965:2:1"},"nodeType":"YulFunctionCall","src":"2965:34:1"},{"arguments":[{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3004:10:1"},{"name":"memPtr","nodeType":"YulIdentifier","src":"3016:6:1"}],"functionName":{"name":"lt","nodeType":"YulIdentifier","src":"3001:2:1"},"nodeType":"YulFunctionCall","src":"3001:22:1"}],"functionName":{"name":"or","nodeType":"YulIdentifier","src":"2962:2:1"},"nodeType":"YulFunctionCall","src":"2962:62:1"},"nodeType":"YulIf","src":"2959:2:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3063:2:1","type":"","value":"64"},{"name":"newFreePtr","nodeType":"YulIdentifier","src":"3067:10:1"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3056:6:1"},"nodeType":"YulFunctionCall","src":"3056:22:1"},"nodeType":"YulExpressionStatement","src":"3056:22:1"}]},"name":"finalize_allocation","nodeType":"YulFunctionDefinition","parameters":[{"name":"memPtr","nodeType":"YulTypedName","src":"2832:6:1","type":""},{"name":"size","nodeType":"YulTypedName","src":"2840:4:1","type":""}],"src":"2803:281:1"},{"body":{"nodeType":"YulBlock","src":"3118:152:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3135:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3138:77:1","type":"","value":"35408467139433450592217433187231851964531694900788300625387963629091585785856"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3128:6:1"},"nodeType":"YulFunctionCall","src":"3128:88:1"},"nodeType":"YulExpressionStatement","src":"3128:88:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3232:1:1","type":"","value":"4"},{"kind":"number","nodeType":"YulLiteral","src":"3235:4:1","type":"","value":"0x41"}],"functionName":{"name":"mstore","nodeType":"YulIdentifier","src":"3225:6:1"},"nodeType":"YulFunctionCall","src":"3225:15:1"},"nodeType":"YulExpressionStatement","src":"3225:15:1"},{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3256:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3259:4:1","type":"","value":"0x24"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3249:6:1"},"nodeType":"YulFunctionCall","src":"3249:15:1"},"nodeType":"YulExpressionStatement","src":"3249:15:1"}]},"name":"panic_error_0x41","nodeType":"YulFunctionDefinition","src":"3090:180:1"},{"body":{"nodeType":"YulBlock","src":"3365:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3382:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3385:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3375:6:1"},"nodeType":"YulFunctionCall","src":"3375:12:1"},"nodeType":"YulExpressionStatement","src":"3375:12:1"}]},"name":"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d","nodeType":"YulFunctionDefinition","src":"3276:117:1"},{"body":{"nodeType":"YulBlock","src":"3488:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3505:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3508:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3498:6:1"},"nodeType":"YulFunctionCall","src":"3498:12:1"},"nodeType":"YulExpressionStatement","src":"3498:12:1"}]},"name":"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef","nodeType":"YulFunctionDefinition","src":"3399:117:1"},{"body":{"nodeType":"YulBlock","src":"3611:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3628:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3631:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3621:6:1"},"nodeType":"YulFunctionCall","src":"3621:12:1"},"nodeType":"YulExpressionStatement","src":"3621:12:1"}]},"name":"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db","nodeType":"YulFunctionDefinition","src":"3522:117:1"},{"body":{"nodeType":"YulBlock","src":"3734:28:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3751:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3754:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3744:6:1"},"nodeType":"YulFunctionCall","src":"3744:12:1"},"nodeType":"YulExpressionStatement","src":"3744:12:1"}]},"name":"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b","nodeType":"YulFunctionDefinition","src":"3645:117:1"},{"body":{"nodeType":"YulBlock","src":"3816:54:1","statements":[{"nodeType":"YulAssignment","src":"3826:38:1","value":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3844:5:1"},{"kind":"number","nodeType":"YulLiteral","src":"3851:2:1","type":"","value":"31"}],"functionName":{"name":"add","nodeType":"YulIdentifier","src":"3840:3:1"},"nodeType":"YulFunctionCall","src":"3840:14:1"},{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3860:2:1","type":"","value":"31"}],"functionName":{"name":"not","nodeType":"YulIdentifier","src":"3856:3:1"},"nodeType":"YulFunctionCall","src":"3856:7:1"}],"functionName":{"name":"and","nodeType":"YulIdentifier","src":"3836:3:1"},"nodeType":"YulFunctionCall","src":"3836:28:1"},"variableNames":[{"name":"result","nodeType":"YulIdentifier","src":"3826:6:1"}]}]},"name":"round_up_to_mul_of_32","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3799:5:1","type":""}],"returnVariables":[{"name":"result","nodeType":"YulTypedName","src":"3809:6:1","type":""}],"src":"3768:102:1"},{"body":{"nodeType":"YulBlock","src":"3919:79:1","statements":[{"body":{"nodeType":"YulBlock","src":"3976:16:1","statements":[{"expression":{"arguments":[{"kind":"number","nodeType":"YulLiteral","src":"3985:1:1","type":"","value":"0"},{"kind":"number","nodeType":"YulLiteral","src":"3988:1:1","type":"","value":"0"}],"functionName":{"name":"revert","nodeType":"YulIdentifier","src":"3978:6:1"},"nodeType":"YulFunctionCall","src":"3978:12:1"},"nodeType":"YulExpressionStatement","src":"3978:12:1"}]},"condition":{"arguments":[{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3942:5:1"},{"arguments":[{"name":"value","nodeType":"YulIdentifier","src":"3967:5:1"}],"functionName":{"name":"cleanup_t_uint256","nodeType":"YulIdentifier","src":"3949:17:1"},"nodeType":"YulFunctionCall","src":"3949:24:1"}],"functionName":{"name":"eq","nodeType":"YulIdentifier","src":"3939:2:1"},"nodeType":"YulFunctionCall","src":"3939:35:1"}],"functionName":{"name":"iszero","nodeType":"YulIdentifier","src":"3932:6:1"},"nodeType":"YulFunctionCall","src":"3932:43:1"},"nodeType":"YulIf","src":"3929:2:1"}]},"name":"validator_revert_t_uint256","nodeType":"YulFunctionDefinition","parameters":[{"name":"value","nodeType":"YulTypedName","src":"3912:5:1","type":""}],"src":"3876:122:1"}]},"contents":"{ // uint256[] function abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(offset, length, end) -> array { @@ -10,7 +10,7 @@ let src := offset if gt(add(src, mul(length, 0x20)), end) { - revert(0, 0) + revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() } for { let i := 0 } lt(i, length) { i := add(i, 1) } { @@ -25,7 +25,7 @@ // uint256[] function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset, end) -> array { - if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } + if iszero(slt(add(offset, 0x1f), end)) { revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() } let length := calldataload(offset) array := abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr(add(offset, 0x20), length, end) } @@ -36,12 +36,12 @@ } function abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr(headStart, dataEnd) -> value0 { - if slt(sub(dataEnd, headStart), 32) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 32) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } { let offset := calldataload(add(headStart, 0)) - if gt(offset, 0xffffffffffffffff) { revert(0, 0) } + if gt(offset, 0xffffffffffffffff) { revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() } value0 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(headStart, offset), dataEnd) } @@ -96,6 +96,22 @@ revert(0, 0x24) } + function revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d() { + revert(0, 0) + } + + function revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef() { + revert(0, 0) + } + + function revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function round_up_to_mul_of_32(value) -> result { result := and(add(value, 31), not(31)) } diff --git a/test/cmdlineTests/standard_irOptimized_requested/output.json b/test/cmdlineTests/standard_irOptimized_requested/output.json index 2aeac9773..85364f6d5 100644 --- a/test/cmdlineTests/standard_irOptimized_requested/output.json +++ b/test/cmdlineTests/standard_irOptimized_requested/output.json @@ -8,7 +8,10 @@ object \"C_7\" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() + { + revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + } constructor_C_7() let _1 := allocate_unbounded() codecopy(_1, dataoffset(\"C_7_deployed\"), datasize(\"C_7_deployed\")) @@ -17,6 +20,8 @@ object \"C_7\" { { memPtr := mload(64) } function constructor_C_7() { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + { revert(0, 0) } } object \"C_7_deployed\" { code { @@ -26,7 +31,10 @@ object \"C_7\" { let selector := shift_right_224_unsigned(calldataload(0)) switch selector case 0x26121ff0 { - if callvalue() { revert(0, 0) } + if callvalue() + { + revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + } abi_decode_tuple_(4, calldatasize()) fun_f_6() let memPos := allocate_unbounded() @@ -36,10 +44,13 @@ object \"C_7\" { default { } } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 0) + { + revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() + } } function abi_encode_tuple__to__fromStack(headStart) -> tail { tail := add(headStart, 0) } @@ -47,6 +58,12 @@ object \"C_7\" { { memPtr := mload(64) } function fun_f_6() { } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + { revert(0, 0) } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + { revert(0, 0) } + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() + { revert(0, 0) } function shift_right_224_unsigned(value) -> newValue { newValue := shr(224, value) } } diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 5b91fb56f..edee7445e 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -9,7 +9,7 @@ object \"C_7\" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_C_7() @@ -26,6 +26,10 @@ object \"C_7\" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object \"C_7_deployed\" { code { @@ -40,7 +44,7 @@ object \"C_7\" { { // f() - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } abi_decode_tuple_(4, calldatasize()) fun_f_6() let memPos := allocate_unbounded() @@ -51,10 +55,10 @@ object \"C_7\" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } @@ -71,6 +75,18 @@ object \"C_7\" { } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function shift_right_224_unsigned(value) -> newValue { newValue := diff --git a/test/cmdlineTests/standard_viair_requested/output.json b/test/cmdlineTests/standard_viair_requested/output.json index ca3054c46..b09f39fa7 100644 --- a/test/cmdlineTests/standard_viair_requested/output.json +++ b/test/cmdlineTests/standard_viair_requested/output.json @@ -9,7 +9,7 @@ object \"C_3\" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_C_3() @@ -26,6 +26,10 @@ object \"C_3\" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object \"C_3_deployed\" { code { @@ -39,7 +43,15 @@ object \"C_3\" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } function shift_right_224_unsigned(value) -> newValue { newValue := @@ -65,7 +77,7 @@ object \"C_3\" { object \"D_16\" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_D_16() @@ -82,6 +94,10 @@ object \"D_16\" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object \"D_16_deployed\" { code { @@ -96,7 +112,7 @@ object \"D_16\" { { // f() - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } abi_decode_tuple_(4, calldatasize()) fun_f_15() let memPos := allocate_unbounded() @@ -107,10 +123,10 @@ object \"D_16\" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } @@ -145,6 +161,18 @@ object \"D_16\" { revert(0, 0x24) } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function revert_forward_1() { let pos := allocate_unbounded() returndatacopy(pos, 0, returndatasize()) @@ -169,7 +197,7 @@ object \"D_16\" { object \"C_3\" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_C_3() @@ -186,6 +214,10 @@ object \"D_16\" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object \"C_3_deployed\" { code { @@ -199,7 +231,15 @@ object \"D_16\" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + + function allocate_unbounded() -> memPtr { + memPtr := mload(64) + } + + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } function shift_right_224_unsigned(value) -> newValue { newValue := diff --git a/test/cmdlineTests/viair_abicoder_v1/output b/test/cmdlineTests/viair_abicoder_v1/output index 37e957259..5c227b33f 100644 --- a/test/cmdlineTests/viair_abicoder_v1/output +++ b/test/cmdlineTests/viair_abicoder_v1/output @@ -10,7 +10,7 @@ IR: object "test_11" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_test_11() @@ -27,6 +27,10 @@ object "test_11" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object "test_11_deployed" { code { @@ -41,7 +45,7 @@ object "test_11" { { // f() - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_10() let memPos := allocate_unbounded() @@ -52,10 +56,10 @@ object "test_11" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } @@ -88,6 +92,18 @@ object "test_11" { } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function shift_right_224_unsigned(value) -> newValue { newValue := diff --git a/test/cmdlineTests/yul_optimizer_steps/output b/test/cmdlineTests/yul_optimizer_steps/output index fe6a4be49..dee816447 100644 --- a/test/cmdlineTests/yul_optimizer_steps/output +++ b/test/cmdlineTests/yul_optimizer_steps/output @@ -10,13 +10,18 @@ object "C_7" { code { { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() + { + revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + } let _1 := allocate_unbounded() codecopy(_1, dataoffset("C_7_deployed"), datasize("C_7_deployed")) return(_1, datasize("C_7_deployed")) } function allocate_unbounded() -> memPtr { memPtr := mload(64) } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + { revert(0, 0) } } object "C_7_deployed" { code { @@ -28,8 +33,10 @@ object "C_7" { pop(selector) } pop(iszero(calldatasize())) - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + { revert(0, 0) } function shift_right_unsigned(value) -> newValue { newValue := shr(224, value) } } diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index 28a63d8c3..379c14171 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -9,7 +9,7 @@ object \"C_11\" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_C_11() @@ -26,6 +26,10 @@ object \"C_11\" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object \"C_11_deployed\" { code { @@ -40,7 +44,7 @@ object \"C_11\" { { // f() - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_10() let memPos := allocate_unbounded() @@ -51,10 +55,10 @@ object \"C_11\" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } @@ -156,6 +160,18 @@ object \"C_11\" { revert(0, 0x24) } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function round_up_to_mul_of_32(value) -> result { result := and(add(value, 31), not(31)) } diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index eb6e8f48c..98e28617f 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -9,7 +9,7 @@ object \"C_11\" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_C_11() @@ -26,6 +26,10 @@ object \"C_11\" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object \"C_11_deployed\" { code { @@ -40,7 +44,7 @@ object \"C_11\" { { // f() - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_10() let memPos := allocate_unbounded() @@ -51,10 +55,10 @@ object \"C_11\" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } @@ -90,6 +94,18 @@ object \"C_11\" { } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function shift_right_224_unsigned(value) -> newValue { newValue := diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index 87d0f171c..7226bfa95 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -9,7 +9,7 @@ object \"C_11\" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_C_11() @@ -26,6 +26,10 @@ object \"C_11\" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object \"C_11_deployed\" { code { @@ -40,7 +44,7 @@ object \"C_11\" { { // f() - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_10() let memPos := allocate_unbounded() @@ -51,10 +55,10 @@ object \"C_11\" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } @@ -95,6 +99,18 @@ object \"C_11\" { } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function shift_left_224(value) -> newValue { newValue := diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 25bc6ac1d..6ad20e86b 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -9,7 +9,7 @@ object \"C_11\" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_C_11() @@ -26,6 +26,10 @@ object \"C_11\" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object \"C_11_deployed\" { code { @@ -40,7 +44,7 @@ object \"C_11\" { { // f() - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_10() let memPos := allocate_unbounded() @@ -51,10 +55,10 @@ object \"C_11\" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } @@ -156,6 +160,18 @@ object \"C_11\" { revert(0, 0x24) } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function round_up_to_mul_of_32(value) -> result { result := and(add(value, 31), not(31)) } diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index 7bead0aec..dc8a2a67c 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -9,7 +9,7 @@ object \"C_11\" { code { mstore(64, 128) - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } constructor_C_11() @@ -26,6 +26,10 @@ object \"C_11\" { } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + } object \"C_11_deployed\" { code { @@ -40,7 +44,7 @@ object \"C_11\" { { // f() - if callvalue() { revert(0, 0) } + if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } abi_decode_tuple_(4, calldatasize()) let ret_0 := fun_f_10() let memPos := allocate_unbounded() @@ -51,10 +55,10 @@ object \"C_11\" { default {} } if iszero(calldatasize()) { } - revert(0, 0) + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() function abi_decode_tuple_(headStart, dataEnd) { - if slt(sub(dataEnd, headStart), 0) { revert(0, 0) } + if slt(sub(dataEnd, headStart), 0) { revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() } } @@ -95,6 +99,18 @@ object \"C_11\" { } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() { + revert(0, 0) + } + + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() { + revert(0, 0) + } + + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() { + revert(0, 0) + } + function shift_left_224(value) -> newValue { newValue := diff --git a/test/libsolidity/gasTests/abiv2.sol b/test/libsolidity/gasTests/abiv2.sol index 980a9ee28..04feacc57 100644 --- a/test/libsolidity/gasTests/abiv2.sol +++ b/test/libsolidity/gasTests/abiv2.sol @@ -14,9 +14,9 @@ contract C { } // ---- // creation: -// codeDepositCost: 1211600 -// executionCost: 1261 -// totalCost: 1212861 +// codeDepositCost: 1259800 +// executionCost: 1308 +// totalCost: 1261108 // external: // a(): 1130 // b(uint256): infinite diff --git a/test/libsolidity/gasTests/abiv2_optimised.sol b/test/libsolidity/gasTests/abiv2_optimised.sol index da44369cf..66a0bb64c 100644 --- a/test/libsolidity/gasTests/abiv2_optimised.sol +++ b/test/libsolidity/gasTests/abiv2_optimised.sol @@ -17,9 +17,9 @@ contract C { // optimize-yul: true // ---- // creation: -// codeDepositCost: 681000 +// codeDepositCost: 680600 // executionCost: 715 -// totalCost: 681715 +// totalCost: 681315 // external: // a(): 985 // b(uint256): 2052 diff --git a/test/libsolidity/gasTests/dispatch_large.sol b/test/libsolidity/gasTests/dispatch_large.sol index f4332d80d..7b00816c7 100644 --- a/test/libsolidity/gasTests/dispatch_large.sol +++ b/test/libsolidity/gasTests/dispatch_large.sol @@ -24,9 +24,9 @@ contract Large { } // ---- // creation: -// codeDepositCost: 902600 +// codeDepositCost: 904400 // executionCost: 942 -// totalCost: 903542 +// totalCost: 905342 // external: // a(): 1175 // b(uint256): infinite diff --git a/test/libsolidity/gasTests/dispatch_medium.sol b/test/libsolidity/gasTests/dispatch_medium.sol index 8dcf8d344..502ddcfbb 100644 --- a/test/libsolidity/gasTests/dispatch_medium.sol +++ b/test/libsolidity/gasTests/dispatch_medium.sol @@ -11,9 +11,9 @@ contract Medium { } // ---- // creation: -// codeDepositCost: 349600 +// codeDepositCost: 351400 // executionCost: 386 -// totalCost: 349986 +// totalCost: 351786 // external: // a(): 1152 // b(uint256): infinite diff --git a/test/libsolidity/gasTests/dispatch_small.sol b/test/libsolidity/gasTests/dispatch_small.sol index 56af8bf2b..1e866910d 100644 --- a/test/libsolidity/gasTests/dispatch_small.sol +++ b/test/libsolidity/gasTests/dispatch_small.sol @@ -6,9 +6,9 @@ contract Small { } // ---- // creation: -// codeDepositCost: 112800 +// codeDepositCost: 114600 // executionCost: 159 -// totalCost: 112959 +// totalCost: 114759 // external: // fallback: 129 // a(): 1107 diff --git a/test/libsolidity/gasTests/exp.sol b/test/libsolidity/gasTests/exp.sol index 0f69a0725..8dc59b094 100644 --- a/test/libsolidity/gasTests/exp.sol +++ b/test/libsolidity/gasTests/exp.sol @@ -19,9 +19,9 @@ contract C { // optimize-yul: false // ---- // creation: -// codeDepositCost: 109000 +// codeDepositCost: 110800 // executionCost: 159 -// totalCost: 109159 +// totalCost: 110959 // external: // exp_neg_one(uint256): 2259 // exp_one(uint256): infinite diff --git a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol index b9b928653..a24c0fd1b 100644 --- a/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol +++ b/test/libsolidity/semanticTests/array/copying/array_copy_including_array.sol @@ -37,7 +37,7 @@ contract c { // compileViaYul: also // ---- // test() -> 0x02000202 -// gas irOptimized: 2470372 +// gas irOptimized: 2476392 // gas legacy: 2288641 // gas legacyOptimized: 2258654 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol index 8e9c7ef53..3dcef2c42 100644 --- a/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol +++ b/test/libsolidity/semanticTests/array/fixed_arrays_as_return_type.sol @@ -22,5 +22,5 @@ contract B { // ---- // f() -> 2, 3, 4, 5, 6, 1000, 1001, 1002, 1003, 1004 // gas irOptimized: 135883 -// gas legacy: 264410 +// gas legacy: 266210 // gas legacyOptimized: 135699 diff --git a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol index 8a8590434..bd01bb886 100644 --- a/test/libsolidity/semanticTests/array/function_array_cross_calls.sol +++ b/test/libsolidity/semanticTests/array/function_array_cross_calls.sol @@ -46,5 +46,5 @@ contract C { // ---- // test() -> 5, 6, 7 // gas irOptimized: 345955 -// gas legacy: 500424 +// gas legacy: 508437 // gas legacyOptimized: 309013 diff --git a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol index a9f65676b..691f85e3e 100644 --- a/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol +++ b/test/libsolidity/semanticTests/constructor/arrays_in_constructors.sol @@ -27,5 +27,5 @@ contract Creator { // ---- // f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8 // gas irOptimized: 474718 -// gas legacy: 570900 +// gas legacy: 578926 // gas legacyOptimized: 436724 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index e12576a4e..aa6693033 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -27,5 +27,5 @@ contract Creator { // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" // gas irOptimized: 330976 -// gas legacy: 414850 +// gas legacy: 422873 // gas legacyOptimized: 292281 diff --git a/test/libsolidity/semanticTests/functionTypes/store_function.sol b/test/libsolidity/semanticTests/functionTypes/store_function.sol index e48ff21e1..937ce9519 100644 --- a/test/libsolidity/semanticTests/functionTypes/store_function.sol +++ b/test/libsolidity/semanticTests/functionTypes/store_function.sol @@ -29,5 +29,5 @@ contract C { // ---- // t() -> 9 // gas irOptimized: 103953 -// gas legacy: 161097 +// gas legacy: 162897 // gas legacyOptimized: 112116 diff --git a/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol b/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol index 3f988d9bf..3e370307f 100644 --- a/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol +++ b/test/libsolidity/semanticTests/inheritance/address_overload_resolution.sol @@ -24,7 +24,7 @@ contract D { // ---- // f() -> 1 // gas irOptimized: 86504 -// gas legacy: 114412 +// gas legacy: 116212 // g() -> 5 // gas irOptimized: 86600 -// gas legacy: 114872 +// gas legacy: 116672 diff --git a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol index 44688ada4..d4670efed 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_calldata_interface.sol @@ -26,4 +26,4 @@ contract B { // ---- // g() -> 42 // gas irOptimized: 90635 -// gas legacy: 117797 +// gas legacy: 126809 diff --git a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol index 3b5ea5ee8..34820dbcb 100644 --- a/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol +++ b/test/libsolidity/semanticTests/inheritance/inherited_function_calldata_memory_interface.sol @@ -26,5 +26,5 @@ contract B { // ---- // g() -> 42 // gas irOptimized: 119658 -// gas legacy: 180597 +// gas legacy: 187809 // gas legacyOptimized: 117351 diff --git a/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol b/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol index 378e0a5b6..94a773161 100644 --- a/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol +++ b/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol @@ -65,11 +65,11 @@ contract Test { } } // ==== -// compileViaYul: also // compileToEwasm: also +// compileViaYul: also // ---- // load() -> 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 -// gas irOptimized: 111580 +// gas irOptimized: 111532 // gas legacy: 113999 // gas legacyOptimized: 106281 // store() -> 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 From 01d45ccc79f95810af133651452eb99f50b2b212 Mon Sep 17 00:00:00 2001 From: franzihei Date: Tue, 4 May 2021 15:04:43 +0100 Subject: [PATCH 034/235] [Docs] Update information on contributing to translations --- docs/contributing.rst | 3 ++- docs/index.rst | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 0c084c264..a98adf286 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -388,7 +388,8 @@ local slang and references, making your language as clear to all readers as poss .. note:: While the official Solidity documentation is written in English, there are community contributed :ref:`translations` - in other languages available. + in other languages available. Please refer to the `translation guide `_ + for information on how to contribute to the community translations. Title Case for Headings ----------------------- diff --git a/docs/index.rst b/docs/index.rst index 1674248c1..c167c134c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -91,6 +91,12 @@ Community volunteers help translate this documentation into several languages. They have varying degrees of completeness and up-to-dateness. The English version stands as a reference. +.. note:: + + We recently set up a new GitHub organization and translation workflow to help streamline the + community efforts. Please refer to the `translation guide `_ + for information on how to contribute to the community translations moving forward. + * `French `_ (in progress) * `Italian `_ (in progress) * `Japanese `_ From e404b6e7a63201e01d8f79863c35e8393ff90ed4 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Tue, 27 Apr 2021 16:53:04 +0200 Subject: [PATCH 035/235] Refactoring yul source locations. --- libsolidity/analysis/ControlFlowBuilder.cpp | 4 +- libsolidity/analysis/ReferencesResolver.cpp | 14 ++-- libsolidity/analysis/TypeChecker.cpp | 40 +++++----- libsolidity/analysis/ViewPureChecker.cpp | 2 +- libsolidity/ast/ASTJsonConverter.cpp | 2 +- libsolidity/ast/ASTJsonImporter.cpp | 2 - libsolidity/codegen/CompilerContext.cpp | 4 +- .../codegen/ir/IRGeneratorForStatements.cpp | 4 +- libsolidity/interface/CompilerStack.cpp | 2 +- libsolidity/parsing/Parser.cpp | 4 +- libyul/AST.h | 60 +++++++++----- libyul/ASTForward.h | 1 + libyul/AsmAnalysis.cpp | 66 ++++++++-------- libyul/AsmJsonConverter.cpp | 32 ++++---- libyul/AsmJsonImporter.cpp | 5 +- libyul/AsmParser.cpp | 52 ++++++++----- libyul/AsmParser.h | 2 +- libyul/AssemblyStack.cpp | 1 - libyul/Dialect.cpp | 8 +- libyul/ObjectParser.cpp | 2 +- libyul/ScopeFiller.cpp | 6 +- libyul/backends/evm/AbstractAssembly.h | 2 + libyul/backends/evm/ConstantOptimiser.cpp | 12 +-- libyul/backends/evm/ConstantOptimiser.h | 6 +- libyul/backends/evm/EVMAssembly.cpp | 2 +- libyul/backends/evm/EVMCodeTransform.cpp | 62 ++++++++------- libyul/backends/evm/EVMCodeTransform.h | 1 + libyul/backends/evm/EVMDialect.cpp | 4 +- libyul/backends/wasm/EVMToEwasmTranslator.cpp | 2 +- libyul/backends/wasm/WordSizeTransform.cpp | 78 +++++++++---------- libyul/backends/wasm/WordSizeTransform.h | 2 +- libyul/optimiser/ASTCopier.cpp | 24 +++--- .../CommonSubexpressionEliminator.cpp | 12 +-- libyul/optimiser/ConditionalSimplifier.cpp | 10 +-- libyul/optimiser/ControlFlowSimplifier.cpp | 26 +++---- libyul/optimiser/ExpressionSimplifier.cpp | 2 +- libyul/optimiser/ExpressionSplitter.cpp | 8 +- libyul/optimiser/ForLoopConditionIntoBody.cpp | 12 +-- .../optimiser/ForLoopConditionOutOfBody.cpp | 6 +- libyul/optimiser/FullInliner.cpp | 10 +-- libyul/optimiser/FunctionGrouper.cpp | 2 +- libyul/optimiser/FunctionHoister.cpp | 2 +- libyul/optimiser/FunctionSpecializer.cpp | 2 +- libyul/optimiser/KnowledgeBase.cpp | 2 +- libyul/optimiser/LoadResolver.cpp | 6 +- libyul/optimiser/MainFunction.cpp | 2 +- libyul/optimiser/ReasoningBasedSimplifier.cpp | 4 +- libyul/optimiser/SSAReverser.cpp | 10 +-- libyul/optimiser/SSATransform.cpp | 31 ++++---- libyul/optimiser/SimplificationRules.cpp | 11 ++- libyul/optimiser/SimplificationRules.h | 2 +- libyul/optimiser/StackToMemoryMover.cpp | 39 +++++----- libyul/optimiser/UnusedFunctionsCommon.cpp | 18 ++--- libyul/optimiser/UnusedPruner.cpp | 14 ++-- libyul/optimiser/VarDeclInitializer.cpp | 3 +- test/libyul/Common.cpp | 1 - test/libyul/EwasmTranslationTest.cpp | 1 - test/libyul/Parser.cpp | 2 +- test/libyul/SyntaxTest.cpp | 1 - test/libyul/YulInterpreterTest.cpp | 1 - test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp | 2 - test/tools/yulrun.cpp | 2 - 62 files changed, 394 insertions(+), 358 deletions(-) diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp index 8946e6ed3..5c179fbf5 100644 --- a/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/libsolidity/analysis/ControlFlowBuilder.cpp @@ -482,7 +482,7 @@ void ControlFlowBuilder::operator()(yul::Identifier const& _identifier) m_currentNode->variableOccurrences.emplace_back( *declaration, VariableOccurrence::Kind::Access, - _identifier.location + _identifier.debugData->location ); } } @@ -498,7 +498,7 @@ void ControlFlowBuilder::operator()(yul::Assignment const& _assignment) m_currentNode->variableOccurrences.emplace_back( *declaration, VariableOccurrence::Kind::Assignment, - variable.location + variable.debugData->location ); } diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index aaa9c6ef9..abaeada03 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -201,9 +201,9 @@ bool ReferencesResolver::visit(Return const& _return) void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) { - validateYulIdentifierName(_function.name, _function.location); + validateYulIdentifierName(_function.name, _function.debugData->location); for (yul::TypedName const& varName: _function.parameters + _function.returnVariables) - validateYulIdentifierName(varName.name, varName.location); + validateYulIdentifierName(varName.name, varName.debugData->location); bool wasInsideFunction = m_yulInsideFunction; m_yulInsideFunction = true; @@ -238,7 +238,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) { m_errorReporter.declarationError( 4718_error, - _identifier.location, + _identifier.debugData->location, "Multiple matching identifiers. Resolving overloaded identifiers is not supported." ); return; @@ -251,7 +251,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) ) m_errorReporter.declarationError( 9467_error, - _identifier.location, + _identifier.debugData->location, "Identifier not found. Use \".slot\" and \".offset\" to access storage variables." ); return; @@ -261,7 +261,7 @@ void ReferencesResolver::operator()(yul::Identifier const& _identifier) { m_errorReporter.declarationError( 6578_error, - _identifier.location, + _identifier.debugData->location, "Cannot access local Solidity variables from inside an inline assembly function." ); return; @@ -275,7 +275,7 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) { for (auto const& identifier: _varDecl.variables) { - validateYulIdentifierName(identifier.name, identifier.location); + validateYulIdentifierName(identifier.name, identifier.debugData->location); if ( @@ -289,7 +289,7 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl) if (!ssl.infos.empty()) m_errorReporter.declarationError( 3859_error, - identifier.location, + identifier.debugData->location, ssl, "This declaration shadows a declaration outside the inline assembly block." ); diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index e0c288aeb..e57822ee1 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -743,7 +743,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(var->type(), "Expected variable type!"); if (var->immutable()) { - m_errorReporter.typeError(3773_error, _identifier.location, "Assembly access to immutable variables is not supported."); + m_errorReporter.typeError(3773_error, _identifier.debugData->location, "Assembly access to immutable variables is not supported."); return false; } if (var->isConstant()) @@ -752,7 +752,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { m_errorReporter.typeError( 3558_error, - _identifier.location, + _identifier.debugData->location, "Constant variable is circular." ); return false; @@ -762,24 +762,24 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (var && !var->value()) { - m_errorReporter.typeError(3224_error, _identifier.location, "Constant has no value."); + m_errorReporter.typeError(3224_error, _identifier.debugData->location, "Constant has no value."); return false; } else if (_context == yul::IdentifierContext::LValue) { - m_errorReporter.typeError(6252_error, _identifier.location, "Constant variables cannot be assigned to."); + m_errorReporter.typeError(6252_error, _identifier.debugData->location, "Constant variables cannot be assigned to."); return false; } else if (!identifierInfo.suffix.empty()) { - m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes .offset and .slot can only be used on non-constant storage variables."); + m_errorReporter.typeError(6617_error, _identifier.debugData->location, "The suffixes .offset and .slot can only be used on non-constant storage variables."); return false; } else if (var && var->value() && !var->value()->annotation().type && !dynamic_cast(var->value().get())) { m_errorReporter.typeError( 2249_error, - _identifier.location, + _identifier.debugData->location, "Constant variables with non-literal values cannot be forward referenced from inline assembly." ); return false; @@ -789,7 +789,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) type(*var->value())->category() != Type::Category::RationalNumber )) { - m_errorReporter.typeError(7615_error, _identifier.location, "Only direct number constants and references to such constants are supported by inline assembly."); + m_errorReporter.typeError(7615_error, _identifier.debugData->location, "Only direct number constants and references to such constants are supported by inline assembly."); return false; } } @@ -804,19 +804,19 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { if (suffix != "slot" && suffix != "offset") { - m_errorReporter.typeError(4656_error, _identifier.location, "State variables only support \".slot\" and \".offset\"."); + m_errorReporter.typeError(4656_error, _identifier.debugData->location, "State variables only support \".slot\" and \".offset\"."); return false; } else if (_context == yul::IdentifierContext::LValue) { if (var->isStateVariable()) { - m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\"."); + m_errorReporter.typeError(4713_error, _identifier.debugData->location, "State variables cannot be assigned to - you have to use \"sstore()\"."); return false; } else if (suffix != "slot") { - m_errorReporter.typeError(9739_error, _identifier.location, "Only .slot can be assigned to."); + m_errorReporter.typeError(9739_error, _identifier.debugData->location, "Only .slot can be assigned to."); return false; } } @@ -828,13 +828,13 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { if (suffix != "offset" && suffix != "length") { - m_errorReporter.typeError(1536_error, _identifier.location, "Calldata variables only support \".offset\" and \".length\"."); + m_errorReporter.typeError(1536_error, _identifier.debugData->location, "Calldata variables only support \".offset\" and \".length\"."); return false; } } else { - m_errorReporter.typeError(3622_error, _identifier.location, "The suffix \"." + suffix + "\" is not supported by this variable or type."); + m_errorReporter.typeError(3622_error, _identifier.debugData->location, "The suffix \"." + suffix + "\" is not supported by this variable or type."); return false; } } @@ -842,14 +842,14 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { m_errorReporter.typeError( 1408_error, - _identifier.location, + _identifier.debugData->location, "Only local variables are supported. To access storage variables, use the \".slot\" and \".offset\" suffixes." ); return false; } else if (var->type()->dataStoredIn(DataLocation::Storage)) { - m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the \".slot\" or \".offset\" suffix to access storage reference variables."); + m_errorReporter.typeError(9068_error, _identifier.debugData->location, "You have to use the \".slot\" or \".offset\" suffix to access storage reference variables."); return false; } else if (var->type()->sizeOnStack() != 1) @@ -858,18 +858,18 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) auto const* arrayType = dynamic_cast(var->type()); arrayType && arrayType->isDynamicallySized() && arrayType->dataStoredIn(DataLocation::CallData) ) - m_errorReporter.typeError(1397_error, _identifier.location, "Call data elements cannot be accessed directly. Use \".offset\" and \".length\" to access the calldata offset and length of this array and then use \"calldatacopy\"."); + m_errorReporter.typeError(1397_error, _identifier.debugData->location, "Call data elements cannot be accessed directly. Use \".offset\" and \".length\" to access the calldata offset and length of this array and then use \"calldatacopy\"."); else { solAssert(!var->type()->dataStoredIn(DataLocation::CallData), ""); - m_errorReporter.typeError(9857_error, _identifier.location, "Only types that use one stack slot are supported."); + m_errorReporter.typeError(9857_error, _identifier.debugData->location, "Only types that use one stack slot are supported."); } return false; } } else if (!identifierInfo.suffix.empty()) { - m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables."); + m_errorReporter.typeError(7944_error, _identifier.debugData->location, "The suffixes \".offset\", \".slot\" and \".length\" can only be used with variables."); return false; } else if (_context == yul::IdentifierContext::LValue) @@ -877,7 +877,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) if (dynamic_cast(declaration)) return false; - m_errorReporter.typeError(1990_error, _identifier.location, "Only local variables can be assigned to in inline assembly."); + m_errorReporter.typeError(1990_error, _identifier.debugData->location, "Only local variables can be assigned to in inline assembly."); return false; } @@ -886,7 +886,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); if (dynamic_cast(declaration)) { - m_errorReporter.declarationError(2025_error, _identifier.location, "Access to functions is not allowed in inline assembly."); + m_errorReporter.declarationError(2025_error, _identifier.debugData->location, "Access to functions is not allowed in inline assembly."); return false; } else if (dynamic_cast(declaration)) @@ -896,7 +896,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { if (!contract->isLibrary()) { - m_errorReporter.typeError(4977_error, _identifier.location, "Expected a library."); + m_errorReporter.typeError(4977_error, _identifier.debugData->location, "Expected a library."); return false; } } diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index daf0f8625..87831972d 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -69,7 +69,7 @@ public: if (yul::EVMDialect const* dialect = dynamic_cast(&m_dialect)) if (yul::BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name)) if (fun->instruction) - checkInstruction(_funCall.location, *fun->instruction); + checkInstruction(_funCall.debugData->location, *fun->instruction); for (auto const& arg: _funCall.arguments) std::visit(*this, arg); diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 70ec98992..40cef6b8e 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -175,7 +175,7 @@ void ASTJsonConverter::appendExpressionAttributes( Json::Value ASTJsonConverter::inlineAssemblyIdentifierToJson(pair _info) const { Json::Value tuple(Json::objectValue); - tuple["src"] = sourceLocationToString(_info.first->location); + tuple["src"] = sourceLocationToString(_info.first->debugData->location); tuple["declaration"] = idOrNull(_info.second.declaration); tuple["isSlot"] = Json::Value(_info.second.suffix == "slot"); tuple["isOffset"] = Json::Value(_info.second.suffix == "offset"); diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 0fa88ae3b..916dbac14 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -24,12 +24,10 @@ #include #include -#include #include #include #include -#include #include #include #include diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index cddeea472..2897d533c 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -28,11 +28,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include @@ -426,7 +426,7 @@ void CompilerContext::appendInlineAssembly( if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( StackTooDeepError() << - errinfo_sourceLocation(_identifier.location) << + errinfo_sourceLocation(_identifier.debugData->location) << util::errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.") ); if (_context == yul::IdentifierContext::RValue) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index fc33065ef..2aaf249d8 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -192,9 +192,9 @@ private: solAssert(false, ""); if (isdigit(value.front())) - return yul::Literal{_identifier.location, yul::LiteralKind::Number, yul::YulString{value}, {}}; + return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::YulString{value}, {}}; else - return yul::Identifier{_identifier.location, yul::YulString{value}}; + return yul::Identifier{_identifier.debugData, yul::YulString{value}}; } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index f29f5f137..12f3ceac7 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -61,8 +61,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index cad62e173..560d374c9 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -24,8 +24,8 @@ #include #include -#include #include +#include #include #include #include @@ -1299,7 +1299,7 @@ ASTPointer Parser::parseInlineAssembly(ASTPointer con if (block == nullptr) BOOST_THROW_EXCEPTION(FatalError()); - location.end = block->location.end; + location.end = block->debugData->location.end; return make_shared(nextID(), location, _docString, dialect, block); } diff --git a/libyul/AST.h b/libyul/AST.h index 09d39832f..54d44f6ae 100644 --- a/libyul/AST.h +++ b/libyul/AST.h @@ -35,56 +35,80 @@ namespace solidity::yul using Type = YulString; -struct TypedName { langutil::SourceLocation location; YulString name; Type type; }; +struct DebugData +{ + explicit DebugData(langutil::SourceLocation _location): location(std::move(_location)) {} + langutil::SourceLocation location; + static std::shared_ptr create(langutil::SourceLocation _location = {}) + { + return std::make_shared(_location); + } +}; + +struct TypedName { std::shared_ptr debugData; YulString name; Type type; }; using TypedNameList = std::vector; /// Literal number or string (up to 32 bytes) enum class LiteralKind { Number, Boolean, String }; -struct Literal { langutil::SourceLocation location; LiteralKind kind; YulString value; Type type; }; +struct Literal { std::shared_ptr debugData; LiteralKind kind; YulString value; Type type; }; /// External / internal identifier or label reference -struct Identifier { langutil::SourceLocation location; YulString name; }; +struct Identifier { std::shared_ptr debugData; YulString name; }; /// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand /// side and requires x to occupy exactly one stack slot. /// /// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy /// a single stack slot and expects a single expression on the right hand returning /// the same amount of items as the number of variables. -struct Assignment { langutil::SourceLocation location; std::vector variableNames; std::unique_ptr value; }; -struct FunctionCall { langutil::SourceLocation location; Identifier functionName; std::vector arguments; }; +struct Assignment { std::shared_ptr debugData; std::vector variableNames; std::unique_ptr value; }; +struct FunctionCall { std::shared_ptr debugData; Identifier functionName; std::vector arguments; }; /// Statement that contains only a single expression -struct ExpressionStatement { langutil::SourceLocation location; Expression expression; }; +struct ExpressionStatement { std::shared_ptr debugData; Expression expression; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted -struct VariableDeclaration { langutil::SourceLocation location; TypedNameList variables; std::unique_ptr value; }; +struct VariableDeclaration { std::shared_ptr debugData; TypedNameList variables; std::unique_ptr value; }; /// Block that creates a scope (frees declared stack variables) -struct Block { langutil::SourceLocation location; std::vector statements; }; +struct Block { std::shared_ptr debugData; std::vector statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") -struct FunctionDefinition { langutil::SourceLocation location; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; +struct FunctionDefinition { std::shared_ptr debugData; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. -struct If { langutil::SourceLocation location; std::unique_ptr condition; Block body; }; +struct If { std::shared_ptr debugData; std::unique_ptr condition; Block body; }; /// Switch case or default case -struct Case { langutil::SourceLocation location; std::unique_ptr value; Block body; }; +struct Case { std::shared_ptr debugData; std::unique_ptr value; Block body; }; /// Switch statement -struct Switch { langutil::SourceLocation location; std::unique_ptr expression; std::vector cases; }; -struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr condition; Block post; Block body; }; +struct Switch { std::shared_ptr debugData; std::unique_ptr expression; std::vector cases; }; +struct ForLoop { std::shared_ptr debugData; Block pre; std::unique_ptr condition; Block post; Block body; }; /// Break statement (valid within for loop) -struct Break { langutil::SourceLocation location; }; +struct Break { std::shared_ptr debugData; }; /// Continue statement (valid within for loop) -struct Continue { langutil::SourceLocation location; }; +struct Continue { std::shared_ptr debugData; }; /// Leave statement (valid within function) -struct Leave { langutil::SourceLocation location; }; +struct Leave { std::shared_ptr debugData; }; struct LocationExtractor { template langutil::SourceLocation operator()(T const& _node) const { - return _node.location; + return _node.debugData ? _node.debugData->location : langutil::SourceLocation{}; } }; -/// Extracts the source location from an inline assembly node. +/// Extracts the source location from a Yul node. template inline langutil::SourceLocation locationOf(T const& _node) { return std::visit(LocationExtractor(), _node); } +struct DebugDataExtractor +{ + template std::shared_ptr const& operator()(T const& _node) const + { + return _node.debugData; + } +}; + +/// Extracts the debug data from a Yul node. +template inline std::shared_ptr const& debugDataOf(T const& _node) +{ + return std::visit(DebugDataExtractor(), _node); +} + } diff --git a/libyul/ASTForward.h b/libyul/ASTForward.h index a4dc5be3f..0e61c6ca1 100644 --- a/libyul/ASTForward.h +++ b/libyul/ASTForward.h @@ -28,6 +28,7 @@ namespace solidity::yul { +struct DebugData; enum class LiteralKind; struct Literal; struct Label; diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index d3b5cd890..a6b0e729c 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -98,22 +98,22 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, vector AsmAnalyzer::operator()(Literal const& _literal) { - expectValidType(_literal.type, _literal.location); + expectValidType(_literal.type, _literal.debugData->location); if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32) m_errorReporter.typeError( 3069_error, - _literal.location, + _literal.debugData->location, "String literal too long (" + to_string(_literal.value.str().size()) + " > 32)" ); else if (_literal.kind == LiteralKind::Number && bigint(_literal.value.str()) > u256(-1)) - m_errorReporter.typeError(6708_error, _literal.location, "Number literal too large (> 256 bits)"); + m_errorReporter.typeError(6708_error, _literal.debugData->location, "Number literal too large (> 256 bits)"); else if (_literal.kind == LiteralKind::Boolean) yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); if (!m_dialect.validTypeForLiteral(_literal.kind, _literal.value, _literal.type)) m_errorReporter.typeError( 5170_error, - _literal.location, + _literal.debugData->location, "Invalid type \"" + _literal.type.str() + "\" for literal \"" + _literal.value.str() + "\"." ); @@ -133,7 +133,7 @@ vector AsmAnalyzer::operator()(Identifier const& _identifier) if (!m_activeVariables.count(&_var)) m_errorReporter.declarationError( 4990_error, - _identifier.location, + _identifier.debugData->location, "Variable " + _identifier.name.str() + " used before it was declared." ); type = _var.type; @@ -142,7 +142,7 @@ vector AsmAnalyzer::operator()(Identifier const& _identifier) { m_errorReporter.typeError( 6041_error, - _identifier.location, + _identifier.debugData->location, "Function " + _identifier.name.str() + " used without being called." ); } @@ -160,7 +160,7 @@ vector AsmAnalyzer::operator()(Identifier const& _identifier) // Only add an error message if the callback did not do it. m_errorReporter.declarationError( 8198_error, - _identifier.location, + _identifier.debugData->location, "Identifier \"" + _identifier.name.str() + "\" not found." ); @@ -176,7 +176,7 @@ void AsmAnalyzer::operator()(ExpressionStatement const& _statement) if (watcher.ok() && !types.empty()) m_errorReporter.typeError( 3083_error, - _statement.location, + _statement.debugData->location, "Top-level expressions are not supposed to return values (this expression returns " + to_string(types.size()) + " value" + @@ -196,7 +196,7 @@ void AsmAnalyzer::operator()(Assignment const& _assignment) if (!variables.insert(_variableName.name).second) m_errorReporter.declarationError( 9005_error, - _assignment.location, + _assignment.debugData->location, "Variable " + _variableName.name.str() + " occurs multiple times on the left-hand side of the assignment." @@ -207,7 +207,7 @@ void AsmAnalyzer::operator()(Assignment const& _assignment) if (types.size() != numVariables) m_errorReporter.declarationError( 8678_error, - _assignment.location, + _assignment.debugData->location, "Variable count for assignment to \"" + joinHumanReadable(applyMap(_assignment.variableNames, [](auto const& _identifier){ return _identifier.name.str(); })) + "\" does not match number of values (" + @@ -229,14 +229,14 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) for (auto const& variable: _varDecl.variables) // Call the resolver for variable declarations to allow it to raise errors on shadowing. m_resolver( - yul::Identifier{variable.location, variable.name}, + yul::Identifier{variable.debugData, variable.name}, yul::IdentifierContext::VariableDeclaration, m_currentScope->insideFunction() ); for (auto const& variable: _varDecl.variables) { - expectValidIdentifier(variable.name, variable.location); - expectValidType(variable.type, variable.location); + expectValidIdentifier(variable.name, variable.debugData->location); + expectValidType(variable.type, variable.debugData->location); } if (_varDecl.value) @@ -245,7 +245,7 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) if (types.size() != numVariables) m_errorReporter.declarationError( 3812_error, - _varDecl.location, + _varDecl.debugData->location, "Variable count mismatch for declaration of \"" + joinHumanReadable(applyMap(_varDecl.variables, [](auto const& _identifier){ return _identifier.name.str(); })) + + "\": " + @@ -264,7 +264,7 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) if (variable.type != givenType) m_errorReporter.typeError( 3947_error, - variable.location, + variable.debugData->location, "Assigning value of type \"" + givenType.str() + "\" to variable of type \"" + variable.type.str() + "\"." ); } @@ -279,14 +279,14 @@ void AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) void AsmAnalyzer::operator()(FunctionDefinition const& _funDef) { yulAssert(!_funDef.name.empty(), ""); - expectValidIdentifier(_funDef.name, _funDef.location); + expectValidIdentifier(_funDef.name, _funDef.debugData->location); Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); yulAssert(virtualBlock, ""); Scope& varScope = scope(virtualBlock); for (auto const& var: _funDef.parameters + _funDef.returnVariables) { - expectValidIdentifier(var.name, var.location); - expectValidType(var.type, var.location); + expectValidIdentifier(var.name, var.debugData->location); + expectValidType(var.type, var.debugData->location); m_activeVariables.insert(&std::get(varScope.identifiers.at(var.name))); } @@ -315,7 +315,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) { m_errorReporter.typeError( 4202_error, - _funCall.functionName.location, + _funCall.functionName.debugData->location, "Attempt to call variable instead of function." ); }, @@ -329,7 +329,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (!validateInstructions(_funCall)) m_errorReporter.declarationError( 4619_error, - _funCall.functionName.location, + _funCall.functionName.debugData->location, "Function \"" + _funCall.functionName.name.str() + "\" not found." ); yulAssert(!watcher.ok(), "Expected a reported error."); @@ -338,7 +338,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (parameterTypes && _funCall.arguments.size() != parameterTypes->size()) m_errorReporter.typeError( 7000_error, - _funCall.functionName.location, + _funCall.functionName.debugData->location, "Function \"" + _funCall.functionName.name.str() + "\" expects " + to_string(parameterTypes->size()) + " arguments but got " + @@ -358,13 +358,13 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (!holds_alternative(arg)) m_errorReporter.typeError( 9114_error, - _funCall.functionName.location, + _funCall.functionName.debugData->location, "Function expects direct literals as arguments." ); else if (*literalArgumentKind != get(arg).kind) m_errorReporter.typeError( 5859_error, - get(arg).location, + get(arg).debugData->location, "Function expects " + to_string(*literalArgumentKind) + " literal." ); else if (*literalArgumentKind == LiteralKind::String) @@ -375,7 +375,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (!m_dataNames.count(get(arg).value)) m_errorReporter.typeError( 3517_error, - get(arg).location, + get(arg).debugData->location, "Unknown data object \"" + std::get(arg).value.str() + "\"." ); } @@ -384,7 +384,7 @@ vector AsmAnalyzer::operator()(FunctionCall const& _funCall) if (get(arg).value.empty()) m_errorReporter.typeError( 1844_error, - get(arg).location, + get(arg).debugData->location, "The \"verbatim_*\" builtins cannot be used with empty bytecode." ); } @@ -427,7 +427,7 @@ void AsmAnalyzer::operator()(Switch const& _switch) if (_switch.cases.size() == 1 && !_switch.cases[0].value) m_errorReporter.warning( 9592_error, - _switch.location, + _switch.debugData->location, "\"switch\" statement with only a default case." ); @@ -440,7 +440,7 @@ void AsmAnalyzer::operator()(Switch const& _switch) { auto watcher = m_errorReporter.errorWatcher(); - expectType(valueType, _case.value->type, _case.value->location); + expectType(valueType, _case.value->type, _case.value->debugData->location); // We cannot use "expectExpression" here because *_case.value is not an // Expression and would be converted to an Expression otherwise. @@ -450,7 +450,7 @@ void AsmAnalyzer::operator()(Switch const& _switch) if (watcher.ok() && !cases.insert(valueOfLiteral(*_case.value)).second) m_errorReporter.declarationError( 6792_error, - _case.location, + _case.debugData->location, "Duplicate case \"" + valueOfLiteral(*_case.value).str() + "\" defined." @@ -542,11 +542,11 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueT if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name)) { if (!holds_alternative(*var)) - m_errorReporter.typeError(2657_error, _variable.location, "Assignment requires variable."); + m_errorReporter.typeError(2657_error, _variable.debugData->location, "Assignment requires variable."); else if (!m_activeVariables.count(&std::get(*var))) m_errorReporter.declarationError( 1133_error, - _variable.location, + _variable.debugData->location, "Variable " + _variable.name.str() + " used before it was declared." ); else @@ -565,11 +565,11 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueT if (!found && watcher.ok()) // Only add message if the callback did not. - m_errorReporter.declarationError(4634_error, _variable.location, "Variable not found or variable not lvalue."); + m_errorReporter.declarationError(4634_error, _variable.debugData->location, "Variable not found or variable not lvalue."); if (variableType && *variableType != _valueType) m_errorReporter.typeError( 9547_error, - _variable.location, + _variable.debugData->location, "Assigning a value of type \"" + _valueType.str() + "\" to a variable of type \"" + @@ -713,5 +713,5 @@ bool AsmAnalyzer::validateInstructions(evmasm::Instruction _instr, SourceLocatio bool AsmAnalyzer::validateInstructions(FunctionCall const& _functionCall) { - return validateInstructions(_functionCall.functionName.name.str(), _functionCall.functionName.location); + return validateInstructions(_functionCall.functionName.name.str(), _functionCall.functionName.debugData->location); } diff --git a/libyul/AsmJsonConverter.cpp b/libyul/AsmJsonConverter.cpp index aece124aa..582d95a82 100644 --- a/libyul/AsmJsonConverter.cpp +++ b/libyul/AsmJsonConverter.cpp @@ -32,7 +32,7 @@ namespace solidity::yul Json::Value AsmJsonConverter::operator()(Block const& _node) const { - Json::Value ret = createAstNode(_node.location, "YulBlock"); + Json::Value ret = createAstNode(_node.debugData->location, "YulBlock"); ret["statements"] = vectorOfVariantsToJson(_node.statements); return ret; } @@ -40,7 +40,7 @@ Json::Value AsmJsonConverter::operator()(Block const& _node) const Json::Value AsmJsonConverter::operator()(TypedName const& _node) const { yulAssert(!_node.name.empty(), "Invalid variable name."); - Json::Value ret = createAstNode(_node.location, "YulTypedName"); + Json::Value ret = createAstNode(_node.debugData->location, "YulTypedName"); ret["name"] = _node.name.str(); ret["type"] = _node.type.str(); return ret; @@ -48,7 +48,7 @@ Json::Value AsmJsonConverter::operator()(TypedName const& _node) const Json::Value AsmJsonConverter::operator()(Literal const& _node) const { - Json::Value ret = createAstNode(_node.location, "YulLiteral"); + Json::Value ret = createAstNode(_node.debugData->location, "YulLiteral"); switch (_node.kind) { case LiteralKind::Number: @@ -73,7 +73,7 @@ Json::Value AsmJsonConverter::operator()(Literal const& _node) const Json::Value AsmJsonConverter::operator()(Identifier const& _node) const { yulAssert(!_node.name.empty(), "Invalid identifier"); - Json::Value ret = createAstNode(_node.location, "YulIdentifier"); + Json::Value ret = createAstNode(_node.debugData->location, "YulIdentifier"); ret["name"] = _node.name.str(); return ret; } @@ -81,7 +81,7 @@ Json::Value AsmJsonConverter::operator()(Identifier const& _node) const Json::Value AsmJsonConverter::operator()(Assignment const& _node) const { yulAssert(_node.variableNames.size() >= 1, "Invalid assignment syntax"); - Json::Value ret = createAstNode(_node.location, "YulAssignment"); + Json::Value ret = createAstNode(_node.debugData->location, "YulAssignment"); for (auto const& var: _node.variableNames) ret["variableNames"].append((*this)(var)); ret["value"] = _node.value ? std::visit(*this, *_node.value) : Json::nullValue; @@ -90,7 +90,7 @@ Json::Value AsmJsonConverter::operator()(Assignment const& _node) const Json::Value AsmJsonConverter::operator()(FunctionCall const& _node) const { - Json::Value ret = createAstNode(_node.location, "YulFunctionCall"); + Json::Value ret = createAstNode(_node.debugData->location, "YulFunctionCall"); ret["functionName"] = (*this)(_node.functionName); ret["arguments"] = vectorOfVariantsToJson(_node.arguments); return ret; @@ -98,14 +98,14 @@ Json::Value AsmJsonConverter::operator()(FunctionCall const& _node) const Json::Value AsmJsonConverter::operator()(ExpressionStatement const& _node) const { - Json::Value ret = createAstNode(_node.location, "YulExpressionStatement"); + Json::Value ret = createAstNode(_node.debugData->location, "YulExpressionStatement"); ret["expression"] = std::visit(*this, _node.expression); return ret; } Json::Value AsmJsonConverter::operator()(VariableDeclaration const& _node) const { - Json::Value ret = createAstNode(_node.location, "YulVariableDeclaration"); + Json::Value ret = createAstNode(_node.debugData->location, "YulVariableDeclaration"); for (auto const& var: _node.variables) ret["variables"].append((*this)(var)); @@ -117,7 +117,7 @@ Json::Value AsmJsonConverter::operator()(VariableDeclaration const& _node) const Json::Value AsmJsonConverter::operator()(FunctionDefinition const& _node) const { yulAssert(!_node.name.empty(), "Invalid function name."); - Json::Value ret = createAstNode(_node.location, "YulFunctionDefinition"); + Json::Value ret = createAstNode(_node.debugData->location, "YulFunctionDefinition"); ret["name"] = _node.name.str(); for (auto const& var: _node.parameters) ret["parameters"].append((*this)(var)); @@ -130,7 +130,7 @@ Json::Value AsmJsonConverter::operator()(FunctionDefinition const& _node) const Json::Value AsmJsonConverter::operator()(If const& _node) const { yulAssert(_node.condition, "Invalid if condition."); - Json::Value ret = createAstNode(_node.location, "YulIf"); + Json::Value ret = createAstNode(_node.debugData->location, "YulIf"); ret["condition"] = std::visit(*this, *_node.condition); ret["body"] = (*this)(_node.body); return ret; @@ -139,7 +139,7 @@ Json::Value AsmJsonConverter::operator()(If const& _node) const Json::Value AsmJsonConverter::operator()(Switch const& _node) const { yulAssert(_node.expression, "Invalid expression pointer."); - Json::Value ret = createAstNode(_node.location, "YulSwitch"); + Json::Value ret = createAstNode(_node.debugData->location, "YulSwitch"); ret["expression"] = std::visit(*this, *_node.expression); for (auto const& var: _node.cases) ret["cases"].append((*this)(var)); @@ -148,7 +148,7 @@ Json::Value AsmJsonConverter::operator()(Switch const& _node) const Json::Value AsmJsonConverter::operator()(Case const& _node) const { - Json::Value ret = createAstNode(_node.location, "YulCase"); + Json::Value ret = createAstNode(_node.debugData->location, "YulCase"); ret["value"] = _node.value ? (*this)(*_node.value) : "default"; ret["body"] = (*this)(_node.body); return ret; @@ -157,7 +157,7 @@ Json::Value AsmJsonConverter::operator()(Case const& _node) const Json::Value AsmJsonConverter::operator()(ForLoop const& _node) const { yulAssert(_node.condition, "Invalid for loop condition."); - Json::Value ret = createAstNode(_node.location, "YulForLoop"); + Json::Value ret = createAstNode(_node.debugData->location, "YulForLoop"); ret["pre"] = (*this)(_node.pre); ret["condition"] = std::visit(*this, *_node.condition); ret["post"] = (*this)(_node.post); @@ -167,17 +167,17 @@ Json::Value AsmJsonConverter::operator()(ForLoop const& _node) const Json::Value AsmJsonConverter::operator()(Break const& _node) const { - return createAstNode(_node.location, "YulBreak"); + return createAstNode(_node.debugData->location, "YulBreak"); } Json::Value AsmJsonConverter::operator()(Continue const& _node) const { - return createAstNode(_node.location, "YulContinue"); + return createAstNode(_node.debugData->location, "YulContinue"); } Json::Value AsmJsonConverter::operator()(Leave const& _node) const { - return createAstNode(_node.location, "YulLeave"); + return createAstNode(_node.debugData->location, "YulLeave"); } Json::Value AsmJsonConverter::createAstNode(langutil::SourceLocation const& _location, string _nodeType) const diff --git a/libyul/AsmJsonImporter.cpp b/libyul/AsmJsonImporter.cpp index bbcd1b9a7..410bdbed5 100644 --- a/libyul/AsmJsonImporter.cpp +++ b/libyul/AsmJsonImporter.cpp @@ -52,11 +52,12 @@ template T AsmJsonImporter::createAsmNode(Json::Value const& _node) { T r; - r.location = createSourceLocation(_node); + SourceLocation location = createSourceLocation(_node); yulAssert( - r.location.source && 0 <= r.location.start && r.location.start <= r.location.end, + location.source && 0 <= location.start && location.start <= location.end, "Invalid source location in Asm AST" ); + r.debugData = DebugData::create(location); return r; } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 408fadf7a..20a0e9f03 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -21,8 +21,8 @@ * Solidity inline assembly parser. */ -#include #include +#include #include #include #include @@ -40,6 +40,21 @@ using namespace solidity::util; using namespace solidity::langutil; using namespace solidity::yul; +namespace { + +[[nodiscard]] +shared_ptr updateLocationEndFrom( + shared_ptr const& _debugData, + langutil::SourceLocation const& _location +) +{ + SourceLocation updatedLocation = _debugData->location; + updatedLocation.end = _location.end; + return make_shared(updatedLocation); +} + +} + unique_ptr Parser::parse(std::shared_ptr const& _scanner, bool _reuseScanner) { m_recursionDepth = 0; @@ -70,7 +85,7 @@ Block Parser::parseBlock() expectToken(Token::LBrace); while (currentToken() != Token::RBrace) block.statements.emplace_back(parseStatement()); - block.location.end = currentLocation().end; + block.debugData = updateLocationEndFrom(block.debugData, currentLocation()); advance(); return block; } @@ -109,7 +124,7 @@ Statement Parser::parseStatement() fatalParserError(4904_error, "Case not allowed after default case."); if (_switch.cases.empty()) fatalParserError(2418_error, "Switch statement without any cases."); - _switch.location.end = _switch.cases.back().body.location.end; + _switch.debugData = updateLocationEndFrom(_switch.debugData, _switch.cases.back().body.debugData->location); return Statement{move(_switch)}; } case Token::For: @@ -150,13 +165,13 @@ Statement Parser::parseStatement() case Token::LParen: { Expression expr = parseCall(std::move(elementary)); - return ExpressionStatement{locationOf(expr), move(expr)}; + return ExpressionStatement{debugDataOf(expr), move(expr)}; } case Token::Comma: case Token::AssemblyAssign: { Assignment assignment; - assignment.location = locationOf(elementary); + assignment.debugData = debugDataOf(elementary); while (true) { @@ -191,7 +206,7 @@ Statement Parser::parseStatement() expectToken(Token::AssemblyAssign); assignment.value = make_unique(parseExpression()); - assignment.location.end = locationOf(*assignment.value).end; + assignment.debugData = updateLocationEndFrom(assignment.debugData, locationOf(*assignment.value)); return Statement{move(assignment)}; } @@ -221,7 +236,7 @@ Case Parser::parseCase() else yulAssert(false, "Case or default case expected."); _case.body = parseBlock(); - _case.location.end = _case.body.location.end; + _case.debugData = updateLocationEndFrom(_case.debugData, _case.body.debugData->location); return _case; } @@ -241,7 +256,7 @@ ForLoop Parser::parseForLoop() forLoop.post = parseBlock(); m_currentForLoopComponent = ForLoopComponent::ForLoopBody; forLoop.body = parseBlock(); - forLoop.location.end = forLoop.body.location.end; + forLoop.debugData = updateLocationEndFrom(forLoop.debugData, forLoop.body.debugData->location); m_currentForLoopComponent = outerForLoopComponent; @@ -261,7 +276,7 @@ Expression Parser::parseExpression() if (m_dialect.builtin(_identifier.name)) fatalParserError( 7104_error, - _identifier.location, + _identifier.debugData->location, "Builtin function \"" + _identifier.name.str() + "\" must be called." ); return move(_identifier); @@ -280,7 +295,7 @@ variant Parser::parseLiteralOrIdentifier() { case Token::Identifier: { - Identifier identifier{currentLocation(), YulString{currentLiteral()}}; + Identifier identifier{DebugData::create(currentLocation()), YulString{currentLiteral()}}; advance(); return identifier; } @@ -311,7 +326,7 @@ variant Parser::parseLiteralOrIdentifier() } Literal literal{ - currentLocation(), + DebugData::create(currentLocation()), kind, YulString{currentLiteral()}, kind == LiteralKind::Boolean ? m_dialect.boolType : m_dialect.defaultType @@ -320,7 +335,7 @@ variant Parser::parseLiteralOrIdentifier() if (currentToken() == Token::Colon) { expectToken(Token::Colon); - literal.location.end = currentLocation().end; + literal.debugData = updateLocationEndFrom(literal.debugData, currentLocation()); literal.type = expectAsmIdentifier(); } @@ -352,10 +367,11 @@ VariableDeclaration Parser::parseVariableDeclaration() { expectToken(Token::AssemblyAssign); varDecl.value = make_unique(parseExpression()); - varDecl.location.end = locationOf(*varDecl.value).end; + varDecl.debugData = updateLocationEndFrom(varDecl.debugData, locationOf(*varDecl.value)); } else - varDecl.location.end = varDecl.variables.back().location.end; + varDecl.debugData = updateLocationEndFrom(varDecl.debugData, varDecl.variables.back().debugData->location); + return varDecl; } @@ -400,7 +416,7 @@ FunctionDefinition Parser::parseFunctionDefinition() m_insideFunction = true; funDef.body = parseBlock(); m_insideFunction = preInsideFunction; - funDef.location.end = funDef.body.location.end; + funDef.debugData = updateLocationEndFrom(funDef.debugData, funDef.body.debugData->location); m_currentForLoopComponent = outerForLoopComponent; return funDef; @@ -415,7 +431,7 @@ FunctionCall Parser::parseCall(variant&& _initialOp) FunctionCall ret; ret.functionName = std::move(std::get(_initialOp)); - ret.location = ret.functionName.location; + ret.debugData = ret.functionName.debugData; expectToken(Token::LParen); if (currentToken() != Token::RParen) @@ -427,7 +443,7 @@ FunctionCall Parser::parseCall(variant&& _initialOp) ret.arguments.emplace_back(parseExpression()); } } - ret.location.end = currentLocation().end; + ret.debugData = updateLocationEndFrom(ret.debugData, currentLocation()); expectToken(Token::RParen); return ret; } @@ -440,7 +456,7 @@ TypedName Parser::parseTypedName() if (currentToken() == Token::Colon) { expectToken(Token::Colon); - typedName.location.end = currentLocation().end; + typedName.debugData = updateLocationEndFrom(typedName.debugData, currentLocation()); typedName.type = expectAsmIdentifier(); } else diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index d1294b111..896cb6d51 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -70,7 +70,7 @@ protected: template T createWithLocation() const { T r; - r.location = currentLocation(); + r.debugData = DebugData::create(currentLocation()); return r; } diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 0f52bdaa2..4eb655fb6 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -25,7 +25,6 @@ #include #include -#include #include #include #include diff --git a/libyul/Dialect.cpp b/libyul/Dialect.cpp index 847c9f1ac..9ed18a21e 100644 --- a/libyul/Dialect.cpp +++ b/libyul/Dialect.cpp @@ -29,17 +29,17 @@ using namespace solidity::langutil; Literal Dialect::zeroLiteralForType(solidity::yul::YulString _type) const { if (_type == boolType && _type != defaultType) - return {SourceLocation{}, LiteralKind::Boolean, "false"_yulstring, _type}; - return {SourceLocation{}, LiteralKind::Number, "0"_yulstring, _type}; + return {DebugData::create(), LiteralKind::Boolean, "false"_yulstring, _type}; + return {DebugData::create(), LiteralKind::Number, "0"_yulstring, _type}; } Literal Dialect::trueLiteral() const { if (boolType != defaultType) - return {SourceLocation{}, LiteralKind::Boolean, "true"_yulstring, boolType}; + return {DebugData::create(), LiteralKind::Boolean, "true"_yulstring, boolType}; else - return {SourceLocation{}, LiteralKind::Number, "1"_yulstring, defaultType}; + return {DebugData::create(), LiteralKind::Number, "1"_yulstring, defaultType}; } bool Dialect::validTypeForLiteral(LiteralKind _kind, YulString, YulString _type) const diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index 4385c2b3b..d2eb1feca 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -19,10 +19,10 @@ * Parser for Yul code and data object container. */ +#include #include #include -#include #include #include diff --git a/libyul/ScopeFiller.cpp b/libyul/ScopeFiller.cpp index 691bdc981..5c4599eda 100644 --- a/libyul/ScopeFiller.cpp +++ b/libyul/ScopeFiller.cpp @@ -53,7 +53,7 @@ bool ScopeFiller::operator()(ExpressionStatement const& _expr) bool ScopeFiller::operator()(VariableDeclaration const& _varDecl) { for (auto const& variable: _varDecl.variables) - if (!registerVariable(variable, _varDecl.location, *m_currentScope)) + if (!registerVariable(variable, _varDecl.debugData->location, *m_currentScope)) return false; return true; } @@ -68,7 +68,7 @@ bool ScopeFiller::operator()(FunctionDefinition const& _funDef) bool success = true; for (auto const& var: _funDef.parameters + _funDef.returnVariables) - if (!registerVariable(var, _funDef.location, varScope)) + if (!registerVariable(var, _funDef.debugData->location, varScope)) success = false; if (!(*this)(_funDef.body)) @@ -162,7 +162,7 @@ bool ScopeFiller::registerFunction(FunctionDefinition const& _funDef) //@TODO secondary location m_errorReporter.declarationError( 6052_error, - _funDef.location, + _funDef.debugData->location, "Function name " + _funDef.name.str() + " already taken in this scope." ); return false; diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index 1eb5a3fd8..18d36953a 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -23,6 +23,8 @@ #pragma once +#include + #include #include diff --git a/libyul/backends/evm/ConstantOptimiser.cpp b/libyul/backends/evm/ConstantOptimiser.cpp index d37ddade1..09d87da83 100644 --- a/libyul/backends/evm/ConstantOptimiser.cpp +++ b/libyul/backends/evm/ConstantOptimiser.cpp @@ -100,7 +100,7 @@ void ConstantOptimiser::visit(Expression& _e) if ( Expression const* repr = - RepresentationFinder(m_dialect, m_meter, locationOf(_e), m_cache) + RepresentationFinder(m_dialect, m_meter, debugDataOf(_e), m_cache) .tryFindRepresentation(valueOfLiteral(literal)) ) _e = ASTCopier{}.translate(*repr); @@ -180,7 +180,7 @@ Representation const& RepresentationFinder::findRepresentation(u256 const& _valu Representation RepresentationFinder::represent(u256 const& _value) const { Representation repr; - repr.expression = make_unique(Literal{m_location, LiteralKind::Number, YulString{formatNumber(_value)}, {}}); + repr.expression = make_unique(Literal{m_debugData, LiteralKind::Number, YulString{formatNumber(_value)}, {}}); repr.cost = m_meter.costs(*repr.expression); return repr; } @@ -192,8 +192,8 @@ Representation RepresentationFinder::represent( { Representation repr; repr.expression = make_unique(FunctionCall{ - m_location, - Identifier{m_location, _instruction}, + m_debugData, + Identifier{m_debugData, _instruction}, {ASTCopier{}.translate(*_argument.expression)} }); repr.cost = _argument.cost + m_meter.instructionCosts(*m_dialect.builtin(_instruction)->instruction); @@ -208,8 +208,8 @@ Representation RepresentationFinder::represent( { Representation repr; repr.expression = make_unique(FunctionCall{ - m_location, - Identifier{m_location, _instruction}, + m_debugData, + Identifier{m_debugData, _instruction}, {ASTCopier{}.translate(*_arg1.expression), ASTCopier{}.translate(*_arg2.expression)} }); repr.cost = m_meter.instructionCosts(*m_dialect.builtin(_instruction)->instruction) + _arg1.cost + _arg2.cost; diff --git a/libyul/backends/evm/ConstantOptimiser.h b/libyul/backends/evm/ConstantOptimiser.h index e95a152c8..a60d2b2f5 100644 --- a/libyul/backends/evm/ConstantOptimiser.h +++ b/libyul/backends/evm/ConstantOptimiser.h @@ -74,12 +74,12 @@ public: RepresentationFinder( EVMDialect const& _dialect, GasMeter const& _meter, - langutil::SourceLocation _location, + std::shared_ptr _debugData, std::map& _cache ): m_dialect(_dialect), m_meter(_meter), - m_location(std::move(_location)), + m_debugData(std::move(_debugData)), m_cache(_cache) {} @@ -100,7 +100,7 @@ private: EVMDialect const& m_dialect; GasMeter const& m_meter; - langutil::SourceLocation m_location; + std::shared_ptr m_debugData; /// Counter for the complexity of optimization, will stop when it reaches zero. size_t m_maxSteps = 10000; std::map& m_cache; diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp index 949f89c9d..63f54419f 100644 --- a/libyul/backends/evm/EVMAssembly.cpp +++ b/libyul/backends/evm/EVMAssembly.cpp @@ -39,7 +39,7 @@ size_t constexpr assemblySizeReferenceSize = 4; } -void EVMAssembly::setSourceLocation(SourceLocation const&) +void EVMAssembly::setSourceLocation(langutil::SourceLocation const&) { // Ignored for now; } diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index e357a071c..49bfed968 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -44,6 +44,16 @@ using namespace solidity; using namespace solidity::yul; using namespace solidity::util; +namespace +{ + +langutil::SourceLocation extractSourceLocationFromDebugData(shared_ptr const& _debugData) +{ + return _debugData ? _debugData->location : langutil::SourceLocation{}; +} + +} + CodeTransform::CodeTransform( AbstractAssembly& _assembly, AsmAnalysisInfo& _analysisInfo, @@ -145,13 +155,13 @@ void CodeTransform::operator()(VariableDeclaration const& _varDecl) } else { - m_assembly.setSourceLocation(_varDecl.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_varDecl.debugData)); size_t variablesLeft = numVariables; while (variablesLeft--) m_assembly.appendConstant(u256(0)); } - m_assembly.setSourceLocation(_varDecl.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_varDecl.debugData)); bool atTopOfStack = true; for (size_t varIndex = 0; varIndex < numVariables; ++varIndex) { @@ -205,13 +215,13 @@ void CodeTransform::operator()(Assignment const& _assignment) std::visit(*this, *_assignment.value); expectDeposit(static_cast(_assignment.variableNames.size()), height); - m_assembly.setSourceLocation(_assignment.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_assignment.debugData)); generateMultiAssignment(_assignment.variableNames); } void CodeTransform::operator()(ExpressionStatement const& _statement) { - m_assembly.setSourceLocation(_statement.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_statement.debugData)); std::visit(*this, _statement.expression); } @@ -225,7 +235,7 @@ void CodeTransform::operator()(FunctionCall const& _call) }); else { - m_assembly.setSourceLocation(_call.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData)); EVMAssembly::LabelID returnLabel(numeric_limits::max()); // only used for evm 1.0 returnLabel = m_assembly.newLabelId(); @@ -240,7 +250,7 @@ void CodeTransform::operator()(FunctionCall const& _call) yulAssert(function->arguments.size() == _call.arguments.size(), ""); for (auto const& arg: _call.arguments | ranges::views::reverse) visitExpression(arg); - m_assembly.setSourceLocation(_call.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData)); m_assembly.appendJumpTo( functionEntryID(_call.functionName.name, *function), static_cast(function->returns.size() - function->arguments.size()) - 1, @@ -252,7 +262,7 @@ void CodeTransform::operator()(FunctionCall const& _call) void CodeTransform::operator()(Identifier const& _identifier) { - m_assembly.setSourceLocation(_identifier.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_identifier.debugData)); // First search internals, then externals. yulAssert(m_scope, ""); if (m_scope->lookup(_identifier.name, GenericVisitor{ @@ -284,19 +294,19 @@ void CodeTransform::operator()(Identifier const& _identifier) void CodeTransform::operator()(Literal const& _literal) { - m_assembly.setSourceLocation(_literal.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_literal.debugData)); m_assembly.appendConstant(valueOfLiteral(_literal)); } void CodeTransform::operator()(If const& _if) { visitExpression(*_if.condition); - m_assembly.setSourceLocation(_if.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_if.debugData)); m_assembly.appendInstruction(evmasm::Instruction::ISZERO); AbstractAssembly::LabelID end = m_assembly.newLabelId(); m_assembly.appendJumpToIf(end); (*this)(_if.body); - m_assembly.setSourceLocation(_if.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_if.debugData)); m_assembly.appendLabel(end); } @@ -313,7 +323,7 @@ void CodeTransform::operator()(Switch const& _switch) if (c.value) { (*this)(*c.value); - m_assembly.setSourceLocation(c.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(c.debugData)); AbstractAssembly::LabelID bodyLabel = m_assembly.newLabelId(); caseBodies[&c] = bodyLabel; yulAssert(m_assembly.stackHeight() == expressionHeight + 1, ""); @@ -325,24 +335,24 @@ void CodeTransform::operator()(Switch const& _switch) // default case (*this)(c.body); } - m_assembly.setSourceLocation(_switch.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_switch.debugData)); m_assembly.appendJumpTo(end); size_t numCases = caseBodies.size(); for (auto const& c: caseBodies) { - m_assembly.setSourceLocation(c.first->location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(c.first->debugData)); m_assembly.appendLabel(c.second); (*this)(c.first->body); // Avoid useless "jump to next" for the last case. if (--numCases > 0) { - m_assembly.setSourceLocation(c.first->location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(c.first->debugData)); m_assembly.appendJumpTo(end); } } - m_assembly.setSourceLocation(_switch.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_switch.debugData)); m_assembly.appendLabel(end); m_assembly.appendInstruction(evmasm::Instruction::POP); } @@ -363,7 +373,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function) m_context->variableStackHeights[&var] = height++; } - m_assembly.setSourceLocation(_function.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_function.debugData)); int const stackHeightBefore = m_assembly.stackHeight(); m_assembly.appendLabel(functionEntryID(_function.name, function)); @@ -488,11 +498,11 @@ void CodeTransform::operator()(ForLoop const& _forLoop) AbstractAssembly::LabelID postPart = m_assembly.newLabelId(); AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId(); - m_assembly.setSourceLocation(_forLoop.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_forLoop.debugData)); m_assembly.appendLabel(loopStart); visitExpression(*_forLoop.condition); - m_assembly.setSourceLocation(_forLoop.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_forLoop.debugData)); m_assembly.appendInstruction(evmasm::Instruction::ISZERO); m_assembly.appendJumpToIf(loopEnd); @@ -500,12 +510,12 @@ void CodeTransform::operator()(ForLoop const& _forLoop) m_context->forLoopStack.emplace(Context::ForLoopLabels{ {postPart, stackHeightBody}, {loopEnd, stackHeightBody} }); (*this)(_forLoop.body); - m_assembly.setSourceLocation(_forLoop.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_forLoop.debugData)); m_assembly.appendLabel(postPart); (*this)(_forLoop.post); - m_assembly.setSourceLocation(_forLoop.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_forLoop.debugData)); m_assembly.appendJumpTo(loopStart); m_assembly.appendLabel(loopEnd); @@ -525,7 +535,7 @@ int CodeTransform::appendPopUntil(int _targetDepth) void CodeTransform::operator()(Break const& _break) { yulAssert(!m_context->forLoopStack.empty(), "Invalid break-statement. Requires surrounding for-loop in code generation."); - m_assembly.setSourceLocation(_break.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_break.debugData)); Context::JumpInfo const& jump = m_context->forLoopStack.top().done; m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight)); @@ -534,7 +544,7 @@ void CodeTransform::operator()(Break const& _break) void CodeTransform::operator()(Continue const& _continue) { yulAssert(!m_context->forLoopStack.empty(), "Invalid continue-statement. Requires surrounding for-loop in code generation."); - m_assembly.setSourceLocation(_continue.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_continue.debugData)); Context::JumpInfo const& jump = m_context->forLoopStack.top().post; m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight)); @@ -544,7 +554,7 @@ void CodeTransform::operator()(Leave const& _leaveStatement) { yulAssert(m_functionExitLabel, "Invalid leave-statement. Requires surrounding function in code generation."); yulAssert(m_functionExitStackHeight, ""); - m_assembly.setSourceLocation(_leaveStatement.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_leaveStatement.debugData)); m_assembly.appendJumpTo(*m_functionExitLabel, appendPopUntil(*m_functionExitStackHeight)); } @@ -605,7 +615,7 @@ void CodeTransform::setupReturnVariablesAndFunctionExit() // Allocate slots for return variables as if they were declared as variables in the virtual function scope. for (TypedName const& var: m_delayedReturnVariables) - (*this)(VariableDeclaration{var.location, {var}, {}}); + (*this)(VariableDeclaration{var.debugData, {var}, {}}); m_functionExitStackHeight = ranges::max(m_delayedReturnVariables | ranges::views::transform([&](TypedName const& _name) { return variableStackHeight(_name.name); @@ -654,7 +664,7 @@ void CodeTransform::visitStatements(vector const& _statements) auto const* functionDefinition = std::get_if(&statement); if (functionDefinition && !jumpTarget) { - m_assembly.setSourceLocation(locationOf(statement)); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(functionDefinition->debugData)); jumpTarget = m_assembly.newLabelId(); m_assembly.appendJumpTo(*jumpTarget, 0); } @@ -675,7 +685,7 @@ void CodeTransform::visitStatements(vector const& _statements) void CodeTransform::finalizeBlock(Block const& _block, optional blockStartStackHeight) { - m_assembly.setSourceLocation(_block.location); + m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_block.debugData)); freeUnusedVariables(); diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index be7fef528..531861a6e 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -39,6 +39,7 @@ class ErrorReporter; namespace solidity::yul { + struct AsmAnalysisInfo; class EVMAssembly; diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index dda28f19e..90e5b2815 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -55,7 +55,7 @@ void visitArguments( for (auto const& arg: _call.arguments | ranges::views::reverse) _visitExpression(arg); - _assembly.setSourceLocation(_call.location); + _assembly.setSourceLocation(_call.debugData->location); } @@ -259,7 +259,7 @@ map createBuiltins(langutil::EVMVersion _evmVe _visitExpression(_call.arguments[2]); YulString identifier = std::get(_call.arguments[1]).value; _visitExpression(_call.arguments[0]); - _assembly.setSourceLocation(_call.location); + _assembly.setSourceLocation(_call.debugData->location); _assembly.appendImmutableAssignment(identifier.str()); } )); diff --git a/libyul/backends/wasm/EVMToEwasmTranslator.cpp b/libyul/backends/wasm/EVMToEwasmTranslator.cpp index a2723533d..074174532 100644 --- a/libyul/backends/wasm/EVMToEwasmTranslator.cpp +++ b/libyul/backends/wasm/EVMToEwasmTranslator.cpp @@ -32,10 +32,10 @@ #include #include +#include #include #include #include -#include #include #include diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index ec5e3a0de..e796b1f8f 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -65,8 +65,8 @@ void WordSizeTransform::operator()(FunctionCall& _fc) void WordSizeTransform::operator()(If& _if) { _if.condition = make_unique(FunctionCall{ - locationOf(*_if.condition), - Identifier{locationOf(*_if.condition), "or_bool"_yulstring}, + debugDataOf(*_if.condition), + Identifier{debugDataOf(*_if.condition), "or_bool"_yulstring}, expandValueToVector(*_if.condition) }); (*this)(_if.body); @@ -81,8 +81,8 @@ void WordSizeTransform::operator()(ForLoop& _for) { (*this)(_for.pre); _for.condition = make_unique(FunctionCall{ - locationOf(*_for.condition), - Identifier{locationOf(*_for.condition), "or_bool"_yulstring}, + debugDataOf(*_for.condition), + Identifier{debugDataOf(*_for.condition), "or_bool"_yulstring}, expandValueToVector(*_for.condition) }); (*this)(_for.post); @@ -116,18 +116,18 @@ void WordSizeTransform::operator()(Block& _block) vector ret; for (size_t i = 0; i < 3; i++) ret.emplace_back(VariableDeclaration{ - varDecl.location, - {TypedName{varDecl.location, newLhs[i], m_targetDialect.defaultType}}, + varDecl.debugData, + {TypedName{varDecl.debugData, newLhs[i], m_targetDialect.defaultType}}, make_unique(Literal{ - locationOf(*varDecl.value), + debugDataOf(*varDecl.value), LiteralKind::Number, "0"_yulstring, m_targetDialect.defaultType }) }); ret.emplace_back(VariableDeclaration{ - varDecl.location, - {TypedName{varDecl.location, newLhs[3], m_targetDialect.defaultType}}, + varDecl.debugData, + {TypedName{varDecl.debugData, newLhs[3], m_targetDialect.defaultType}}, std::move(varDecl.value) }); return {std::move(ret)}; @@ -147,8 +147,8 @@ void WordSizeTransform::operator()(Block& _block) vector ret; for (size_t i = 0; i < 4; i++) ret.emplace_back(VariableDeclaration{ - varDecl.location, - {TypedName{varDecl.location, newLhs[i], m_targetDialect.defaultType}}, + varDecl.debugData, + {TypedName{varDecl.debugData, newLhs[i], m_targetDialect.defaultType}}, std::move(newRhs[i]) } ); @@ -177,18 +177,18 @@ void WordSizeTransform::operator()(Block& _block) vector ret; for (size_t i = 0; i < 3; i++) ret.emplace_back(Assignment{ - assignment.location, - {Identifier{assignment.location, newLhs[i]}}, + assignment.debugData, + {Identifier{assignment.debugData, newLhs[i]}}, make_unique(Literal{ - locationOf(*assignment.value), + debugDataOf(*assignment.value), LiteralKind::Number, "0"_yulstring, m_targetDialect.defaultType }) }); ret.emplace_back(Assignment{ - assignment.location, - {Identifier{assignment.location, newLhs[3]}}, + assignment.debugData, + {Identifier{assignment.debugData, newLhs[3]}}, std::move(assignment.value) }); return {std::move(ret)}; @@ -208,8 +208,8 @@ void WordSizeTransform::operator()(Block& _block) vector ret; for (size_t i = 0; i < 4; i++) ret.emplace_back(Assignment{ - assignment.location, - {Identifier{assignment.location, m_variableMapping.at(lhsName)[i]}}, + assignment.debugData, + {Identifier{assignment.debugData, m_variableMapping.at(lhsName)[i]}}, std::move(newRhs[i]) } ); @@ -258,7 +258,7 @@ void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList) { TypedNameList ret; for (auto newName: generateU64IdentifierNames(_n.name)) - ret.emplace_back(TypedName{_n.location, newName, m_targetDialect.defaultType}); + ret.emplace_back(TypedName{_n.debugData, newName, m_targetDialect.defaultType}); return ret; } ); @@ -272,14 +272,14 @@ void WordSizeTransform::rewriteIdentifierList(vector& _ids) { vector ret; for (auto newId: m_variableMapping.at(_id.name)) - ret.push_back(Identifier{_id.location, newId}); + ret.push_back(Identifier{_id.debugData, newId}); return ret; } ); } vector WordSizeTransform::handleSwitchInternal( - langutil::SourceLocation const& _location, + shared_ptr const& _debugData, vector const& _splitExpressions, vector _cases, YulString _runDefaultFlag, @@ -304,19 +304,19 @@ vector WordSizeTransform::handleSwitchInternal( } Switch ret{ - _location, - make_unique(Identifier{_location, _splitExpressions.at(_depth)}), + _debugData, + make_unique(Identifier{_debugData, _splitExpressions.at(_depth)}), {} }; for (auto& c: cases) { - Literal label{_location, LiteralKind::Number, YulString(c.first.str()), m_targetDialect.defaultType}; + Literal label{_debugData, LiteralKind::Number, YulString(c.first.str()), m_targetDialect.defaultType}; ret.cases.emplace_back(Case{ - c.second.front().location, + c.second.front().debugData, make_unique(std::move(label)), - Block{_location, handleSwitchInternal( - _location, + Block{_debugData, handleSwitchInternal( + _debugData, _splitExpressions, std::move(c.second), _runDefaultFlag, @@ -326,13 +326,13 @@ vector WordSizeTransform::handleSwitchInternal( } if (!_runDefaultFlag.empty()) ret.cases.emplace_back(Case{ - _location, + _debugData, nullptr, - Block{_location, make_vector( + Block{_debugData, make_vector( Assignment{ - _location, - {{_location, _runDefaultFlag}}, - make_unique(Literal{_location, LiteralKind::Boolean, "true"_yulstring, m_targetDialect.boolType}) + _debugData, + {{_debugData, _runDefaultFlag}}, + make_unique(Literal{_debugData, LiteralKind::Boolean, "true"_yulstring, m_targetDialect.boolType}) } )} }); @@ -356,8 +356,8 @@ std::vector WordSizeTransform::handleSwitch(Switch& _switch) defaultCase = std::move(_switch.cases.back()); _switch.cases.pop_back(); ret.emplace_back(VariableDeclaration{ - _switch.location, - {TypedName{_switch.location, runDefaultFlag, m_targetDialect.boolType}}, + _switch.debugData, + {TypedName{_switch.debugData, runDefaultFlag, m_targetDialect.boolType}}, {} }); } @@ -366,7 +366,7 @@ std::vector WordSizeTransform::handleSwitch(Switch& _switch) splitExpressions.emplace_back(std::get(*expr).name); ret += handleSwitchInternal( - _switch.location, + _switch.debugData, splitExpressions, std::move(_switch.cases), runDefaultFlag, @@ -374,8 +374,8 @@ std::vector WordSizeTransform::handleSwitch(Switch& _switch) ); if (!runDefaultFlag.empty()) ret.emplace_back(If{ - _switch.location, - make_unique(Identifier{_switch.location, runDefaultFlag}), + _switch.debugData, + make_unique(Identifier{_switch.debugData, runDefaultFlag}), std::move(defaultCase.body) }); return ret; @@ -397,7 +397,7 @@ array, 4> WordSizeTransform::expandValue(Expression const { auto const& id = std::get(_e); for (size_t i = 0; i < 4; i++) - ret[i] = make_unique(Identifier{id.location, m_variableMapping.at(id.name)[i]}); + ret[i] = make_unique(Identifier{id.debugData, m_variableMapping.at(id.name)[i]}); } else if (holds_alternative(_e)) { @@ -410,7 +410,7 @@ array, 4> WordSizeTransform::expandValue(Expression const val >>= 64; ret[exprIndexReverse] = make_unique( Literal{ - lit.location, + lit.debugData, LiteralKind::Number, YulString(currentVal.str()), m_targetDialect.defaultType diff --git a/libyul/backends/wasm/WordSizeTransform.h b/libyul/backends/wasm/WordSizeTransform.h index 1bf7dcaca..faa7806a5 100644 --- a/libyul/backends/wasm/WordSizeTransform.h +++ b/libyul/backends/wasm/WordSizeTransform.h @@ -87,7 +87,7 @@ private: std::vector handleSwitch(Switch& _switch); std::vector handleSwitchInternal( - langutil::SourceLocation const& _location, + std::shared_ptr const& _debugData, std::vector const& _splitExpressions, std::vector _cases, YulString _runDefaultFlag, diff --git a/libyul/optimiser/ASTCopier.cpp b/libyul/optimiser/ASTCopier.cpp index 8c0b3acfa..6117e0bfa 100644 --- a/libyul/optimiser/ASTCopier.cpp +++ b/libyul/optimiser/ASTCopier.cpp @@ -34,13 +34,13 @@ using namespace solidity::util; Statement ASTCopier::operator()(ExpressionStatement const& _statement) { - return ExpressionStatement{ _statement.location, translate(_statement.expression) }; + return ExpressionStatement{ _statement.debugData, translate(_statement.expression) }; } Statement ASTCopier::operator()(VariableDeclaration const& _varDecl) { return VariableDeclaration{ - _varDecl.location, + _varDecl.debugData, translateVector(_varDecl.variables), translate(_varDecl.value) }; @@ -49,7 +49,7 @@ Statement ASTCopier::operator()(VariableDeclaration const& _varDecl) Statement ASTCopier::operator()(Assignment const& _assignment) { return Assignment{ - _assignment.location, + _assignment.debugData, translateVector(_assignment.variableNames), translate(_assignment.value) }; @@ -58,7 +58,7 @@ Statement ASTCopier::operator()(Assignment const& _assignment) Expression ASTCopier::operator()(FunctionCall const& _call) { return FunctionCall{ - _call.location, + _call.debugData, translate(_call.functionName), translateVector(_call.arguments) }; @@ -76,12 +76,12 @@ Expression ASTCopier::operator()(Literal const& _literal) Statement ASTCopier::operator()(If const& _if) { - return If{_if.location, translate(_if.condition), translate(_if.body)}; + return If{_if.debugData, translate(_if.condition), translate(_if.body)}; } Statement ASTCopier::operator()(Switch const& _switch) { - return Switch{_switch.location, translate(_switch.expression), translateVector(_switch.cases)}; + return Switch{_switch.debugData, translate(_switch.expression), translateVector(_switch.cases)}; } Statement ASTCopier::operator()(FunctionDefinition const& _function) @@ -92,7 +92,7 @@ Statement ASTCopier::operator()(FunctionDefinition const& _function) ScopeGuard g([&]() { this->leaveFunction(_function); }); return FunctionDefinition{ - _function.location, + _function.debugData, translatedName, translateVector(_function.parameters), translateVector(_function.returnVariables), @@ -106,7 +106,7 @@ Statement ASTCopier::operator()(ForLoop const& _forLoop) ScopeGuard g([&]() { this->leaveScope(_forLoop.pre); }); return ForLoop{ - _forLoop.location, + _forLoop.debugData, translate(_forLoop.pre), translate(_forLoop.condition), translate(_forLoop.post), @@ -148,17 +148,17 @@ Block ASTCopier::translate(Block const& _block) enterScope(_block); ScopeGuard g([&]() { this->leaveScope(_block); }); - return Block{_block.location, translateVector(_block.statements)}; + return Block{_block.debugData, translateVector(_block.statements)}; } Case ASTCopier::translate(Case const& _case) { - return Case{_case.location, translate(_case.value), translate(_case.body)}; + return Case{_case.debugData, translate(_case.value), translate(_case.body)}; } Identifier ASTCopier::translate(Identifier const& _identifier) { - return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; + return Identifier{_identifier.debugData, translateIdentifier(_identifier.name)}; } Literal ASTCopier::translate(Literal const& _literal) @@ -168,7 +168,7 @@ Literal ASTCopier::translate(Literal const& _literal) TypedName ASTCopier::translate(TypedName const& _typedName) { - return TypedName{_typedName.location, translateIdentifier(_typedName.name), _typedName.type}; + return TypedName{_typedName.debugData, translateIdentifier(_typedName.name), _typedName.type}; } YulString FunctionCopier::translateIdentifier(YulString _name) diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index c2b5f8a42..2725db395 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -95,13 +95,13 @@ void CommonSubexpressionEliminator::visit(Expression& _e) if (Identifier const* identifier = get_if(&_e)) { - YulString name = identifier->name; - if (m_value.count(name)) + YulString identifierName = identifier->name; + if (m_value.count(identifierName)) { - assertThrow(m_value.at(name).value, OptimizerException, ""); - if (Identifier const* value = get_if(m_value.at(name).value)) + assertThrow(m_value.at(identifierName).value, OptimizerException, ""); + if (Identifier const* value = get_if(m_value.at(identifierName).value)) if (inScope(value->name)) - _e = Identifier{locationOf(_e), value->name}; + _e = Identifier{debugDataOf(_e), value->name}; } } else @@ -120,7 +120,7 @@ void CommonSubexpressionEliminator::visit(Expression& _e) continue; if (SyntacticallyEqual{}(_e, *value.value) && inScope(variable)) { - _e = Identifier{locationOf(_e), variable}; + _e = Identifier{debugDataOf(_e), variable}; break; } } diff --git a/libyul/optimiser/ConditionalSimplifier.cpp b/libyul/optimiser/ConditionalSimplifier.cpp index 0a66e28df..78fcb174e 100644 --- a/libyul/optimiser/ConditionalSimplifier.cpp +++ b/libyul/optimiser/ConditionalSimplifier.cpp @@ -44,8 +44,8 @@ void ConditionalSimplifier::operator()(Switch& _switch) (*this)(*_case.value); _case.body.statements.insert(_case.body.statements.begin(), Assignment{ - _case.body.location, - {Identifier{_case.body.location, expr}}, + _case.body.debugData, + {Identifier{_case.body.debugData, expr}}, make_unique(*_case.value) } ); @@ -72,12 +72,12 @@ void ConditionalSimplifier::operator()(Block& _block) ) { YulString condition = std::get(*_if.condition).name; - langutil::SourceLocation location = _if.location; + std::shared_ptr debugData = _if.debugData; return make_vector( std::move(_s), Assignment{ - location, - {Identifier{location, condition}}, + debugData, + {Identifier{debugData, condition}}, make_unique(m_dialect.zeroLiteralForType(m_dialect.boolType)) } ); diff --git a/libyul/optimiser/ControlFlowSimplifier.cpp b/libyul/optimiser/ControlFlowSimplifier.cpp index 73d907d33..443a2a9be 100644 --- a/libyul/optimiser/ControlFlowSimplifier.cpp +++ b/libyul/optimiser/ControlFlowSimplifier.cpp @@ -38,14 +38,14 @@ namespace { ExpressionStatement makeDiscardCall( - langutil::SourceLocation const& _location, + std::shared_ptr const& _debugData, BuiltinFunction const& _discardFunction, Expression&& _expression ) { - return {_location, FunctionCall{ - _location, - Identifier{_location, _discardFunction.name}, + return {_debugData, FunctionCall{ + _debugData, + Identifier{_debugData, _discardFunction.name}, {std::move(_expression)} }}; } @@ -126,7 +126,7 @@ void ControlFlowSimplifier::visit(Statement& _st) if (isTerminating && m_numContinueStatements == 0 && m_numBreakStatements == 0) { - If replacement{forLoop.location, std::move(forLoop.condition), std::move(forLoop.body)}; + If replacement{forLoop.debugData, std::move(forLoop.condition), std::move(forLoop.body)}; if (controlFlow == TerminationFinder::ControlFlow::Break) replacement.body.statements.resize(replacement.body.statements.size() - 1); _st = std::move(replacement); @@ -149,7 +149,7 @@ void ControlFlowSimplifier::simplify(std::vector& _statements) { OptionalStatements s = vector{}; s->emplace_back(makeDiscardCall( - _ifStmt.location, + _ifStmt.debugData, *m_dialect.discardFunction(m_dialect.boolType), std::move(*_ifStmt.condition) )); @@ -191,10 +191,8 @@ OptionalStatements ControlFlowSimplifier::reduceNoCaseSwitch(Switch& _switchStmt if (!discardFunction) return {}; - auto loc = locationOf(*_switchStmt.expression); - return make_vector(makeDiscardCall( - loc, + debugDataOf(*_switchStmt.expression), *discardFunction, std::move(*_switchStmt.expression) )); @@ -205,17 +203,17 @@ OptionalStatements ControlFlowSimplifier::reduceSingleCaseSwitch(Switch& _switch yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!"); auto& switchCase = _switchStmt.cases.front(); - auto loc = locationOf(*_switchStmt.expression); + shared_ptr debugData = debugDataOf(*_switchStmt.expression); YulString type = m_typeInfo.typeOf(*_switchStmt.expression); if (switchCase.value) { if (!m_dialect.equalityFunction(type)) return {}; return make_vector(If{ - std::move(_switchStmt.location), + std::move(_switchStmt.debugData), make_unique(FunctionCall{ - loc, - Identifier{loc, m_dialect.equalityFunction(type)->name}, + debugData, + Identifier{debugData, m_dialect.equalityFunction(type)->name}, {std::move(*switchCase.value), std::move(*_switchStmt.expression)} }), std::move(switchCase.body) @@ -228,7 +226,7 @@ OptionalStatements ControlFlowSimplifier::reduceSingleCaseSwitch(Switch& _switch return make_vector( makeDiscardCall( - loc, + debugData, *m_dialect.discardFunction(type), std::move(*_switchStmt.expression) ), diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index 21107c026..736ebe4b9 100644 --- a/libyul/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -39,5 +39,5 @@ void ExpressionSimplifier::visit(Expression& _expression) ASTModifier::visit(_expression); while (auto const* match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_value)) - _expression = match->action().toExpression(locationOf(_expression)); + _expression = match->action().toExpression(debugDataOf(_expression)); } diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index 7aad0bd58..b93b55613 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -101,15 +101,15 @@ void ExpressionSplitter::outlineExpression(Expression& _expr) visit(_expr); - SourceLocation location = locationOf(_expr); + shared_ptr debugData = debugDataOf(_expr); YulString var = m_nameDispenser.newName({}); YulString type = m_typeInfo.typeOf(_expr); m_statementsToPrefix.emplace_back(VariableDeclaration{ - location, - {{TypedName{location, var, type}}}, + debugData, + {{TypedName{debugData, var, type}}}, make_unique(std::move(_expr)) }); - _expr = Identifier{location, var}; + _expr = Identifier{debugData, var}; m_typeInfo.setVariableType(var, type); } diff --git a/libyul/optimiser/ForLoopConditionIntoBody.cpp b/libyul/optimiser/ForLoopConditionIntoBody.cpp index 059479757..cf7c6814e 100644 --- a/libyul/optimiser/ForLoopConditionIntoBody.cpp +++ b/libyul/optimiser/ForLoopConditionIntoBody.cpp @@ -39,25 +39,25 @@ void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop) !holds_alternative(*_forLoop.condition) ) { - langutil::SourceLocation const loc = locationOf(*_forLoop.condition); + shared_ptr debugData = debugDataOf(*_forLoop.condition); _forLoop.body.statements.emplace( begin(_forLoop.body.statements), If { - loc, + debugData, make_unique( FunctionCall { - loc, - {loc, m_dialect.booleanNegationFunction()->name}, + debugData, + {debugData, m_dialect.booleanNegationFunction()->name}, util::make_vector(std::move(*_forLoop.condition)) } ), - Block {loc, util::make_vector(Break{{}})} + Block {debugData, util::make_vector(Break{{}})} } ); _forLoop.condition = make_unique( Literal { - loc, + debugData, LiteralKind::Boolean, "true"_yulstring, m_dialect.boolType diff --git a/libyul/optimiser/ForLoopConditionOutOfBody.cpp b/libyul/optimiser/ForLoopConditionOutOfBody.cpp index 1ddc3c257..98a37d9fc 100644 --- a/libyul/optimiser/ForLoopConditionOutOfBody.cpp +++ b/libyul/optimiser/ForLoopConditionOutOfBody.cpp @@ -55,7 +55,7 @@ void ForLoopConditionOutOfBody::operator()(ForLoop& _forLoop) return; YulString iszero = m_dialect.booleanNegationFunction()->name; - langutil::SourceLocation location = locationOf(*firstStatement.condition); + shared_ptr debugData = debugDataOf(*firstStatement.condition); if ( holds_alternative(*firstStatement.condition) && @@ -64,8 +64,8 @@ void ForLoopConditionOutOfBody::operator()(ForLoop& _forLoop) _forLoop.condition = make_unique(std::move(std::get(*firstStatement.condition).arguments.front())); else _forLoop.condition = make_unique(FunctionCall{ - location, - Identifier{location, iszero}, + debugData, + Identifier{debugData, iszero}, util::make_vector( std::move(*firstStatement.condition) ) diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index 189a06164..0a494a31f 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -268,7 +268,7 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC auto newVariable = [&](TypedName const& _existingVariable, Expression* _value) { YulString newName = m_nameDispenser.newName(_existingVariable.name); variableReplacements[_existingVariable.name] = newName; - VariableDeclaration varDecl{_funCall.location, {{_funCall.location, newName, _existingVariable.type}}, {}}; + VariableDeclaration varDecl{_funCall.debugData, {{_funCall.debugData, newName, _existingVariable.type}}, {}}; if (_value) varDecl.value = make_unique(std::move(*_value)); else @@ -290,10 +290,10 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC { for (size_t i = 0; i < _assignment.variableNames.size(); ++i) newStatements.emplace_back(Assignment{ - _assignment.location, + _assignment.debugData, {_assignment.variableNames[i]}, make_unique(Identifier{ - _assignment.location, + _assignment.debugData, variableReplacements.at(function->returnVariables[i].name) }) }); @@ -302,10 +302,10 @@ vector InlineModifier::performInline(Statement& _statement, FunctionC { for (size_t i = 0; i < _varDecl.variables.size(); ++i) newStatements.emplace_back(VariableDeclaration{ - _varDecl.location, + _varDecl.debugData, {std::move(_varDecl.variables[i])}, make_unique(Identifier{ - _varDecl.location, + _varDecl.debugData, variableReplacements.at(function->returnVariables[i].name) }) }); diff --git a/libyul/optimiser/FunctionGrouper.cpp b/libyul/optimiser/FunctionGrouper.cpp index 2ed92435e..41fb75554 100644 --- a/libyul/optimiser/FunctionGrouper.cpp +++ b/libyul/optimiser/FunctionGrouper.cpp @@ -35,7 +35,7 @@ void FunctionGrouper::operator()(Block& _block) return; vector reordered; - reordered.emplace_back(Block{_block.location, {}}); + reordered.emplace_back(Block{_block.debugData, {}}); for (auto&& statement: _block.statements) { diff --git a/libyul/optimiser/FunctionHoister.cpp b/libyul/optimiser/FunctionHoister.cpp index aa5c373e9..5d5cf7d12 100644 --- a/libyul/optimiser/FunctionHoister.cpp +++ b/libyul/optimiser/FunctionHoister.cpp @@ -41,7 +41,7 @@ void FunctionHoister::operator()(Block& _block) if (holds_alternative(statement)) { m_functions.emplace_back(std::move(statement)); - statement = Block{_block.location, {}}; + statement = Block{_block.debugData, {}}; } } removeEmptyBlocks(_block); diff --git a/libyul/optimiser/FunctionSpecializer.cpp b/libyul/optimiser/FunctionSpecializer.cpp index 65f092487..1d14db18b 100644 --- a/libyul/optimiser/FunctionSpecializer.cpp +++ b/libyul/optimiser/FunctionSpecializer.cpp @@ -104,7 +104,7 @@ FunctionDefinition FunctionSpecializer::specialize( if (argument) missingVariableDeclarations.emplace_back( VariableDeclaration{ - _f.location, + _f.debugData, vector{newFunction.parameters[index]}, make_unique(move(*argument)) } diff --git a/libyul/optimiser/KnowledgeBase.cpp b/libyul/optimiser/KnowledgeBase.cpp index 623db6a70..059c9d40c 100644 --- a/libyul/optimiser/KnowledgeBase.cpp +++ b/libyul/optimiser/KnowledgeBase.cpp @@ -84,7 +84,7 @@ Expression KnowledgeBase::simplify(Expression _expression) arg = simplify(arg); if (auto match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_variableValues)) - return simplify(match->action().toExpression(locationOf(_expression))); + return simplify(match->action().toExpression(debugDataOf(_expression))); return _expression; } diff --git a/libyul/optimiser/LoadResolver.cpp b/libyul/optimiser/LoadResolver.cpp index 3ef080155..2716be3bd 100644 --- a/libyul/optimiser/LoadResolver.cpp +++ b/libyul/optimiser/LoadResolver.cpp @@ -85,12 +85,12 @@ void LoadResolver::tryResolve( { if (auto value = util::valueOrNullptr(m_storage, key)) if (inScope(*value)) - _e = Identifier{locationOf(_e), *value}; + _e = Identifier{debugDataOf(_e), *value}; } else if (!m_containsMSize && _location == StoreLoadLocation::Memory) if (auto value = util::valueOrNullptr(m_memory, key)) if (inScope(*value)) - _e = Identifier{locationOf(_e), *value}; + _e = Identifier{debugDataOf(_e), *value}; } void LoadResolver::tryEvaluateKeccak( @@ -140,7 +140,7 @@ void LoadResolver::tryEvaluateKeccak( bytes contentAsBytes = toBigEndian(*memoryContent); contentAsBytes.resize(static_cast(*byteLength)); _e = Literal{ - locationOf(_e), + debugDataOf(_e), LiteralKind::Number, YulString{u256(keccak256(contentAsBytes)).str()}, m_dialect.defaultType diff --git a/libyul/optimiser/MainFunction.cpp b/libyul/optimiser/MainFunction.cpp index 646bc8faf..8adabbe32 100644 --- a/libyul/optimiser/MainFunction.cpp +++ b/libyul/optimiser/MainFunction.cpp @@ -44,7 +44,7 @@ void MainFunction::operator()(Block& _block) Block& block = std::get(_block.statements[0]); FunctionDefinition main{ - block.location, + block.debugData, "main"_yulstring, {}, {}, diff --git a/libyul/optimiser/ReasoningBasedSimplifier.cpp b/libyul/optimiser/ReasoningBasedSimplifier.cpp index b428cbab5..e75ca3cf5 100644 --- a/libyul/optimiser/ReasoningBasedSimplifier.cpp +++ b/libyul/optimiser/ReasoningBasedSimplifier.cpp @@ -79,7 +79,7 @@ void ReasoningBasedSimplifier::operator()(If& _if) if (result == CheckResult::UNSATISFIABLE) { Literal trueCondition = m_dialect.trueLiteral(); - trueCondition.location = locationOf(*_if.condition); + trueCondition.debugData = debugDataOf(*_if.condition); _if.condition = make_unique(move(trueCondition)); } else @@ -91,7 +91,7 @@ void ReasoningBasedSimplifier::operator()(If& _if) if (result2 == CheckResult::UNSATISFIABLE) { Literal falseCondition = m_dialect.zeroLiteralForType(m_dialect.boolType); - falseCondition.location = locationOf(*_if.condition); + falseCondition.debugData = debugDataOf(*_if.condition); _if.condition = make_unique(move(falseCondition)); _if.body = yul::Block{}; // Nothing left to be done. diff --git a/libyul/optimiser/SSAReverser.cpp b/libyul/optimiser/SSAReverser.cpp index d59a93d0e..d6bf7138a 100644 --- a/libyul/optimiser/SSAReverser.cpp +++ b/libyul/optimiser/SSAReverser.cpp @@ -66,12 +66,12 @@ void SSAReverser::operator()(Block& _block) else return util::make_vector( Assignment{ - std::move(assignment->location), + std::move(assignment->debugData), assignment->variableNames, std::move(varDecl->value) }, VariableDeclaration{ - std::move(varDecl->location), + std::move(varDecl->debugData), std::move(varDecl->variables), std::make_unique(assignment->variableNames.front()) } @@ -97,17 +97,17 @@ void SSAReverser::operator()(Block& _block) ) { auto varIdentifier2 = std::make_unique(Identifier{ - varDecl2->variables.front().location, + varDecl2->variables.front().debugData, varDecl2->variables.front().name }); return util::make_vector( VariableDeclaration{ - std::move(varDecl2->location), + std::move(varDecl2->debugData), std::move(varDecl2->variables), std::move(varDecl->value) }, VariableDeclaration{ - std::move(varDecl->location), + std::move(varDecl->debugData), std::move(varDecl->variables), std::move(varIdentifier2) } diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index f77e836f2..f0ce68c92 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -85,19 +85,19 @@ void IntroduceSSA::operator()(Block& _block) // Replace "let a := v" by "let a_1 := v let a := a_1" // Replace "let a, b := v" by "let a_1, b_1 := v let a := a_1 let b := b_2" - auto loc = varDecl.location; + shared_ptr debugData = varDecl.debugData; vector statements; - statements.emplace_back(VariableDeclaration{loc, {}, std::move(varDecl.value)}); + statements.emplace_back(VariableDeclaration{debugData, {}, std::move(varDecl.value)}); TypedNameList newVariables; for (auto const& var: varDecl.variables) { YulString oldName = var.name; YulString newName = m_nameDispenser.newName(oldName); - newVariables.emplace_back(TypedName{loc, newName, var.type}); + newVariables.emplace_back(TypedName{debugData, newName, var.type}); statements.emplace_back(VariableDeclaration{ - loc, - {TypedName{loc, oldName, var.type}}, - make_unique(Identifier{loc, newName}) + debugData, + {TypedName{debugData, oldName, var.type}}, + make_unique(Identifier{debugData, newName}) }); } std::get(statements.front()).variables = std::move(newVariables); @@ -112,23 +112,22 @@ void IntroduceSSA::operator()(Block& _block) // Replace "a := v" by "let a_1 := v a := v" // Replace "a, b := v" by "let a_1, b_1 := v a := a_1 b := b_2" - auto loc = assignment.location; + std::shared_ptr debugData = assignment.debugData; vector statements; - statements.emplace_back(VariableDeclaration{loc, {}, std::move(assignment.value)}); + statements.emplace_back(VariableDeclaration{debugData, {}, std::move(assignment.value)}); TypedNameList newVariables; for (auto const& var: assignment.variableNames) { YulString oldName = var.name; YulString newName = m_nameDispenser.newName(oldName); - newVariables.emplace_back(TypedName{ - loc, + newVariables.emplace_back(TypedName{debugData, newName, m_typeInfo.typeOfVariable(oldName) }); statements.emplace_back(Assignment{ - loc, - {Identifier{loc, oldName}}, - make_unique(Identifier{loc, newName}) + debugData, + {Identifier{debugData, oldName}}, + make_unique(Identifier{debugData, newName}) }); } std::get(statements.front()).variables = std::move(newVariables); @@ -238,9 +237,9 @@ void IntroduceControlFlowSSA::operator()(Block& _block) { YulString newName = m_nameDispenser.newName(toReassign); toPrepend.emplace_back(VariableDeclaration{ - locationOf(_s), - {TypedName{locationOf(_s), newName, m_typeInfo.typeOfVariable(toReassign)}}, - make_unique(Identifier{locationOf(_s), toReassign}) + debugDataOf(_s), + {TypedName{debugDataOf(_s), newName, m_typeInfo.typeOfVariable(toReassign)}}, + make_unique(Identifier{debugDataOf(_s), toReassign}) }); assignedVariables.insert(toReassign); } diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 8e55e8c5e..4a0e0668c 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -234,27 +234,26 @@ evmasm::Instruction Pattern::instruction() const return m_instruction; } -Expression Pattern::toExpression(SourceLocation const& _location) const +Expression Pattern::toExpression(shared_ptr const& _debugData) const { if (matchGroup()) return ASTCopier().translate(matchGroupValue()); if (m_kind == PatternKind::Constant) { assertThrow(m_data, OptimizerException, "No match group and no constant value given."); - return Literal{_location, LiteralKind::Number, YulString{util::formatNumber(*m_data)}, {}}; + return Literal{_debugData, LiteralKind::Number, YulString{util::formatNumber(*m_data)}, {}}; } else if (m_kind == PatternKind::Operation) { vector arguments; for (auto const& arg: m_arguments) - arguments.emplace_back(arg.toExpression(_location)); + arguments.emplace_back(arg.toExpression(_debugData)); string name = instructionInfo(m_instruction).name; transform(begin(name), end(name), begin(name), [](auto _c) { return tolower(_c); }); - return FunctionCall{ - _location, - Identifier{_location, YulString{name}}, + return FunctionCall{_debugData, + Identifier{_debugData, YulString{name}}, std::move(arguments) }; } diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index f9d0c9da9..d4da4a2b0 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -130,7 +130,7 @@ public: /// Turns this pattern into an actual expression. Should only be called /// for patterns resulting from an action, i.e. with match groups assigned. - Expression toExpression(langutil::SourceLocation const& _location) const; + Expression toExpression(std::shared_ptr const& _debugData) const; private: Expression const& matchGroupValue() const; diff --git a/libyul/optimiser/StackToMemoryMover.cpp b/libyul/optimiser/StackToMemoryMover.cpp index 25eb25cd5..3e2d72cad 100644 --- a/libyul/optimiser/StackToMemoryMover.cpp +++ b/libyul/optimiser/StackToMemoryMover.cpp @@ -31,7 +31,7 @@ namespace { vector generateMemoryStore( Dialect const& _dialect, - langutil::SourceLocation const& _loc, + shared_ptr const& _debugData, YulString _mpos, Expression _value ) @@ -39,26 +39,26 @@ vector generateMemoryStore( BuiltinFunction const* memoryStoreFunction = _dialect.memoryStoreFunction(_dialect.defaultType); yulAssert(memoryStoreFunction, ""); vector result; - result.emplace_back(ExpressionStatement{_loc, FunctionCall{ - _loc, - Identifier{_loc, memoryStoreFunction->name}, + result.emplace_back(ExpressionStatement{_debugData, FunctionCall{ + _debugData, + Identifier{_debugData, memoryStoreFunction->name}, { - Literal{_loc, LiteralKind::Number, _mpos, {}}, + Literal{_debugData, LiteralKind::Number, _mpos, {}}, std::move(_value) } }}); return result; } -FunctionCall generateMemoryLoad(Dialect const& _dialect, langutil::SourceLocation const& _loc, YulString _mpos) +FunctionCall generateMemoryLoad(Dialect const& _dialect, std::shared_ptr const& _debugData, YulString _mpos) { BuiltinFunction const* memoryLoadFunction = _dialect.memoryLoadFunction(_dialect.defaultType); yulAssert(memoryLoadFunction, ""); return FunctionCall{ - _loc, - Identifier{_loc, memoryLoadFunction->name}, { + _debugData, + Identifier{_debugData, memoryLoadFunction->name}, { Literal{ - _loc, + _debugData, LiteralKind::Number, _mpos, {} @@ -123,39 +123,38 @@ void StackToMemoryMover::operator()(Block& _block) if (!leftHandSideNeedsMoving) return {}; - langutil::SourceLocation loc = _stmt.location; - if (_variables.size() == 1) { optional offset = m_memoryOffsetTracker(_variables.front().name); yulAssert(offset, ""); return generateMemoryStore( m_context.dialect, - loc, + _stmt.debugData, *offset, - _stmt.value ? *std::move(_stmt.value) : Literal{loc, LiteralKind::Number, "0"_yulstring, {}} + _stmt.value ? *std::move(_stmt.value) : Literal{_stmt.debugData, LiteralKind::Number, "0"_yulstring, {}} ); } - VariableDeclaration tempDecl{loc, {}, std::move(_stmt.value)}; + VariableDeclaration tempDecl{_stmt.debugData, {}, std::move(_stmt.value)}; vector memoryAssignments; vector variableAssignments; for (auto& var: _variables) { YulString tempVarName = m_nameDispenser.newName(var.name); - tempDecl.variables.emplace_back(TypedName{var.location, tempVarName, {}}); + tempDecl.variables.emplace_back(TypedName{var.debugData, tempVarName, {}}); if (optional offset = m_memoryOffsetTracker(var.name)) memoryAssignments += generateMemoryStore( m_context.dialect, - loc, + _stmt.debugData, *offset, - Identifier{loc, tempVarName} + Identifier{_stmt.debugData, tempVarName} ); else variableAssignments.emplace_back(StatementType{ - loc, {move(var)}, - make_unique(Identifier{loc, tempVarName}) + _stmt.debugData, + {move(var)}, + make_unique(Identifier{_stmt.debugData, tempVarName}) }); } std::vector result; @@ -191,7 +190,7 @@ void StackToMemoryMover::visit(Expression& _expression) ASTModifier::visit(_expression); if (Identifier* identifier = std::get_if(&_expression)) if (optional offset = m_memoryOffsetTracker(identifier->name)) - _expression = generateMemoryLoad(m_context.dialect, identifier->location, *offset); + _expression = generateMemoryLoad(m_context.dialect, identifier->debugData, *offset); } optional StackToMemoryMover::VariableMemoryOffsetTracker::operator()(YulString _variable) const diff --git a/libyul/optimiser/UnusedFunctionsCommon.cpp b/libyul/optimiser/UnusedFunctionsCommon.cpp index db2ca5dee..161a51b97 100644 --- a/libyul/optimiser/UnusedFunctionsCommon.cpp +++ b/libyul/optimiser/UnusedFunctionsCommon.cpp @@ -40,33 +40,31 @@ FunctionDefinition unusedFunctionsCommon::createLinkingFunction( auto generateTypedName = [&](TypedName t) { return TypedName{ - t.location, + t.debugData, _nameDispenser.newName(t.name), t.type }; }; - langutil::SourceLocation loc = _original.location; - FunctionDefinition linkingFunction{ - loc, + _original.debugData, _linkingFunctionName, util::applyMap(_original.parameters, generateTypedName), util::applyMap(_original.returnVariables, generateTypedName), - {loc, {}} // body + {_original.debugData, {}} // body }; - FunctionCall call{loc, Identifier{loc, _originalFunctionName}, {}}; + FunctionCall call{_original.debugData, Identifier{_original.debugData, _originalFunctionName}, {}}; for (auto const& p: filter(linkingFunction.parameters, _usedParametersAndReturns.first)) - call.arguments.emplace_back(Identifier{loc, p.name}); + call.arguments.emplace_back(Identifier{_original.debugData, p.name}); - Assignment assignment{loc, {}, nullptr}; + Assignment assignment{_original.debugData, {}, nullptr}; for (auto const& r: filter(linkingFunction.returnVariables, _usedParametersAndReturns.second)) - assignment.variableNames.emplace_back(Identifier{loc, r.name}); + assignment.variableNames.emplace_back(Identifier{_original.debugData, r.name}); if (assignment.variableNames.empty()) - linkingFunction.body.statements.emplace_back(ExpressionStatement{loc, std::move(call)}); + linkingFunction.body.statements.emplace_back(ExpressionStatement{_original.debugData, std::move(call)}); else { assignment.value = std::make_unique(std::move(call)); diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index b0b4b794c..dae4d4c6f 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -72,7 +72,7 @@ void UnusedPruner::operator()(Block& _block) if (!used(funDef.name)) { subtractReferences(ReferencesCounter::countReferences(funDef.body)); - statement = Block{std::move(funDef.location), {}}; + statement = Block{std::move(funDef.debugData), {}}; } } else if (holds_alternative(statement)) @@ -90,19 +90,19 @@ void UnusedPruner::operator()(Block& _block) )) { if (!varDecl.value) - statement = Block{std::move(varDecl.location), {}}; + statement = Block{std::move(varDecl.debugData), {}}; else if ( SideEffectsCollector(m_dialect, *varDecl.value, m_functionSideEffects). canBeRemoved(m_allowMSizeOptimization) ) { subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); - statement = Block{std::move(varDecl.location), {}}; + statement = Block{std::move(varDecl.debugData), {}}; } else if (varDecl.variables.size() == 1 && m_dialect.discardFunction(varDecl.variables.front().type)) - statement = ExpressionStatement{varDecl.location, FunctionCall{ - varDecl.location, - {varDecl.location, m_dialect.discardFunction(varDecl.variables.front().type)->name}, + statement = ExpressionStatement{varDecl.debugData, FunctionCall{ + varDecl.debugData, + {varDecl.debugData, m_dialect.discardFunction(varDecl.variables.front().type)->name}, {*std::move(varDecl.value)} }}; } @@ -116,7 +116,7 @@ void UnusedPruner::operator()(Block& _block) ) { subtractReferences(ReferencesCounter::countReferences(exprStmt.expression)); - statement = Block{std::move(exprStmt.location), {}}; + statement = Block{std::move(exprStmt.debugData), {}}; } } diff --git a/libyul/optimiser/VarDeclInitializer.cpp b/libyul/optimiser/VarDeclInitializer.cpp index dc3e27c2a..9b96bef3b 100644 --- a/libyul/optimiser/VarDeclInitializer.cpp +++ b/libyul/optimiser/VarDeclInitializer.cpp @@ -47,11 +47,10 @@ void VarDeclInitializer::operator()(Block& _block) else { OptionalStatements ret{vector{}}; - langutil::SourceLocation loc{std::move(_varDecl.location)}; for (auto& var: _varDecl.variables) { unique_ptr expr = make_unique(m_dialect.zeroLiteralForType(var.type)); - ret->emplace_back(VariableDeclaration{loc, {std::move(var)}, std::move(expr)}); + ret->emplace_back(VariableDeclaration{std::move(_varDecl.debugData), {std::move(var)}, std::move(expr)}); } return ret; } diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index 547f665b9..35dd0de26 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include diff --git a/test/libyul/EwasmTranslationTest.cpp b/test/libyul/EwasmTranslationTest.cpp index 15ddfb86e..3c5e6cacb 100644 --- a/test/libyul/EwasmTranslationTest.cpp +++ b/test/libyul/EwasmTranslationTest.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index b4a848267..6bb34064e 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -24,11 +24,11 @@ #include #include +#include #include #include #include #include -#include #include #include #include diff --git a/test/libyul/SyntaxTest.cpp b/test/libyul/SyntaxTest.cpp index 21d295058..4668773e9 100644 --- a/test/libyul/SyntaxTest.cpp +++ b/test/libyul/SyntaxTest.cpp @@ -18,7 +18,6 @@ #include #include -#include #include #include diff --git a/test/libyul/YulInterpreterTest.cpp b/test/libyul/YulInterpreterTest.cpp index 11e85d66b..f275e38a4 100644 --- a/test/libyul/YulInterpreterTest.cpp +++ b/test/libyul/YulInterpreterTest.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include diff --git a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp index 4a097d2b7..6c7bc61cb 100644 --- a/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/strictasm_diff_ossfuzz.cpp @@ -17,14 +17,12 @@ // SPDX-License-Identifier: GPL-3.0 #include -#include #include #include #include #include #include -#include #include #include diff --git a/test/tools/yulrun.cpp b/test/tools/yulrun.cpp index 3cc16c56f..9f764f539 100644 --- a/test/tools/yulrun.cpp +++ b/test/tools/yulrun.cpp @@ -22,14 +22,12 @@ #include #include -#include #include #include #include #include #include -#include #include #include From f9c94d7c427dfd8dd27dd388f8d48739684f3a35 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 7 May 2020 14:46:47 +0200 Subject: [PATCH 036/235] Note function entry points. --- Changelog.md | 1 + docs/using-the-compiler.rst | 12 ++++ libevmasm/Assembly.cpp | 32 +++++++++-- libevmasm/Assembly.h | 14 ++++- libevmasm/LinkerObject.h | 11 ++++ libsolidity/codegen/CompilerContext.cpp | 20 ++++++- libsolidity/codegen/CompilerContext.h | 5 +- libsolidity/interface/StandardCompiler.cpp | 29 +++++++++- libsolidity/interface/StandardCompiler.h | 4 ++ libyul/backends/evm/AbstractAssembly.h | 3 +- libyul/backends/evm/EVMAssembly.cpp | 2 +- libyul/backends/evm/EVMAssembly.h | 2 +- libyul/backends/evm/EVMCodeTransform.cpp | 2 +- libyul/backends/evm/EthAssemblyAdapter.cpp | 4 +- libyul/backends/evm/EthAssemblyAdapter.h | 2 +- libyul/backends/evm/NoOutputAssembly.cpp | 2 +- libyul/backends/evm/NoOutputAssembly.h | 2 +- solc/CommandLineInterface.cpp | 12 ++++ test/cmdlineTests/function_debug_info/args | 1 + test/cmdlineTests/function_debug_info/err | 1 + .../function_debug_info/input.sol | 11 ++++ test/cmdlineTests/function_debug_info/output | 57 +++++++++++++++++++ .../function_debug_info_via_yul/args | 1 + .../function_debug_info_via_yul/err | 1 + .../function_debug_info_via_yul/input.sol | 7 +++ .../function_debug_info_via_yul/output | 11 ++++ .../standard_function_debug_info/input.json | 18 ++++++ .../standard_function_debug_info/output.json | 1 + test/cmdlineTests/standard_yul/output.json | 2 +- .../standard_yul_object/output.json | 2 +- .../standard_yul_object_name/output.json | 2 +- .../standard_yul_optimiserSteps/output.json | 2 +- .../standard_yul_optimized/output.json | 2 +- 33 files changed, 252 insertions(+), 26 deletions(-) create mode 100644 test/cmdlineTests/function_debug_info/args create mode 100644 test/cmdlineTests/function_debug_info/err create mode 100644 test/cmdlineTests/function_debug_info/input.sol create mode 100644 test/cmdlineTests/function_debug_info/output create mode 100644 test/cmdlineTests/function_debug_info_via_yul/args create mode 100644 test/cmdlineTests/function_debug_info_via_yul/err create mode 100644 test/cmdlineTests/function_debug_info_via_yul/input.sol create mode 100644 test/cmdlineTests/function_debug_info_via_yul/output create mode 100644 test/cmdlineTests/standard_function_debug_info/input.json create mode 100644 test/cmdlineTests/standard_function_debug_info/output.json diff --git a/Changelog.md b/Changelog.md index 0179f7524..87f108a3f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Language Features: Compiler Features: + * Standard JSON / combined JSON: New artifact "functionDebugData" that contains bytecode offsets of entry points of functions and potentially more information in the future. * Yul Optimizer: Evaluate ``keccak256(a, c)``, when the value at memory location ``a`` is known at compile time and ``c`` is a constant ``<= 32``. diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 1ccebf359..4e66b8f34 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -344,6 +344,7 @@ Input Description // storageLayout - Slots, offsets and types of the contract's state variables. // evm.assembly - New assembly format // evm.legacyAssembly - Old-style assembly format in JSON + // evm.bytecode.functionDebugData - Debugging information at function level // evm.bytecode.object - Bytecode object // evm.bytecode.opcodes - Opcodes list // evm.bytecode.sourceMap - Source mapping (useful for debugging) @@ -476,6 +477,17 @@ Output Description "legacyAssembly": {}, // Bytecode and related details. "bytecode": { + // Debugging data at the level of functions. + "functionDebugData": { + // Now follows a set of functions including compiler-internal and + // user-defined function. The set does not have to be complete. + "@mint_13": { // Internal name of the function + "entryPoint": 128, // Byte offset into the bytecode where the function starts (optional) + "id": 13, // AST ID of the function definition or null for compiler-internal functions (optional) + "parameterSlots": 2, // Number of EVM stack slots for the function parameters (optional) + "returnSlots": 1 // Number of EVM stack slots for the return values (optional) + } + }, // The bytecode as a hex string. "object": "00fe", // Opcodes list (string) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 50be995a8..f63f3ede6 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -348,12 +348,18 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) return root; } -AssemblyItem Assembly::namedTag(string const& _name) +AssemblyItem Assembly::namedTag(string const& _name, size_t _params, size_t _returns, optional _sourceID) { assertThrow(!_name.empty(), AssemblyException, "Empty named tag."); - if (!m_namedTags.count(_name)) - m_namedTags[_name] = static_cast(newTag().data()); - return AssemblyItem{Tag, m_namedTags.at(_name)}; + if (m_namedTags.count(_name)) + { + assertThrow(m_namedTags.at(_name).params == _params, AssemblyException, ""); + assertThrow(m_namedTags.at(_name).returns == _returns, AssemblyException, ""); + assertThrow(m_namedTags.at(_name).sourceID == _sourceID, AssemblyException, ""); + } + else + m_namedTags[_name] = {static_cast(newTag().data()), _sourceID, _params, _returns}; + return AssemblyItem{Tag, m_namedTags.at(_name).id}; } AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier) @@ -722,13 +728,16 @@ LinkerObject const& Assembly::assemble() const ret.bytecode.resize(ret.bytecode.size() + 20); break; case Tag: + { assertThrow(i.data() != 0, AssemblyException, "Invalid tag position."); assertThrow(i.splitForeignPushTag().first == numeric_limits::max(), AssemblyException, "Foreign tag."); + size_t tagId = static_cast(i.data()); assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large."); - assertThrow(m_tagPositionsInBytecode[static_cast(i.data())] == numeric_limits::max(), AssemblyException, "Duplicate tag position."); - m_tagPositionsInBytecode[static_cast(i.data())] = ret.bytecode.size(); + assertThrow(m_tagPositionsInBytecode[tagId] == numeric_limits::max(), AssemblyException, "Duplicate tag position."); + m_tagPositionsInBytecode[tagId] = ret.bytecode.size(); ret.bytecode.push_back(static_cast(Instruction::JUMPDEST)); break; + } default: assertThrow(false, InvalidOpcode, "Unexpected opcode while assembling."); } @@ -770,6 +779,17 @@ LinkerObject const& Assembly::assemble() const bytesRef r(ret.bytecode.data() + i.first, bytesPerTag); toBigEndian(pos, r); } + for (auto const& [name, tagInfo]: m_namedTags) + { + size_t position = m_tagPositionsInBytecode.at(tagInfo.id); + ret.functionDebugData[name] = { + position == numeric_limits::max() ? nullopt : optional{position}, + tagInfo.sourceID, + tagInfo.params, + tagInfo.returns + }; + } + for (auto const& dataItem: m_data) { auto references = dataRef.equal_range(dataItem.first); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index e16023ad9..cce06ccb7 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -35,6 +35,7 @@ #include #include #include +#include namespace solidity::evmasm { @@ -49,7 +50,7 @@ public: AssemblyItem newTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(Tag, m_usedTags++); } AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); } /// Returns a tag identified by the given name. Creates it if it does not yet exist. - AssemblyItem namedTag(std::string const& _name); + AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional _sourceID); AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); } bytes const& data(util::h256 const& _i) const { return m_data.at(_i); } AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); } @@ -184,7 +185,16 @@ private: protected: /// 0 is reserved for exception unsigned m_usedTags = 1; - std::map m_namedTags; + + struct NamedTagInfo + { + size_t id; + std::optional sourceID; + size_t params; + size_t returns; + }; + + std::map m_namedTags; AssemblyItems m_items; std::map m_data; /// Data that is appended to the very end of the contract. diff --git a/libevmasm/LinkerObject.h b/libevmasm/LinkerObject.h index 03d01cc2c..e512dd7ab 100644 --- a/libevmasm/LinkerObject.h +++ b/libevmasm/LinkerObject.h @@ -45,6 +45,17 @@ struct LinkerObject /// to a list of offsets into the bytecode that refer to their values. std::map>> immutableReferences; + struct FunctionDebugData + { + std::optional bytecodeOffset; + std::optional sourceID; + size_t params = {}; + size_t returns = {}; + }; + + /// Bytecode offsets of named tags like function entry points. + std::map functionDebugData; + /// Appends the bytecode of @a _other and incorporates its link references. void append(LinkerObject const& _other); diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ab1a67b96..35ff45965 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -148,7 +148,7 @@ void CompilerContext::callYulFunction( m_externallyUsedYulFunctions.insert(_name); auto const retTag = pushNewTag(); CompilerUtils(*this).moveIntoStack(_inArgs); - appendJumpTo(namedTag(_name), evmasm::AssemblyItem::JumpType::IntoFunction); + appendJumpTo(namedTag(_name, _inArgs, _outArgs, {}), evmasm::AssemblyItem::JumpType::IntoFunction); adjustStackOffset(static_cast(_outArgs) - 1 - static_cast(_inArgs)); *this << retTag.tag(); } @@ -596,7 +596,23 @@ evmasm::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabel( auto res = m_entryLabels.find(&_declaration); if (res == m_entryLabels.end()) { - evmasm::AssemblyItem tag(_context.newTag()); + size_t params = 0; + size_t returns = 0; + if (auto const* function = dynamic_cast(&_declaration)) + { + FunctionType functionType(*function, FunctionType::Kind::Internal); + params = CompilerUtils::sizeOnStack(functionType.parameterTypes()); + returns = CompilerUtils::sizeOnStack(functionType.returnParameterTypes()); + } + + // some name that cannot clash with yul function names. + string labelName = "@" + _declaration.name() + "_" + to_string(_declaration.id()); + evmasm::AssemblyItem tag = _context.namedTag( + labelName, + params, + returns, + _declaration.id() + ); m_entryLabels.insert(make_pair(&_declaration, tag)); m_functionsToCompile.push(&_declaration); return tag.tag(); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index bdf9abc7d..e4358ccb5 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -216,7 +216,10 @@ public: /// @returns a new tag without pushing any opcodes or data evmasm::AssemblyItem newTag() { return m_asm->newTag(); } /// @returns a new tag identified by name. - evmasm::AssemblyItem namedTag(std::string const& _name) { return m_asm->namedTag(_name); } + evmasm::AssemblyItem namedTag(std::string const& _name, size_t _params, size_t _returns, std::optional _sourceID) + { + return m_asm->namedTag(_name, _params, _returns, _sourceID); + } /// Adds a subroutine to the code (in the data section) and pushes its size (via a tag) /// on the stack. @returns the pushsub assembly item. evmasm::AssemblyItem addSubroutine(evmasm::AssemblyPointer const& _assembly) { return m_asm->appendSubroutine(_assembly); } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 88857203f..ba9e5e23e 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -241,7 +241,7 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _fil vector evmObjectComponents(string const& _objectKind) { solAssert(_objectKind == "bytecode" || _objectKind == "deployedBytecode", ""); - vector components{"", ".object", ".opcodes", ".sourceMap", ".generatedSources", ".linkReferences"}; + vector components{"", ".object", ".opcodes", ".sourceMap", ".functionDebugData", ".generatedSources", ".linkReferences"}; if (_objectKind == "deployedBytecode") components.push_back(".immutableReferences"); return util::applyMap(components, [&](auto const& _s) { return "evm." + _objectKind + _s; }); @@ -388,6 +388,8 @@ Json::Value collectEVMObject( output["opcodes"] = evmasm::disassemble(_object.bytecode); if (_artifactRequested("sourceMap")) output["sourceMap"] = _sourceMap ? *_sourceMap : ""; + if (_artifactRequested("functionDebugData")) + output["functionDebugData"] = StandardCompiler::formatFunctionDebugData(_object.functionDebugData); if (_artifactRequested("linkReferences")) output["linkReferences"] = formatLinkReferences(_object.linkReferences); if (_runtimeObject && _artifactRequested("immutableReferences")) @@ -614,6 +616,7 @@ std::variant parseOptimizerSettings(Json::Value } + std::variant StandardCompiler::parseInput(Json::Value const& _input) { InputsAndSettings ret; @@ -1423,3 +1426,27 @@ string StandardCompiler::compile(string const& _input) noexcept return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}"; } } + +Json::Value StandardCompiler::formatFunctionDebugData( + map const& _debugInfo +) +{ + Json::Value ret(Json::objectValue); + for (auto const& [name, info]: _debugInfo) + { + Json::Value fun; + if (info.sourceID) + fun["id"] = Json::UInt64(*info.sourceID); + else + fun["id"] = Json::nullValue; + if (info.bytecodeOffset) + fun["entryPoint"] = Json::UInt64(*info.bytecodeOffset); + else + fun["entryPoint"] = Json::nullValue; + fun["parameterSlots"] = Json::UInt64(info.params); + fun["returnSlots"] = Json::UInt64(info.returns); + ret[name] = move(fun); + } + + return ret; +} diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 0eba42789..da193b3a1 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -58,6 +58,10 @@ public: /// output. Parsing errors are returned as regular errors. std::string compile(std::string const& _input) noexcept; + static Json::Value formatFunctionDebugData( + std::map const& _debugInfo + ); + private: struct InputsAndSettings { diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index 18d36953a..ef5c73c67 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -30,6 +30,7 @@ #include #include +#include namespace solidity::langutil { @@ -74,7 +75,7 @@ public: /// Generate a new unique label. virtual LabelID newLabelId() = 0; /// Returns a label identified by the given name. Creates it if it does not yet exist. - virtual LabelID namedLabel(std::string const& _name) = 0; + virtual LabelID namedLabel(std::string const& _name, size_t _params, size_t _returns, std::optional _sourceID) = 0; /// Append a reference to a to-be-linked symbol. /// Currently, we assume that the value is always a 20 byte number. virtual void appendLinkerSymbol(std::string const& _name) = 0; diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp index 63f54419f..2e843b268 100644 --- a/libyul/backends/evm/EVMAssembly.cpp +++ b/libyul/backends/evm/EVMAssembly.cpp @@ -78,7 +78,7 @@ EVMAssembly::LabelID EVMAssembly::newLabelId() return m_nextLabelId++; } -AbstractAssembly::LabelID EVMAssembly::namedLabel(string const& _name) +AbstractAssembly::LabelID EVMAssembly::namedLabel(string const& _name, size_t, size_t, std::optional) { yulAssert(!_name.empty(), ""); if (!m_namedLabels.count(_name)) diff --git a/libyul/backends/evm/EVMAssembly.h b/libyul/backends/evm/EVMAssembly.h index 00cea1208..25c2765f5 100644 --- a/libyul/backends/evm/EVMAssembly.h +++ b/libyul/backends/evm/EVMAssembly.h @@ -58,7 +58,7 @@ public: /// Generate a new unique label. LabelID newLabelId() override; /// Returns a label identified by the given name. Creates it if it does not yet exist. - LabelID namedLabel(std::string const& _name) override; + LabelID namedLabel(std::string const& _name, size_t _params, size_t _returns, std::optional _sourceID) override; /// Append a reference to a to-be-linked symbol. /// Currently, we assume that the value is always a 20 byte number. void appendLinkerSymbol(std::string const& _name) override; diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 49bfed968..c54a5e9a9 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -578,7 +578,7 @@ AbstractAssembly::LabelID CodeTransform::functionEntryID(YulString _name, Scope: { AbstractAssembly::LabelID id = m_useNamedLabelsForFunctions ? - m_assembly.namedLabel(_name.str()) : + m_assembly.namedLabel(_name.str(), _function.arguments.size(), _function.returns.size(), {}) : m_assembly.newLabelId(); m_context->functionEntryIDs[&_function] = id; } diff --git a/libyul/backends/evm/EthAssemblyAdapter.cpp b/libyul/backends/evm/EthAssemblyAdapter.cpp index ed2c81777..85af84fc3 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.cpp +++ b/libyul/backends/evm/EthAssemblyAdapter.cpp @@ -84,9 +84,9 @@ size_t EthAssemblyAdapter::newLabelId() return assemblyTagToIdentifier(m_assembly.newTag()); } -size_t EthAssemblyAdapter::namedLabel(std::string const& _name) +size_t EthAssemblyAdapter::namedLabel(std::string const& _name, size_t _params, size_t _returns, std::optional _sourceID) { - return assemblyTagToIdentifier(m_assembly.namedTag(_name)); + return assemblyTagToIdentifier(m_assembly.namedTag(_name, _params, _returns, _sourceID)); } void EthAssemblyAdapter::appendLinkerSymbol(std::string const& _linkerSymbol) diff --git a/libyul/backends/evm/EthAssemblyAdapter.h b/libyul/backends/evm/EthAssemblyAdapter.h index 26c256d5b..e40719020 100644 --- a/libyul/backends/evm/EthAssemblyAdapter.h +++ b/libyul/backends/evm/EthAssemblyAdapter.h @@ -46,7 +46,7 @@ public: void appendLabel(LabelID _labelId) override; void appendLabelReference(LabelID _labelId) override; size_t newLabelId() override; - size_t namedLabel(std::string const& _name) override; + size_t namedLabel(std::string const& _name, size_t _params, size_t _returns, std::optional _sourceID) override; void appendLinkerSymbol(std::string const& _linkerSymbol) override; void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables) override; void appendJump(int _stackDiffAfter, JumpType _jumpType) override; diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index 50cbf1c78..c8016d4a5 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -59,7 +59,7 @@ NoOutputAssembly::LabelID NoOutputAssembly::newLabelId() return 1; } -AbstractAssembly::LabelID NoOutputAssembly::namedLabel(string const&) +AbstractAssembly::LabelID NoOutputAssembly::namedLabel(string const&, size_t, size_t, optional) { return 1; } diff --git a/libyul/backends/evm/NoOutputAssembly.h b/libyul/backends/evm/NoOutputAssembly.h index fbb51a9c6..ebd83fe57 100644 --- a/libyul/backends/evm/NoOutputAssembly.h +++ b/libyul/backends/evm/NoOutputAssembly.h @@ -56,7 +56,7 @@ public: void appendLabel(LabelID _labelId) override; void appendLabelReference(LabelID _labelId) override; LabelID newLabelId() override; - LabelID namedLabel(std::string const& _name) override; + LabelID namedLabel(std::string const& _name, size_t _params, size_t _returns, std::optional _sourceID) override; void appendLinkerSymbol(std::string const& _name) override; void appendVerbatim(bytes _data, size_t _arguments, size_t _returnVariables) override; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 79587d78d..30a193403 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -189,6 +189,8 @@ static string const g_strSources = "sources"; static string const g_strSourceList = "sourceList"; static string const g_strSrcMap = "srcmap"; static string const g_strSrcMapRuntime = "srcmap-runtime"; +static string const g_strFunDebug = "function-debug"; +static string const g_strFunDebugRuntime = "function-debug-runtime"; static string const g_strStandardJSON = "standard-json"; static string const g_strStrictAssembly = "strict-assembly"; static string const g_strSwarm = "swarm"; @@ -257,6 +259,8 @@ static set const g_combinedJsonArgs g_strBinary, g_strBinaryRuntime, g_strCompactJSON, + g_strFunDebug, + g_strFunDebugRuntime, g_strGeneratedSources, g_strGeneratedSourcesRuntime, g_strInterface, @@ -1672,6 +1676,14 @@ void CommandLineInterface::handleCombinedJSON() auto map = m_compiler->runtimeSourceMapping(contractName); contractData[g_strSrcMapRuntime] = map ? *map : ""; } + if (requests.count(g_strFunDebug) && m_compiler->compilationSuccessful()) + contractData[g_strFunDebug] = StandardCompiler::formatFunctionDebugData( + m_compiler->object(contractName).functionDebugData + ); + if (requests.count(g_strFunDebugRuntime) && m_compiler->compilationSuccessful()) + contractData[g_strFunDebugRuntime] = StandardCompiler::formatFunctionDebugData( + m_compiler->runtimeObject(contractName).functionDebugData + ); if (requests.count(g_strSignatureHashes)) contractData[g_strSignatureHashes] = m_compiler->methodIdentifiers(contractName); if (requests.count(g_strNatspecDev)) diff --git a/test/cmdlineTests/function_debug_info/args b/test/cmdlineTests/function_debug_info/args new file mode 100644 index 000000000..ff730a4ce --- /dev/null +++ b/test/cmdlineTests/function_debug_info/args @@ -0,0 +1 @@ +--optimize --combined-json function-debug,function-debug-runtime --pretty-json diff --git a/test/cmdlineTests/function_debug_info/err b/test/cmdlineTests/function_debug_info/err new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/cmdlineTests/function_debug_info/err @@ -0,0 +1 @@ + diff --git a/test/cmdlineTests/function_debug_info/input.sol b/test/cmdlineTests/function_debug_info/input.sol new file mode 100644 index 000000000..7b4c59676 --- /dev/null +++ b/test/cmdlineTests/function_debug_info/input.sol @@ -0,0 +1,11 @@ +pragma solidity >=0.0; +// SPDX-License-Identifier: GPL-3.0 +contract C { + function f(uint[] calldata x) pure external returns (uint) { return x[0]; } + // This will be optimized out + function g() pure internal {} + mapping(uint => uint) public t; + constructor(uint x) { + t[0] = x; + } +} diff --git a/test/cmdlineTests/function_debug_info/output b/test/cmdlineTests/function_debug_info/output new file mode 100644 index 000000000..4889b15ea --- /dev/null +++ b/test/cmdlineTests/function_debug_info/output @@ -0,0 +1,57 @@ +{ + "contracts": + { + "function_debug_info/input.sol:C": + { + "function-debug": + { + "@_34": + { + "id": 34, + "parameterSlots": 1, + "returnSlots": 0 + }, + "abi_decode_tuple_t_uint256_fromMemory": + { + "entryPoint": 94, + "parameterSlots": 2, + "returnSlots": 1 + } + }, + "function-debug-runtime": + { + "@f_14": + { + "entryPoint": 128, + "id": 14, + "parameterSlots": 2, + "returnSlots": 1 + }, + "@t_22": + { + "id": 22, + "parameterSlots": 0, + "returnSlots": 0 + }, + "abi_decode_tuple_t_array$_t_uint256_$dyn_calldata_ptr": + { + "entryPoint": 178, + "parameterSlots": 2, + "returnSlots": 2 + }, + "abi_decode_tuple_t_uint256": + { + "entryPoint": 295, + "parameterSlots": 2, + "returnSlots": 1 + }, + "abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed": + { + "parameterSlots": 2, + "returnSlots": 1 + } + } + } + }, + "version": "" +} diff --git a/test/cmdlineTests/function_debug_info_via_yul/args b/test/cmdlineTests/function_debug_info_via_yul/args new file mode 100644 index 000000000..ab626e663 --- /dev/null +++ b/test/cmdlineTests/function_debug_info_via_yul/args @@ -0,0 +1 @@ +--experimental-via-ir --optimize --combined-json function-debug,function-debug-runtime --pretty-json diff --git a/test/cmdlineTests/function_debug_info_via_yul/err b/test/cmdlineTests/function_debug_info_via_yul/err new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/cmdlineTests/function_debug_info_via_yul/err @@ -0,0 +1 @@ + diff --git a/test/cmdlineTests/function_debug_info_via_yul/input.sol b/test/cmdlineTests/function_debug_info_via_yul/input.sol new file mode 100644 index 000000000..23040af0f --- /dev/null +++ b/test/cmdlineTests/function_debug_info_via_yul/input.sol @@ -0,0 +1,7 @@ +pragma solidity >=0.0; +// SPDX-License-Identifier: GPL-3.0 +contract C { + function f(uint[] calldata x) pure external returns (uint) { return x[0]; } + // This will be optimized out + function g() pure internal {} +} diff --git a/test/cmdlineTests/function_debug_info_via_yul/output b/test/cmdlineTests/function_debug_info_via_yul/output new file mode 100644 index 000000000..5af245ce4 --- /dev/null +++ b/test/cmdlineTests/function_debug_info_via_yul/output @@ -0,0 +1,11 @@ +{ + "contracts": + { + "function_debug_info_via_yul/input.sol:C": + { + "function-debug": {}, + "function-debug-runtime": {} + } + }, + "version": "" +} diff --git a/test/cmdlineTests/standard_function_debug_info/input.json b/test/cmdlineTests/standard_function_debug_info/input.json new file mode 100644 index 000000000..653c61151 --- /dev/null +++ b/test/cmdlineTests/standard_function_debug_info/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": { + "a.sol": { + "content": "// SPDX-License-Identifier: GPL-3.0\npragma solidity >=0.0; pragma abicoder v2; contract A { function f(uint[] memory x) public pure returns (uint256) { return x[0] + x[1];} }" + } + }, + "settings": { + "outputSelection": { + "*": { + "A": [ + "evm.deployedBytecode.functionDebugData", + "evm.bytecode.functionDebugData" + ] + } + } + } +} diff --git a/test/cmdlineTests/standard_function_debug_info/output.json b/test/cmdlineTests/standard_function_debug_info/output.json new file mode 100644 index 000000000..5dc180875 --- /dev/null +++ b/test/cmdlineTests/standard_function_debug_info/output.json @@ -0,0 +1 @@ +{"contracts":{"a.sol":{"A":{"evm":{"bytecode":{"functionDebugData":{}},"deployedBytecode":{"functionDebugData":{"@f_19":{"entryPoint":96,"id":19,"parameterSlots":1,"returnSlots":1},"abi_decode_available_length_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":247,"id":null,"parameterSlots":3,"returnSlots":1},"abi_decode_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":359,"id":null,"parameterSlots":2,"returnSlots":1},"abi_decode_t_uint256":{"entryPoint":405,"id":null,"parameterSlots":2,"returnSlots":1},"abi_decode_tuple_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":426,"id":null,"parameterSlots":2,"returnSlots":1},"abi_encode_t_uint256_to_t_uint256_fromStack":{"entryPoint":499,"id":null,"parameterSlots":2,"returnSlots":0},"abi_encode_tuple_t_uint256__to_t_uint256__fromStack_reversed":{"entryPoint":514,"id":null,"parameterSlots":2,"returnSlots":1},"allocate_memory":{"entryPoint":541,"id":null,"parameterSlots":1,"returnSlots":1},"allocate_unbounded":{"entryPoint":568,"id":null,"parameterSlots":0,"returnSlots":1},"array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr":{"entryPoint":578,"id":null,"parameterSlots":1,"returnSlots":1},"checked_add_t_uint256":{"entryPoint":622,"id":null,"parameterSlots":2,"returnSlots":1},"cleanup_t_uint256":{"entryPoint":708,"id":null,"parameterSlots":1,"returnSlots":1},"finalize_allocation":{"entryPoint":718,"id":null,"parameterSlots":2,"returnSlots":0},"panic_error_0x11":{"entryPoint":767,"id":null,"parameterSlots":0,"returnSlots":0},"panic_error_0x41":{"entryPoint":814,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_1b9f4a0a5773e33b91aa01db23bf8c55fce1411167c872835e7fa00a4f17d46d":{"entryPoint":861,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_81385d8c0b31fffe14be1da910c8bd3a80be4cfa248e04f42ec0faea3132a8ef":{"entryPoint":866,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_c1322bf8034eace5e0b5c7295db60986aa89aae5e0ea0873e4689e076861a5db":{"entryPoint":871,"id":null,"parameterSlots":0,"returnSlots":0},"revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b":{"entryPoint":876,"id":null,"parameterSlots":0,"returnSlots":0},"round_up_to_mul_of_32":{"entryPoint":881,"id":null,"parameterSlots":1,"returnSlots":1},"validator_revert_t_uint256":{"entryPoint":898,"id":null,"parameterSlots":1,"returnSlots":0}}}}}}},"sources":{"a.sol":{"id":0}}} diff --git a/test/cmdlineTests/standard_yul/output.json b/test/cmdlineTests/standard_yul/output.json index e6c6e1390..e9e49f0b5 100644 --- a/test/cmdlineTests/standard_yul/output.json +++ b/test/cmdlineTests/standard_yul/output.json @@ -14,7 +14,7 @@ sstore /* \"A\":0:42 */ pop -","bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"object\" { +","bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"object\" { code { let x := mload(0) sstore(add(x, 0), 0) diff --git a/test/cmdlineTests/standard_yul_object/output.json b/test/cmdlineTests/standard_yul_object/output.json index d4dcb7acd..0db26c4a1 100644 --- a/test/cmdlineTests/standard_yul_object/output.json +++ b/test/cmdlineTests/standard_yul_object/output.json @@ -13,7 +13,7 @@ pop stop data_4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 616263 -","bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"NamedObject\" { +","bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"NamedObject\" { code { let x := dataoffset(\"DataName\") sstore(add(x, 0), 0) diff --git a/test/cmdlineTests/standard_yul_object_name/output.json b/test/cmdlineTests/standard_yul_object_name/output.json index 0abd732e4..10139b5a2 100644 --- a/test/cmdlineTests/standard_yul_object_name/output.json +++ b/test/cmdlineTests/standard_yul_object_name/output.json @@ -22,7 +22,7 @@ sub_0: assembly { /* \"A\":137:149 */ revert } -","bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"deployedBytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"NamedObject\" { +","bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""},"deployedBytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"NamedObject\" { code { let x := dataoffset(\"DataName\") sstore(add(x, 0), 0) diff --git a/test/cmdlineTests/standard_yul_optimiserSteps/output.json b/test/cmdlineTests/standard_yul_optimiserSteps/output.json index 536ee0047..de08a24b7 100644 --- a/test/cmdlineTests/standard_yul_optimiserSteps/output.json +++ b/test/cmdlineTests/standard_yul_optimiserSteps/output.json @@ -13,7 +13,7 @@ /* \"A\":20:40 */ sstore pop -","bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"object\" { +","bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"object\" { code { let x := mload(0) sstore(add(x, 0), 0) diff --git a/test/cmdlineTests/standard_yul_optimized/output.json b/test/cmdlineTests/standard_yul_optimized/output.json index 758904e93..2aa40d5b7 100644 --- a/test/cmdlineTests/standard_yul_optimized/output.json +++ b/test/cmdlineTests/standard_yul_optimized/output.json @@ -5,7 +5,7 @@ mload /* \"A\":20:40 */ sstore -","bytecode":{"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"object\" { +","bytecode":{"functionDebugData":{},"generatedSources":[],"linkReferences":{},"object":"","opcodes":"","sourceMap":""}},"ir":"object \"object\" { code { let x := mload(0) sstore(add(x, 0), 0) From 6413e36a9a5487fbe6d9a5f6fba16b2f769fce95 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 19 Apr 2021 16:06:31 +0200 Subject: [PATCH 037/235] Rename file. --- docs/internals/{optimiser.rst => optimizer.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/internals/{optimiser.rst => optimizer.rst} (100%) diff --git a/docs/internals/optimiser.rst b/docs/internals/optimizer.rst similarity index 100% rename from docs/internals/optimiser.rst rename to docs/internals/optimizer.rst From 13eec106f4629049d9b89bba69735314b6a644cd Mon Sep 17 00:00:00 2001 From: franzihei Date: Mon, 12 Apr 2021 16:53:42 +0200 Subject: [PATCH 038/235] DOCS: update optimizer docs, harmonize spelling and add Yul-based optimizer module --- docs/index.rst | 2 +- docs/internals/optimizer.rst | 1150 +++++++++++++++++++++++++++++++++- docs/yul.rst | 7 +- libyul/optimiser/README.md | 652 +------------------ 4 files changed, 1139 insertions(+), 672 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index c167c134c..c7d8a341b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -149,7 +149,7 @@ Contents internals/layout_in_calldata.rst internals/variable_cleanup.rst internals/source_mappings.rst - internals/optimiser.rst + internals/optimizer.rst metadata.rst abi-spec.rst diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index edf5c4c70..d56c8adf9 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -1,22 +1,79 @@ -.. index:: optimizer, common subexpression elimination, constant propagation +.. index:: optimizer, optimiser, common subexpression elimination, constant propagation +.. _optimizer: ************* -The Optimiser +The Optimizer ************* -This section discusses the optimiser that was first added to Solidity, -which operates on opcode streams. For information on the new Yul-based optimiser, -please see the `readme on github `_. +The Solidity compiler uses two different optimizer modules: The "old" optimizer +that operates at opcode level and the "new" optimizer that operates on Yul IR code. -The Solidity optimiser operates on assembly. It splits the sequence of instructions into basic blocks -at ``JUMPs`` and ``JUMPDESTs``. Inside these blocks, the optimiser -analyses the instructions and records every modification to the stack, +The opcode-based optimizer applies a set of `simplification rules `_ +to opcodes. It also combines equal code sets and removes unused code. + +The Yul-based optimizer is much more powerful, because it can work across function +calls: In Yul, it is not possible to perform arbitrary jumps, so it is for example +possible to compute the side-effects of each function. Consider two function calls, +where the first does not modify the storage and the second modifies the storage. +If their arguments and return values does not depend on each other, we can reorder +the function calls. Similarly, if a function is +side-effect free and its result is multiplied by zero, you can remove the function +call completely. + +Currently, the parameter ``--optimize`` activates the opcode-based optimizer for the +generated bytecode and the Yul optimizer for the Yul code generated internally, for example for ABI coder v2. +One can use ``solc --ir-optimized --optimize`` to produce +optimized experimental Yul IR for a Solidity source. Similarly, use ``solc --strict-assembly --optimize`` +for a stand-alone Yul mode. + +You can find more details on both optimizer modules and their optimization steps below. + +Benefits of Optimizing Solidity Code +==================================== + +Overall, the optimizer tries to simplify complicated expressions, which reduces both code +size and execution cost, i.e., it can reduce gas needed for contract deployment as well as for external calls to the contract. +It also specializes or inlines functions. Especially +function inlining is an operation that can cause much bigger code, but it is +often done because it results in opportunities for more simplifications. + + +Differences between Optimized and Non-Optimized Code +==================================================== + +Generally, the most visible difference would be constant expressions getting evaluated. +When it comes to the ASM output, one can also notice reduction of equivalent/duplicate +"code blocks" (compare the output of the flags ``--asm`` and ``--asm --optimize``). However, +when it comes to the Yul/intermediate-representation, there can be significant +differences, for example, functions can get inlined, combined, rewritten to eliminate +redundancies, etc. (compare the output between the flags ``--ir`` and +``--optimize --ir-optimized``). + +Optimizer Parameter Runs +======================== + +The number of runs (``--optimize-runs``) specifies roughly how often each opcode of the +deployed code will be executed across the life-time of the contract. This means it is a +trade-off parameter between code size (deploy cost) and code execution cost (cost after deployment). +A "runs" parameter of "1" will produce short but expensive code. The largest value is ``2**32-1``. + +.. note:: + + A common misconception is that this parameter specifies the number of iterations of the optimizer. + This is not true: The optimizer will always run as many times as it can still improve the code. + +Opcode-Based Optimizer Module +============================= + +The opcode-based optimizer module operates on assembly. It splits the +sequence of instructions into basic blocks at ``JUMPs`` and ``JUMPDESTs``. +Inside these blocks, the optimizer analyzes the instructions and records every modification to the stack, memory, or storage as an expression which consists of an instruction and -a list of arguments which are pointers to other expressions. The optimiser +a list of arguments which are pointers to other expressions. The opcode-based optimizer uses a component called "CommonSubexpressionEliminator" that amongst other tasks, finds expressions that are always equal (on every input) and combines -them into an expression class. The optimiser first tries to find each new -expression in a list of already known expressions. If this does not work, +them into an expression class. It first tries to find each new +expression in a list of already known expressions. If no such matches are found, it simplifies the expression according to rules like ``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is a recursive process, we can also apply the latter rule if the second factor @@ -25,8 +82,8 @@ Modifications to storage and memory locations have to erase knowledge about storage and memory locations which are not known to be different. If we first write to location x and then to location y and both are input variables, the second could overwrite the first, so we do not know what is stored at x after -we wrote to y. If simplification of the expression x - y evaluates to a -non-zero constant, we know that we can keep our knowledge about what is stored at x. +we wrote to y. If simplification of the expression ``x - y`` evaluates to a +non-zero constant, we know that we can keep our knowledge about what is stored at ``x``. After this process, we know which expressions have to be on the stack at the end, and have a list of modifications to memory and storage. This information @@ -36,11 +93,11 @@ the next block(s). If we know the targets of all ``JUMP`` and ``JUMPI`` instruct we can build a complete control flow graph of the program. If there is only one target we do not know (this can happen as in principle, jump targets can be computed from inputs), we have to erase all knowledge about the input state -of a block as it can be the target of the unknown ``JUMP``. If the optimiser -finds a ``JUMPI`` whose condition evaluates to a constant, it transforms it +of a block as it can be the target of the unknown ``JUMP``. If the opcode-based +optimizer module finds a ``JUMPI`` whose condition evaluates to a constant, it transforms it to an unconditional jump. -As the last step, the code in each block is re-generated. The optimiser creates +As the last step, the code in each block is re-generated. The optimizer creates a dependency graph from the expressions on the stack at the end of the block, and it drops every operation that is not part of this graph. It generates code that applies the modifications to memory and storage in the order they were @@ -80,6 +137,7 @@ This corresponds to inlining of simple, small Solidity or Yul functions. In part function and behind ``tag`` there is a basic block (as described above for the "CommonSubexpressionEliminator") that ends in another ``JUMP`` which is marked as a jump "out of" a function. + In particular, consider the following prototypical example of assembly generated for a call to an internal Solidity function: @@ -110,7 +168,7 @@ the block at ``tag_f`` resulting in: ...body of function f... jump // out -Now ideally, the other optimiser steps described above will result in the return tag push being moved +Now ideally, the other optimizer steps described above will result in the return tag push being moved towards the remaining jump resulting in: .. code-block:: text @@ -138,4 +196,1060 @@ So the call to function ``f`` is inlined and the original definition of ``f`` ca Inlining like this is attempted, whenever a heuristics suggests that inlining is cheaper over the lifetime of a contract than not inlining. This heuristics depends on the size of the function body, the number of other references to its tag (approximating the number of calls to the function) and -the expected number of executions of the contract (the global optimiser parameter "runs"). +the expected number of executions of the contract (the global optimizer parameter "runs"). + + +Yul-Based Optimizer Module +========================== + +The Yul-based optimizer consists of several stages and components that all transform +the AST in a semantically equivalent way. The goal is to end up either with code +that is shorter or at least only marginally longer but will allow further +optimization steps. + +.. warning:: + + Since the optimizer is under heavy development, the information here might be outdated. + If you rely on a certain functionality, please reach out to the team directly. + +The optimizer currently follows a purely greedy strategy and does not do any +backtracking. + +All components of the Yul-based optimizer module are explained below. +The following transformation steps are the main components: + + - SSA Transform + - Common Subexpression Eliminator + - Expression Simplifier + - Redundant Assign Eliminator + - Full Function Inliner + +Optimizer Steps +--------------- + +This is a list of all steps the Yul-based optimizer sorted alphabetically. You can find more information +on the individual steps and their sequence below. + + - :ref:`block-flattener`. + - :ref:`circular-reference-pruner`. + - :ref:`common-subexpression-eliminator`. + - :ref:`conditional-simplifier`. + - :ref:`conditional-unsimplifier`. + - :ref:`control-flow-simplifier`. + - :ref:`dead-code-eliminator`. + - :ref:`equivalent-function-combiner`. + - :ref:`expression-joiner`. + - :ref:`expression-simplifier`. + - :ref:`expression-splitter`. + - :ref:`for-loop-condition-into-body`. + - :ref:`for-loop-condition-out-of-body`. + - :ref:`for-loop-init-rewriter`. + - :ref:`functional-inliner`. + - :ref:`function-grouper`. + - :ref:`function-hoister`. + - :ref:`function-specializer`. + - :ref:`literal-rematerialiser`. + - :ref:`load-resolver`. + - :ref:`loop-invariant-code-motion`. + - :ref:`redundant-assign-eliminator`. + - :ref:`reasoning-based-simplifier`. + - :ref:`rematerialiser`. + - :ref:`SSA-reverser`. + - :ref:`SSA-transform`. + - :ref:`structural-simplifier`. + - :ref:`unused-function-parameter-pruner`. + - :ref:`unused-pruner`. + - :ref:`var-decl-initializer`. + +Selecting Optimizations +----------------------- + +By default the optimizer applies its predefined sequence of optimization steps to +the generated assembly. You can override this sequence and supply your own using +the ``--yul-optimizations`` option: + +.. code-block:: text + + bash + solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul' + +The sequence inside ``[...]`` will be applied multiple times in a loop until the Yul code +remains unchanged or until the maximum number of rounds (currently 12) has been reached. + +Available abbreviations are listed in the `Yul optimizer docs `_. + +Preprocessing +------------- + +The preprocessing components perform transformations to get the program +into a certain normal form that is easier to work with. This normal +form is kept during the rest of the optimization process. + +.. _disambiguator: + +Disambiguator +^^^^^^^^^^^^^ + +The disambiguator takes an AST and returns a fresh copy where all identifiers have +unique names in the input AST. This is a prerequisite for all other optimizer stages. +One of the benefits is that identifier lookup does not need to take scopes into account +which simplifies the analysis needed for other steps. + +All subsequent stages have the property that all names stay unique. This means if +a new identifier needs to be introduced, a new unique name is generated. + +.. _function-hoister: + +FunctionHoister +^^^^^^^^^^^^^^^ + +The function hoister moves all function definitions to the end of the topmost block. This is +a semantically equivalent transformation as long as it is performed after the +disambiguation stage. The reason is that moving a definition to a higher-level block cannot decrease +its visibility and it is impossible to reference variables defined in a different function. + +The benefit of this stage is that function definitions can be looked up more easily +and functions can be optimized in isolation without having to traverse the AST completely. + +.. _function-grouper: + +FunctionGrouper +^^^^^^^^^^^^^^^ + +The function grouper has to be applied after the disambiguator and the function hoister. +Its effect is that all topmost elements that are not function definitions are moved +into a single block which is the first statement of the root block. + +After this step, a program has the following normal form: + +.. code-block:: text + + { I F... } + +Where ``I`` is a (potentially empty) block that does not contain any function definitions (not even recursively) +and ``F`` is a list of function definitions such that no function contains a function definition. + +The benefit of this stage is that we always know where the list of function begins. + +.. _for-loop-condition-into-body: + +ForLoopConditionIntoBody +^^^^^^^^^^^^^^^^^^^^^^^^ + +This transformation moves the loop-iteration condition of a for-loop into loop body. +We need this transformation because :ref:`expression-splitter` will not +apply to iteration condition expressions (the ``C`` in the following example). + +.. code-block:: text + + for { Init... } C { Post... } { + Body... + } + +is transformed to + +.. code-block:: text + + for { Init... } 1 { Post... } { + if iszero(C) { break } + Body... + } + +This transformation can also be useful when paired with ``LoopInvariantCodeMotion``, since +invariants in the loop-invariant conditions can then be taken outside the loop. + +.. _for-loop-init-rewriter: + +ForLoopInitRewriter +^^^^^^^^^^^^^^^^^^^ + +This transformation moves the initialization part of a for-loop to before +the loop: + +.. code-block:: text + + for { Init... } C { Post... } { + Body... + } + +is transformed to + +.. code-block:: text + + { + Init... + for {} C { Post... } { + Body... + } + } + +This eases the rest of the optimization process because we can ignore +the complicated scoping rules of the for loop initialisation block. + +.. _var-decl-initializer: + +VarDeclInitializer +^^^^^^^^^^^^^^^^^^ +This step rewrites variable declarations so that all of them are initialized. +Declarations like ``let x, y`` are split into multiple declaration statements. + +Only supports initializing with the zero literal for now. + +Pseudo-SSA Transformation +------------------------- + +The purpose of this components is to get the program into a longer form, +so that other components can more easily work with it. The final representation +will be similar to a static-single-assignment (SSA) form, with the difference +that it does not make use of explicit "phi" functions which combines the values +from different branches of control flow because such a feature does not exist +in the Yul language. Instead, when control flow merges, if a variable is re-assigned +in one of the branches, a new SSA variable is declared to hold its current value, +so that the following expressions still only need to reference SSA variables. + +An example transformation is the following: + +:: + + { + let a := calldataload(0) + let b := calldataload(0x20) + if gt(a, 0) { + b := mul(b, 0x20) + } + a := add(a, 1) + sstore(a, add(b, 0x20)) + } + + +When all the following transformation steps are applied, the program will look +as follows: + +:: + + { + let _1 := 0 + let a_9 := calldataload(_1) + let a := a_9 + let _2 := 0x20 + let b_10 := calldataload(_2) + let b := b_10 + let _3 := 0 + let _4 := gt(a_9, _3) + if _4 + { + let _5 := 0x20 + let b_11 := mul(b_10, _5) + b := b_11 + } + let b_12 := b + let _6 := 1 + let a_13 := add(a_9, _6) + let _7 := 0x20 + let _8 := add(b_12, _7) + sstore(a_13, _8) + } + +Note that the only variable that is re-assigned in this snippet is ``b``. +This re-assignment cannot be avoided because ``b`` has different values +depending on the control flow. All other variables never change their +value once they are defined. The advantage of this property is that +variables can be freely moved around and references to them +can be exchanged by their initial value (and vice-versa), +as long as these values are still valid in the new context. + +Of course, the code here is far from being optimized. To the contrary, it is much +longer. The hope is that this code will be easier to work with and furthermore, +there are optimizer steps that undo these changes and make the code more +compact again at the end. + +.. _expression-splitter: + +ExpressionSplitter +^^^^^^^^^^^^^^^^^^ + +The expression splitter turns expressions like ``add(mload(x), mul(mload(y), 0x20))`` +into a sequence of declarations of unique variables that are assigned sub-expressions +of that expression so that each function call has only variables or literals +as arguments. + +The above would be transformed into + +:: + + { + let _1 := mload(y) + let _2 := mul(_1, 0x20) + let _3 := mload(x) + let z := add(_3, _2) + } + +Note that this transformation does not change the order of opcodes or function calls. + +It is not applied to loop iteration-condition, because the loop control flow does not allow +this "outlining" of the inner expressions in all cases. We can sidestep this limitation by applying +:ref:`for-loop-condition-into-body` to move the iteration condition into loop body. + +The final program should be in a form such that (with the exception of loop conditions) +function calls cannot appear nested inside expressions +and all function call arguments have to be literals or variables. + +The benefits of this form are that it is much easier to re-order the sequence of opcodes +and it is also easier to perform function call inlining. Furthermore, it is simpler +to replace individual parts of expressions or re-organize the "expression tree". +The drawback is that such code is much harder to read for humans. + +.. _SSA-transform: + +SSATransform +^^^^^^^^^^^^ + +This stage tries to replace repeated assignments to +existing variables by declarations of new variables as much as +possible. +The reassignments are still there, but all references to the +reassigned variables are replaced by the newly declared variables. + +Example: + +:: + + { + let a := 1 + mstore(a, 2) + a := 3 + } + +is transformed to + +:: + + { + let a_1 := 1 + let a := a_1 + mstore(a_1, 2) + let a_3 := 3 + a := a_3 + } + +Exact semantics: + +For any variable ``a`` that is assigned to somewhere in the code +(variables that are declared with value and never re-assigned +are not modified) perform the following transforms: + + - replace ``let a := v`` by ``let a_i := v let a := a_i`` + - replace ``a := v`` by ``let a_i := v a := a_i`` where ``i`` is a number such that ``a_i`` is yet unused. + +Furthermore, always record the current value of ``i`` used for ``a`` and replace each +reference to ``a`` by ``a_i``. +The current value mapping is cleared for a variable ``a`` at the end of each block +in which it was assigned to and at the end of the for loop init block if it is assigned +inside the for loop body or post block. +If a variable's value is cleared according to the rule above and the variable is declared outside +the block, a new SSA variable will be created at the location where control flow joins, +this includes the beginning of loop post/body block and the location right after +If/Switch/ForLoop/Block statement. + +After this stage, the Redundant Assign Eliminator is recommended to remove the unnecessary +intermediate assignments. + +This stage provides best results if the Expression Splitter and the Common Subexpression Eliminator +are run right before it, because then it does not generate excessive amounts of variables. +On the other hand, the Common Subexpression Eliminator could be more efficient if run after the +SSA transform. + +.. _redundant-assign-eliminator: + +RedundantAssignEliminator +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The SSA transform always generates an assignment of the form ``a := a_i``, even though +these might be unnecessary in many cases, like the following example: + +:: + + { + let a := 1 + a := mload(a) + a := sload(a) + sstore(a, 1) + } + +The SSA transform converts this snippet to the following: + +:: + + { + let a_1 := 1 + a := a_1 + let a_2 := mload(a_1) + a := a_2 + let a_3 := sload(a_2) + a := a_3 + sstore(a_3, 1) + } + +The Redundant Assign Eliminator removes all the three assignments to ``a``, because +the value of ``a`` is not used and thus turn this +snippet into strict SSA form: + +:: + + { + let a_1 := 1 + let a_2 := mload(a_1) + let a_3 := sload(a_2) + sstore(a_3, 1) + } + +Of course the intricate parts of determining whether an assignment is redundant or not +are connected to joining control flow. + +The component works as follows in detail: + +The AST is traversed twice: in an information gathering step and in the +actual removal step. During information gathering, we maintain a +mapping from assignment statements to the three states +"unused", "undecided" and "used" which signifies whether the assigned +value will be used later by a reference to the variable. + +When an assignment is visited, it is added to the mapping in the "undecided" state +(see remark about for loops below) and every other assignment to the same variable +that is still in the "undecided" state is changed to "unused". +When a variable is referenced, the state of any assignment to that variable still +in the "undecided" state is changed to "used". + +At points where control flow splits, a copy +of the mapping is handed over to each branch. At points where control flow +joins, the two mappings coming from the two branches are combined in the following way: +Statements that are only in one mapping or have the same state are used unchanged. +Conflicting values are resolved in the following way: + + - "unused", "undecided" -> "undecided" + - "unused", "used" -> "used" + - "undecided, "used" -> "used" + +For for-loops, the condition, body and post-part are visited twice, taking +the joining control-flow at the condition into account. +In other words, we create three control flow paths: Zero runs of the loop, +one run and two runs and then combine them at the end. + +Simulating a third run or even more is unnecessary, which can be seen as follows: + +A state of an assignment at the beginning of the iteration will deterministically +result in a state of that assignment at the end of the iteration. Let this +state mapping function be called ``f``. The combination of the three different +states ``unused``, ``undecided`` and ``used`` as explained above is the ``max`` +operation where ``unused = 0``, ``undecided = 1`` and ``used = 2``. + +The proper way would be to compute + +:: + + max(s, f(s), f(f(s)), f(f(f(s))), ...) + +as state after the loop. Since ``f`` just has a range of three different values, +iterating it has to reach a cycle after at most three iterations, +and thus ``f(f(f(s)))`` has to equal one of ``s``, ``f(s)``, or ``f(f(s))`` +and thus + +:: + + max(s, f(s), f(f(s))) = max(s, f(s), f(f(s)), f(f(f(s))), ...). + +In summary, running the loop at most twice is enough because there are only three +different states. + +For switch statements that have a "default"-case, there is no control-flow +part that skips the switch. + +When a variable goes out of scope, all statements still in the "undecided" +state are changed to "unused", unless the variable is the return +parameter of a function - there, the state changes to "used". + +In the second traversal, all assignments that are in the "unused" state are removed. + +This step is usually run right after the SSA transform to complete +the generation of the pseudo-SSA. + +Tools +----- + +Movability +^^^^^^^^^^ + +Movability is a property of an expression. It roughly means that the expression +is side-effect free and its evaluation only depends on the values of variables +and the call-constant state of the environment. Most expressions are movable. +The following parts make an expression non-movable: + + - function calls (might be relaxed in the future if all statements in the function are movable) + - opcodes that (can) have side-effects (like ``call`` or ``selfdestruct``) + - opcodes that read or write memory, storage or external state information + - opcodes that depend on the current PC, memory size or returndata size + +DataflowAnalyzer +^^^^^^^^^^^^^^^^ + +The Dataflow Analyzer is not an optimizer step itself but is used as a tool +by other components. While traversing the AST, it tracks the current value of +each variable, as long as that value is a movable expression. +It records the variables that are part of the expression +that is currently assigned to each other variable. Upon each assignment to +a variable ``a``, the current stored value of ``a`` is updated and +all stored values of all variables ``b`` are cleared whenever ``a`` is part +of the currently stored expression for ``b``. + +At control-flow joins, knowledge about variables is cleared if they have or would be assigned +in any of the control-flow paths. For instance, upon entering a +for loop, all variables are cleared that will be assigned during the +body or the post block. + +Expression-Scale Simplifications +-------------------------------- + +These simplification passes change expressions and replace them by equivalent +and hopefully simpler expressions. + +.. _common-subexpression-eliminator: + +CommonSubexpressionEliminator +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This step uses the Dataflow Analyzer and replaces subexpressions that +syntactically match the current value of a variable by a reference to +that variable. This is an equivalence transform because such subexpressions have +to be movable. + +All subexpressions that are identifiers themselves are replaced by their +current value if the value is an identifier. + +The combination of the two rules above allow to compute a local value +numbering, which means that if two variables have the same +value, one of them will always be unused. The Unused Pruner or the +Redundant Assign Eliminator will then be able to fully eliminate such +variables. + +This step is especially efficient if the expression splitter is run +before. If the code is in pseudo-SSA form, +the values of variables are available for a longer time and thus we +have a higher chance of expressions to be replaceable. + +The expression simplifier will be able to perform better replacements +if the common subexpression eliminator was run right before it. + +.. _expression-simplifier: + +Expression Simplifier +^^^^^^^^^^^^^^^^^^^^^ + +The Expression Simplifier uses the Dataflow Analyzer and makes use +of a list of equivalence transforms on expressions like ``X + 0 -> X`` +to simplify the code. + +It tries to match patterns like ``X + 0`` on each subexpression. +During the matching procedure, it resolves variables to their currently +assigned expressions to be able to match more deeply nested patterns +even when the code is in pseudo-SSA form. + +Some of the patterns like ``X - X -> 0`` can only be applied as long +as the expression ``X`` is movable, because otherwise it would remove its potential side-effects. +Since variable references are always movable, even if their current +value might not be, the Expression Simplifier is again more powerful +in split or pseudo-SSA form. + +.. _literal-rematerialiser: + +LiteralRematerialiser +^^^^^^^^^^^^^^^^^^^^^ + +To be documented. + +.. _load-resolver: + +LoadResolver +^^^^^^^^^^^^ + +Optimisation stage that replaces expressions of type ``sload(x)`` and ``mload(x)`` by the value +currently stored in storage resp. memory, if known. + +Works best if the code is in SSA form. + +Prerequisite: Disambiguator, ForLoopInitRewriter. + +.. _reasoning-based-simplifier: + +ReasoningBasedSimplifier +^^^^^^^^^^^^^^^^^^^^^^^^ + +This optimizer uses SMT solvers to check whether ``if`` conditions are constant. + + - If ``constraints AND condition`` is UNSAT, the condition is never true and the whole body can be removed. + - If ``constraints AND NOT condition`` is UNSAT, the condition is always true and can be replaced by ``1``. + +The simplifications above can only be applied if the condition is movable. + +It is only effective on the EVM dialect, but safe to use on other dialects. + +Prerequisite: Disambiguator, SSATransform. + +Statement-Scale Simplifications +------------------------------- + +.. _circular-reference-pruner: + +CircularReferencesPruner +^^^^^^^^^^^^^^^^^^^^^^^^ + +This stage removes functions that call each other but are +neither externally referenced nor referenced from the outermost context. + +.. _conditional-simplifier: + +ConditionalSimplifier +^^^^^^^^^^^^^^^^^^^^^ + +The Conditional Simplifier inserts assignments to condition variables if the value can be determined +from the control-flow. + +Destroys SSA form. + +Currently, this tool is very limited, mostly because we do not yet have support +for boolean types. Since conditions only check for expressions being nonzero, +we cannot assign a specific value. + +Current features: + + - switch cases: insert " := " + - after if statement with terminating control-flow, insert " := 0" + +Future features: + + - allow replacements by "1" + - take termination of user-defined functions into account + +Works best with SSA form and if dead code removal has run before. + +Prerequisite: Disambiguator. + +.. _conditional-unsimplifier: + +ConditionalUnsimplifier +^^^^^^^^^^^^^^^^^^^^^^^ + +Reverse of Conditional Simplifier. + +.. _control-flow-simplifier: + +ControlFlowSimplifier +^^^^^^^^^^^^^^^^^^^^^ + +Simplifies several control-flow structures: + + - replace if with empty body with pop(condition) + - remove empty default switch case + - remove empty switch case if no default case exists + - replace switch with no cases with pop(expression) + - turn switch with single case into if + - replace switch with only default case with pop(expression) and body + - replace switch with const expr with matching case body + - replace ``for`` with terminating control flow and without other break/continue by ``if`` + - remove ``leave`` at the end of a function. + +None of these operations depend on the data flow. The StructuralSimplifier +performs similar tasks that do depend on data flow. + +The ControlFlowSimplifier does record the presence or absence of ``break`` +and ``continue`` statements during its traversal. + +Prerequisite: Disambiguator, FunctionHoister, ForLoopInitRewriter. +Important: Introduces EVM opcodes and thus can only be used on EVM code for now. + +.. _dead-code-eliminator: + +DeadCodeEliminator +^^^^^^^^^^^^^^^^^^ + +This optimization stage removes unreachable code. + +Unreachable code is any code within a block which is preceded by a +leave, return, invalid, break, continue, selfdestruct or revert. + +Function definitions are retained as they might be called by earlier +code and thus are considered reachable. + +Because variables declared in a for loop's init block have their scope extended to the loop body, +we require ForLoopInitRewriter to run before this step. + +Prerequisite: ForLoopInitRewriter, Function Hoister, Function Grouper + +.. _unused-pruner: + +UnusedPruner +^^^^^^^^^^^^ + +This step removes the definitions of all functions that are never referenced. + +It also removes the declaration of variables that are never referenced. +If the declaration assigns a value that is not movable, the expression is retained, +but its value is discarded. + +All movable expression statements (expressions that are not assigned) are removed. + +.. _structural-simplifier: + +StructuralSimplifier +^^^^^^^^^^^^^^^^^^^^ + +This is a general step that performs various kinds of simplifications on +a structural level: + + - replace if statement with empty body by ``pop(condition)`` + - replace if statement with true condition by its body + - remove if statement with false condition + - turn switch with single case into if + - replace switch with only default case by ``pop(expression)`` and body + - replace switch with literal expression by matching case body + - replace for loop with false condition by its initialization part + +This component uses the Dataflow Analyzer. + +.. _block-flattener: + +BlockFlattener +^^^^^^^^^^^^^^ + +This stage eliminates nested blocks by inserting the statement in the +inner block at the appropriate place in the outer block: + +:: + + { + let x := 2 + { + let y := 3 + mstore(x, y) + } + } + +is transformed to + +:: + + { + let x := 2 + let y := 3 + mstore(x, y) + } + +As long as the code is disambiguated, this does not cause a problem because +the scopes of variables can only grow. + +.. _loop-invariant-code-motion: + +LoopInvariantCodeMotion +^^^^^^^^^^^^^^^^^^^^^^^ +This optimization moves movable SSA variable declarations outside the loop. + +Only statements at the top level in a loop's body or post block are considered, i.e variable +declarations inside conditional branches will not be moved out of the loop. + +Requirements: + + - The Disambiguator, ForLoopInitRewriter and FunctionHoister must be run upfront. + - Expression splitter and SSA transform should be run upfront to obtain better result. + + +Function-Level Optimizations +---------------------------- + +.. _function-specializer: + +FunctionSpecializer +^^^^^^^^^^^^^^^^^^^ + +This step specializes the function with its literal arguments. + +If a function, say, ``function f(a, b) { sstore (a, b) }``, is called with literal arguments, for +example, ``f(x, 5)``, where ``x`` is an identifier, it could be specialized by creating a new +function ``f_1`` that takes only one argument, i.e., + +:: + + function f_1(a_1) { + let b_1 := 5 + sstore(a_1, b_1) + } + +Other optimization steps will be able to make more simplifications to the function. The +optimization step is mainly useful for functions that would not be inlined. + +Prerequisites: Disambiguator, FunctionHoister + +LiteralRematerialiser is recommended as a prerequisite, even though it's not required for +correctness. + +.. _unused-function-parameter-pruner: + +UnusedFunctionParameterPruner +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This step removes unused parameters in a function. + +If a parameter is unused, like ``c`` and ``y`` in, ``function f(a,b,c) -> x, y { x := div(a,b) }``, we +remove the parameter and create a new "linking" function as follows: + +:: + + function f(a,b) -> x { x := div(a,b) } + function f2(a,b,c) -> x, y { x := f(a,b) } + +and replace all references to ``f`` by ``f2``. +The inliner should be run afterwards to make sure that all references to ``f2`` are replaced by +``f``. + +Prerequisites: Disambiguator, FunctionHoister, LiteralRematerialiser. + +The step LiteralRematerialiser is not required for correctness. It helps deal with cases such as: +``function f(x) -> y { revert(y, y} }`` where the literal ``y`` will be replaced by its value ``0``, +allowing us to rewrite the function. + +.. _equivalent-function-combiner: + +EquivalentFunctionCombiner +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If two functions are syntactically equivalent, while allowing variable +renaming but not any re-ordering, then any reference to one of the +functions is replaced by the other. + +The actual removal of the function is performed by the Unused Pruner. + + +Function Inlining +----------------- + +.. _functional-inliner: + +FunctionalInliner +^^^^^^^^^^^^^^^^^ + +This component of the optimizer performs restricted function inlining by inlining functions that can be +inlined inside functional expressions, i.e. functions that: + + - return a single value. + - have a body like ``r := ``. + - neither reference themselves nor ``r`` in the right hand side. + +Furthermore, for all parameters, all of the following need to be true: + + - The argument is movable. + - The parameter is either referenced less than twice in the function body, or the argument is rather cheap + ("cost" of at most 1, like a constant up to 0xff). + +Example: The function to be inlined has the form of ``function f(...) -> r { r := E }`` where +``E`` is an expression that does not reference ``r`` and all arguments in the function call are movable expressions. + +The result of this inlining is always a single expression. + +This component can only be used on sources with unique names. + +.. _full-function-inliner: + +FullFunctionInliner +^^^^^^^^^^^^^^^^^^^ + +The Full Function Inliner replaces certain calls of certain functions +by the function's body. This is not very helpful in most cases, because +it just increases the code size but does not have a benefit. Furthermore, +code is usually very expensive and we would often rather have shorter +code than more efficient code. In same cases, though, inlining a function +can have positive effects on subsequent optimizer steps. This is the case +if one of the function arguments is a constant, for example. + +During inlining, a heuristic is used to tell if the function call +should be inlined or not. +The current heuristic does not inline into "large" functions unless +the called function is tiny. Functions that are only used once +are inlined, as well as medium-sized functions, while function +calls with constant arguments allow slightly larger functions. + + +In the future, we may include a backtracking component +that, instead of inlining a function right away, only specializes it, +which means that a copy of the function is generated where +a certain parameter is always replaced by a constant. After that, +we can run the optimizer on this specialized function. If it +results in heavy gains, the specialized function is kept, +otherwise the original function is used instead. + +Cleanup +------- + +The cleanup is performed at the end of the optimizer run. It tries +to combine split expressions into deeply nested ones again and also +improves the "compilability" for stack machines by eliminating +variables as much as possible. + +.. _expression-joiner: + +ExpressionJoiner +^^^^^^^^^^^^^^^^ + +This is the opposite operation of the expression splitter. It turns a sequence of +variable declarations that have exactly one reference into a complex expression. +This stage fully preserves the order of function calls and opcode executions. +It does not make use of any information concerning the commutativity of the opcodes; +if moving the value of a variable to its place of use would change the order +of any function call or opcode execution, the transformation is not performed. + +Note that the component will not move the assigned value of a variable assignment +or a variable that is referenced more than once. + +The snippet ``let x := add(0, 2) let y := mul(x, mload(2))`` is not transformed, +because it would cause the order of the call to the opcodes ``add`` and +``mload`` to be swapped - even though this would not make a difference +because ``add`` is movable. + +When reordering opcodes like that, variable references and literals are ignored. +Because of that, the snippet ``let x := add(0, 2) let y := mul(x, 3)`` is +transformed to ``let y := mul(add(0, 2), 3)``, even though the ``add`` opcode +would be executed after the evaluation of the literal ``3``. + +.. _SSA-reverser: + +SSAReverser +^^^^^^^^^^^ + +This is a tiny step that helps in reversing the effects of the SSA transform +if it is combined with the Common Subexpression Eliminator and the +Unused Pruner. + +The SSA form we generate is detrimental to code generation on the EVM and +WebAssembly alike because it generates many local variables. It would +be better to just re-use existing variables with assignments instead of +fresh variable declarations. + +The SSA transform rewrites + +:: + + a := E + mstore(a, 1) + +to + +:: + + let a_1 := E + a := a_1 + mstore(a_1, 1) + +The problem is that instead of ``a``, the variable ``a_1`` is used +whenever ``a`` was referenced. The SSA transform changes statements +of this form by just swapping out the declaration and the assignment. The above +snippet is turned into + +:: + + a := E + let a_1 := a + mstore(a_1, 1) + +This is a very simple equivalence transform, but when we now run the +Common Subexpression Eliminator, it will replace all occurrences of ``a_1`` +by ``a`` (until ``a`` is re-assigned). The Unused Pruner will then +eliminate the variable ``a_1`` altogether and thus fully reverse the +SSA transform. + +.. _stack-compressor: + +StackCompressor +^^^^^^^^^^^^^^^ + +One problem that makes code generation for the Ethereum Virtual Machine +hard is the fact that there is a hard limit of 16 slots for reaching +down the expression stack. This more or less translates to a limit +of 16 local variables. The stack compressor takes Yul code and +compiles it to EVM bytecode. Whenever the stack difference is too +large, it records the function this happened in. + +For each function that caused such a problem, the Rematerialiser +is called with a special request to aggressively eliminate specific +variables sorted by the cost of their values. + +On failure, this procedure is repeated multiple times. + +.. _rematerialiser: + +Rematerialiser +^^^^^^^^^^^^^^ + +The rematerialisation stage tries to replace variable references by the expression that +was last assigned to the variable. This is of course only beneficial if this expression +is comparatively cheap to evaluate. Furthermore, it is only semantically equivalent if +the value of the expression did not change between the point of assignment and the +point of use. The main benefit of this stage is that it can save stack slots if it +leads to a variable being eliminated completely (see below), but it can also +save a DUP opcode on the EVM if the expression is very cheap. + +The Rematerialiser uses the Dataflow Analyzer to track the current values of variables, +which are always movable. +If the value is very cheap or the variable was explicitly requested to be eliminated, +the variable reference is replaced by its current value. + +.. _for-loop-condition-out-of-body: + +ForLoopConditionOutOfBody +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Reverses the transformation of ForLoopConditionIntoBody. + +For any movable ``c``, it turns + +:: + + for { ... } 1 { ... } { + if iszero(c) { break } + ... + } + +into + +:: + + for { ... } c { ... } { + ... + } + +and it turns + +:: + + for { ... } 1 { ... } { + if c { break } + ... + } + +into + +:: + + for { ... } iszero(c) { ... } { + ... + } + +The LiteralRematerialiser should be run before this step. + + +WebAssembly specific +-------------------- + +MainFunction +^^^^^^^^^^^^ + +Changes the topmost block to be a function with a specific name ("main") which has no +inputs nor outputs. + +Depends on the Function Grouper. diff --git a/docs/yul.rst b/docs/yul.rst index 2c77e4402..fc2dec45d 100644 --- a/docs/yul.rst +++ b/docs/yul.rst @@ -1155,9 +1155,8 @@ Yul Optimizer The Yul optimizer operates on Yul code and uses the same language for input, output and intermediate states. This allows for easy debugging and verification of the optimizer. -Please see the -`documentation in the source code `_ -for more details about its internals. +Please refer to the general :ref:`optimizer documentation ` +for more details about the different optimization stages and how to use the optimizer. If you want to use Solidity in stand-alone Yul mode, you activate the optimizer using ``--optimize``: @@ -1167,7 +1166,7 @@ If you want to use Solidity in stand-alone Yul mode, you activate the optimizer In Solidity mode, the Yul optimizer is activated together with the regular optimizer. -Optimization step sequence +Optimization Step Sequence -------------------------- By default the Yul optimizer applies its predefined sequence of optimization steps to the generated assembly. diff --git a/libyul/optimiser/README.md b/libyul/optimiser/README.md index 4bd3d6a36..67a6e1190 100644 --- a/libyul/optimiser/README.md +++ b/libyul/optimiser/README.md @@ -1,651 +1,5 @@ -Note that the Yul optimiser is still in research phase. Because of that, -the following description might not fully reflect the current or even -planned state of the optimiser. +# Yul-Based Optimizer -Table of Contents: +The documentation of the Yul-based optimizer module has been moved to the official Solidity documentation. -- [Selecting optimisations](#selecting-optimisations) -- [Preprocessing](#preprocessing) -- [Pseudo-SSA Transformation](#pseudo-ssa-transformation) -- [Tools](#tools) -- [Expression-Scale Simplifications](#expression-scale-simplifications) -- [Statement-Scale Simplifications](#statement-scale-simplifications) -- [Function Inlining](#function-inlining) -- [Cleanup](#cleanup) -- [Webassembly-sepcific](#webassembly-specific) - - -# Yul Optimiser - -The Yul optimiser consists of several stages and components that all transform -the AST in a semantically equivalent way. The goal is to end up either with code -that is shorter or at least only marginally longer but will allow further -optimisation steps. - -The optimiser currently follows a purely greedy strategy and does not do any -backtracking. - -All components of the optimiser are explained below, where -the following transformation steps are the main components: - - - [SSA Transform](#ssa-transform) - - [Common Subexpression Eliminator](#common-subexpression-eliminator) - - [Expression Simplifier](#expression-simplifier) - - [Redundant Assign Eliminator](#redundant-assign-eliminator) - - [Full Function Inliner](#full-function-inliner) - -## Selecting optimisations - -By default the optimiser applies its predefined sequence of optimisation steps to the generated assembly. -You can override this sequence and supply your own using the `--yul-optimizations` option: - -``` bash -solc --optimize --ir-optimized --yul-optimizations 'dhfoD[xarrscLMcCTU]uljmul' -``` - -There's a [table listing available abbreviations in the optimiser docs](/docs/yul.rst#optimization-step-sequence). - -## Preprocessing - -The preprocessing components perform transformations to get the program -into a certain normal form that is easier to work with. This normal -form is kept during the rest of the optimisation process. - -### Disambiguator - -The disambiguator takes an AST and returns a fresh copy where all identifiers have -names unique to the input AST. This is a prerequisite for all other optimiser stages. -One of the benefits is that identifier lookup does not need to take scopes into account -and we can basically ignore the result of the analysis phase. - -All subsequent stages have the property that all names stay unique. This means if -a new identifier needs to be introduced, a new unique name is generated. - -### Function Hoister - -The function hoister moves all function definitions to the end of the topmost block. This is -a semantically equivalent transformation as long as it is performed after the -disambiguation stage. The reason is that moving a definition to a higher-level block cannot decrease -its visibility and it is impossible to reference variables defined in a different function. - -The benefit of this stage is that function definitions can be looked up more easily -and functions can be optimised in isolation without having to traverse the AST. - -### Function Grouper - -The function grouper has to be applied after the disambiguator and the function hoister. -Its effect is that all topmost elements that are not function definitions are moved -into a single block which is the first statement of the root block. - -After this step, a program has the following normal form: - - { I F... } - -Where I is a (potentially empty) block that does not contain any function definitions (not even recursively) -and F is a list of function definitions such that no function contains a function definition. - -The benefit of this stage is that we always know where the list of function begins. - -### For Loop Condition Into Body - -This transformation moves the iteration condition of a for-loop into loop body. -We need this transformation because [expression splitter](#expression-splitter) won't -apply to iteration condition expressions (the `C` in the following example). - - for { Init... } C { Post... } { - Body... - } - -is transformed to - - for { Init... } 1 { Post... } { - if iszero(C) { break } - Body... - } - -### For Loop Init Rewriter - -This transformation moves the initialization part of a for-loop to before -the loop: - - for { Init... } C { Post... } { - Body... - } - -is transformed to - - { - Init... - for {} C { Post... } { - Body... - } - } - -This eases the rest of the optimisation process because we can ignore -the complicated scoping rules of the for loop initialisation block. - -## Pseudo-SSA Transformation - -The purpose of this components is to get the program into a longer form, -so that other components can more easily work with it. The final representation -will be similar to a static-single-assignment (SSA) form, with the difference -that it does not make use of explicit "phi" functions which combines the values -from different branches of control flow because such a feature does not exist -in the Yul language. Instead, when control flow merges, if a variable is re-assigned -in one of the branches, a new SSA variable is declared to hold its current value, -so that the following expressions still only need to reference SSA variables. - -An example transformation is the following: - - { - let a := calldataload(0) - let b := calldataload(0x20) - if gt(a, 0) { - b := mul(b, 0x20) - } - a := add(a, 1) - sstore(a, add(b, 0x20)) - } - -When all the following transformation steps are applied, the program will look -as follows: - - { - let _1 := 0 - let a_9 := calldataload(_1) - let a := a_9 - let _2 := 0x20 - let b_10 := calldataload(_2) - let b := b_10 - let _3 := 0 - let _4 := gt(a_9, _3) - if _4 - { - let _5 := 0x20 - let b_11 := mul(b_10, _5) - b := b_11 - } - let b_12 := b - let _6 := 1 - let a_13 := add(a_9, _6) - let _7 := 0x20 - let _8 := add(b_12, _7) - sstore(a_13, _8) - } - -Note that the only variable that is re-assigned in this snippet is ``b``. -This re-assignment cannot be avoided because ``b`` has different values -depending on the control flow. All other variables never change their -value once they are defined. The advantage of this property is that -variables can be freely moved around and references to them -can be exchanged by their initial value (and vice-versa), -as long as these values are still valid in the new context. - -Of course, the code here is far from being optimised. To the contrary, it is much -longer. The hope is that this code will be easier to work with and furthermore, -there are optimiser steps that undo these changes and make the code more -compact again at the end. - -### Expression Splitter - -The expression splitter turns expressions like ``add(mload(x), mul(mload(y), 0x20))`` -into a sequence of declarations of unique variables that are assigned sub-expressions -of that expression so that each function call has only variables or literals -as arguments. - -The above would be transformed into - - { - let _1 := mload(y) - let _2 := mul(_1, 0x20) - let _3 := mload(x) - let z := add(_3, _2) - } - -Note that this transformation does not change the order of opcodes or function calls. - -It is not applied to loop conditions, because the loop control flow does not allow -this "outlining" of the inner expressions in all cases. We can sidestep this limitation by applying -[for loop condition into body](#for-loop-condition-into-body) to move the iteration condition into loop body. - -The final program should be in a form such that (with the exception of loop conditions) -function calls cannot appear nested inside expressions -and all function call arguments have to be constants or variables. - -The benefits of this form are that it is much easier to re-order the sequence of opcodes -and it is also easier to perform function call inlining. Furthermore, it is simpler -to replace individual parts of expressions or re-organize the "expression tree". -The drawback is that such code is much harder to read for humans. - -### SSA Transform - -This stage tries to replace repeated assignments to -existing variables by declarations of new variables as much as -possible. -The reassignments are still there, but all references to the -reassigned variables are replaced by the newly declared variables. - -Example: - - { - let a := 1 - mstore(a, 2) - a := 3 - } - -is transformed to - - { - let a_1 := 1 - let a := a_1 - mstore(a_1, 2) - let a_3 := 3 - a := a_3 - } - -Exact semantics: - -For any variable ``a`` that is assigned to somewhere in the code -(variables that are declared with value and never re-assigned -are not modified) perform the following transforms: - - replace ``let a := v`` by ``let a_i := v let a := a_i`` - - replace ``a := v`` by ``let a_i := v a := a_i`` -where ``i`` is a number such that ``a_i`` is yet unused. - -Furthermore, always record the current value of ``i`` used for ``a`` and replace each -reference to ``a`` by ``a_i``. -The current value mapping is cleared for a variable ``a`` at the end of each block -in which it was assigned to and at the end of the for loop init block if it is assigned -inside the for loop body or post block. -If a variable's value is cleared according to the rule above and the variable is declared outside -the block, a new SSA variable will be created at the location where control flow joins, -this includes the beginning of loop post/body block and the location right after -If/Switch/ForLoop/Block statement. - -After this stage, the Redundant Assign Eliminator is recommended to remove the unnecessary -intermediate assignments. - -This stage provides best results if the Expression Splitter and the Common Subexpression Eliminator -are run right before it, because then it does not generate excessive amounts of variables. -On the other hand, the Common Subexpression Eliminator could be more efficient if run after the -SSA transform. - -### Redundant Assign Eliminator - -The SSA transform always generates an assignment of the form ``a := a_i``, even though -these might be unnecessary in many cases, like the following example: - - { - let a := 1 - a := mload(a) - a := sload(a) - sstore(a, 1) - } - -The SSA transform converts this snippet to the following: - - { - let a_1 := 1 - a := a_1 - let a_2 := mload(a_1) - a := a_2 - let a_3 := sload(a_2) - a := a_3 - sstore(a_3, 1) - } - -The Redundant Assign Eliminator removes all the three assignments to ``a``, because -the value of ``a`` is not used and thus turn this -snippet into strict SSA form: - - { - let a_1 := 1 - let a_2 := mload(a_1) - let a_3 := sload(a_2) - sstore(a_3, 1) - } - -Of course the intricate parts of determining whether an assignment is redundant or not -are connected to joining control flow. - -The component works as follows in detail: - -The AST is traversed twice: in an information gathering step and in the -actual removal step. During information gathering, we maintain a -mapping from assignment statements to the three states -"unused", "undecided" and "used" which signifies whether the assigned -value will be used later by a reference to the variable. - -When an assignment is visited, it is added to the mapping in the "undecided" state -(see remark about for loops below) and every other assignment to the same variable -that is still in the "undecided" state is changed to "unused". -When a variable is referenced, the state of any assignment to that variable still -in the "undecided" state is changed to "used". - -At points where control flow splits, a copy -of the mapping is handed over to each branch. At points where control flow -joins, the two mappings coming from the two branches are combined in the following way: -Statements that are only in one mapping or have the same state are used unchanged. -Conflicting values are resolved in the following way: - - - "unused", "undecided" -> "undecided" - - "unused", "used" -> "used" - - "undecided, "used" -> "used" - -For for-loops, the condition, body and post-part are visited twice, taking -the joining control-flow at the condition into account. -In other words, we create three control flow paths: Zero runs of the loop, -one run and two runs and then combine them at the end. - -Simulating a third run or even more is unnecessary, which can be seen as follows: - -A state of an assignment at the beginning of the iteration will deterministically -result in a state of that assignment at the end of the iteration. Let this -state mapping function be called `f`. The combination of the three different -states `unused`, `undecided` and `used` as explained above is the `max` -operation where `unused = 0`, `undecided = 1` and `used = 2`. - -The proper way would be to compute - - max(s, f(s), f(f(s)), f(f(f(s))), ...) - -as state after the loop. Since `f` just has a range of three different values, -iterating it has to reach a cycle after at most three iterations, -and thus `f(f(f(s)))` has to equal one of `s`, `f(s)`, or `f(f(s))` -and thus - - max(s, f(s), f(f(s))) = max(s, f(s), f(f(s)), f(f(f(s))), ...). - -In summary, running the loop at most twice is enough because there are only three -different states. - -For switch statements that have a "default"-case, there is no control-flow -part that skips the switch. - -When a variable goes out of scope, all statements still in the "undecided" -state are changed to "unused", unless the variable is the return -parameter of a function - there, the state changes to "used". - -In the second traversal, all assignments that are in the "unused" state are removed. - -This step is usually run right after the SSA transform to complete -the generation of the pseudo-SSA. - -## Tools - -### Movability - -Movability is a property of an expression. It roughly means that the expression -is side-effect free and its evaluation only depends on the values of variables -and the call-constant state of the environment. Most expressions are movable. -The following parts make an expression non-movable: - - - function calls (might be relaxed in the future if all statements in the function are movable) - - opcodes that (can) have side-effects (like ``call`` or ``selfdestruct``) - - opcodes that read or write memory, storage or external state information - - opcodes that depend on the current PC, memory size or returndata size - -### Dataflow Analyzer - -The Dataflow Analyzer is not an optimizer step itself but is used as a tool -by other components. While traversing the AST, it tracks the current value of -each variable, as long as that value is a movable expression. -It records the variables that are part of the expression -that is currently assigned to each other variable. Upon each assignment to -a variable ``a``, the current stored value of ``a`` is updated and -all stored values of all variables ``b`` are cleared whenever ``a`` is part -of the currently stored expression for ``b``. - -At control-flow joins, knowledge about variables is cleared if they have or would be assigned -in any of the control-flow paths. For instance, upon entering a -for loop, all variables are cleared that will be assigned during the -body or the post block. - -## Expression-Scale Simplifications - -These simplification passes change expressions and replace them by equivalent -and hopefully simpler expressions. - -### Common Subexpression Eliminator - -This step uses the Dataflow Analyzer and replaces subexpressions that -syntactically match the current value of a variable by a reference to -that variable. This is an equivalence transform because such subexpressions have -to be movable. - -All subexpressions that are identifiers themselves are replaced by their -current value if the value is an identifier. - -The combination of the two rules above allow to compute a local value -numbering, which means that if two variables have the same -value, one of them will always be unused. The Unused Pruner or the -Redundant Assign Eliminator will then be able to fully eliminate such -variables. - -This step is especially efficient if the expression splitter is run -before. If the code is in pseudo-SSA form, -the values of variables are available for a longer time and thus we -have a higher chance of expressions to be replaceable. - -The expression simplifier will be able to perform better replacements -if the common subexpression eliminator was run right before it. - -### Expression Simplifier - -The Expression Simplifier uses the Dataflow Analyzer and makes use -of a list of equivalence transforms on expressions like ``X + 0 -> X`` -to simplify the code. - -It tries to match patterns like ``X + 0`` on each subexpression. -During the matching procedure, it resolves variables to their currently -assigned expressions to be able to match more deeply nested patterns -even when the code is in pseudo-SSA form. - -Some of the patterns like ``X - X -> 0`` can only be applied as long -as the expression ``X`` is movable, because otherwise it would remove its potential side-effects. -Since variable references are always movable, even if their current -value might not be, the Expression Simplifier is again more powerful -in split or pseudo-SSA form. - -## Statement-Scale Simplifications - -### Unused Pruner - -This step removes the definitions of all functions that are never referenced. - -It also removes the declaration of variables that are never referenced. -If the declaration assigns a value that is not movable, the expression is retained, -but its value is discarded. - -All movable expression statements (expressions that are not assigned) are removed. - -### Structural Simplifier - -This is a general step that performs various kinds of simplifications on -a structural level: - - - replace if statement with empty body by ``pop(condition)`` - - replace if statement with true condition by its body - - remove if statement with false condition - - turn switch with single case into if - - replace switch with only default case by ``pop(expression)`` and body - - replace switch with literal expression by matching case body - - replace for loop with false condition by its initialization part - -This component uses the Dataflow Analyzer. - -### Equivalent Function Combiner - -If two functions are syntactically equivalent, while allowing variable -renaming but not any re-ordering, then any reference to one of the -functions is replaced by the other. - -The actual removal of the function is performed by the Unused Pruner. - -### Block Flattener - -This stage eliminates nested blocks by inserting the statement in the -inner block at the appropriate place in the outer block: - - { - let x := 2 - { - let y := 3 - mstore(x, y) - } - } - -is transformed to - - { - let x := 2 - let y := 3 - mstore(x, y) - } - -As long as the code is disambiguated, this does not cause a problem because -the scopes of variables can only grow. - -## Function Inlining - -### Functional Inliner - -The functional inliner performs restricted function inlining. In particular, -the result of this inlining is always a single expression. This can -only be done if the function to be inlined has the form ``function f(...) -> r { r := E }`` where -``E`` is an expression that does not reference ``r`` and all arguments in the -function call are movable expressions. The function call is directly replaced -by ``E``, substituting the function call arguments. Because this can cause the -function call arguments to be duplicated, removed or re-ordered, they have -to be movable. - -### Full Function Inliner - -The Full Function Inliner replaces certain calls of certain functions -by the function's body. This is not very helpful in most cases, because -it just increases the code size but does not have a benefit. Furthermore, -code is usually very expensive and we would often rather have shorter -code than more efficient code. In same cases, though, inlining a function -can have positive effects on subsequent optimizer steps. This is the case -if one of the function arguments is a constant, for example. - -During inlining, a heuristic is used to tell if the function call -should be inlined or not. -The current heuristic does not inline into "large" functions unless -the called function is tiny. Functions that are only used once -are inlined, as well as medium-sized functions, while function -calls with constant arguments allow slightly larger functions. - - -In the future, we might want to have a backtracking component -that, instead of inlining a function right away, only specializes it, -which means that a copy of the function is generated where -a certain parameter is always replaced by a constant. After that, -we can run the optimizer on this specialized function. If it -results in heavy gains, the specialized function is kept, -otherwise the original function is used instead. - -## Cleanup - -The cleanup is performed at the end of the optimizer run. It tries -to combine split expressions into deeply nested ones again and also -improves the "compilability" for stack machines by eliminating -variables as much as possible. - -### Expression Joiner - -This is the opposite operation of the expression splitter. It turns a sequence of -variable declarations that have exactly one reference into a complex expression. -This stage fully preserves the order of function calls and opcode executions. -It does not make use of any information concerning the commutability of opcodes; -if moving the value of a variable to its place of use would change the order -of any function call or opcode execution, the transformation is not performed. - -Note that the component will not move the assigned value of a variable assignment -or a variable that is referenced more than once. - -The snippet ``let x := add(0, 2) let y := mul(x, mload(2))`` is not transformed, -because it would cause the order of the call to the opcodes ``add`` and -``mload`` to be swapped - even though this would not make a difference -because ``add`` is movable. - -When reordering opcodes like that, variable references and literals are ignored. -Because of that, the snippet ``let x := add(0, 2) let y := mul(x, 3)`` is -transformed to ``let y := mul(add(0, 2), 3)``, even though the ``add`` opcode -would be executed after the evaluation of the literal ``3``. - -### SSA Reverser - -This is a tiny step that helps in reversing the effects of the SSA transform -if it is combined with the Common Subexpression Eliminator and the -Unused Pruner. - -The SSA form we generate is detrimental to code generation on the EVM and -WebAssembly alike because it generates many local variables. It would -be better to just re-use existing variables with assignments instead of -fresh variable declarations. - -The SSA transform rewrites - - a := E - mstore(a, 1) - -to - - let a_1 := E - a := a_1 - mstore(a_1, 1) - -The problem is that instead of ``a``, the variable ``a_1`` is used -whenever ``a`` was referenced. The SSA transform changes statements -of this form by just swapping out the declaration and the assignment. The above -snippet is turned into - - a := E - let a_1 := a - mstore(a_1, 1) - -This is a very simple equivalence transform, but when we now run the -Common Subexpression Eliminator, it will replace all occurrences of ``a_1`` -by ``a`` (until ``a`` is re-assigned). The Unused Pruner will then -eliminate the variable ``a_1`` altogether and thus fully reverse the -SSA transform. - -### Stack Compressor - -One problem that makes code generation for the Ethereum Virtual Machine -hard is the fact that there is a hard limit of 16 slots for reaching -down the expression stack. This more or less translates to a limit -of 16 local variables. The stack compressor takes Yul code and -compiles it to EVM bytecode. Whenever the stack difference is too -large, it records the function this happened in. - -For each function that caused such a problem, the Rematerialiser -is called with a special request to aggressively eliminate specific -variables sorted by the cost of their values. - -On failure, this procedure is repeated multiple times. - -### Rematerialiser - -The rematerialisation stage tries to replace variable references by the expression that -was last assigned to the variable. This is of course only beneficial if this expression -is comparatively cheap to evaluate. Furthermore, it is only semantically equivalent if -the value of the expression did not change between the point of assignment and the -point of use. The main benefit of this stage is that it can save stack slots if it -leads to a variable being eliminated completely (see below), but it can also -save a DUP opcode on the EVM if the expression is very cheap. - -The Rematerialiser uses the Dataflow Analyzer to track the current values of variables, -which are always movable. -If the value is very cheap or the variable was explicitly requested to be eliminated, -the variable reference is replaced by its current value. - -## WebAssembly specific - -### Main Function - -Changes the topmost block to be a function with a specific name ("main") which has no -inputs nor outputs. - -Depends on the Function Grouper. +Please refer to the [optimizer documentation](/docs/internals/optimizer.rst) for a description of all optimization stages and how to use the optimizer and to the [Yul documentation](/docs/yul.rst#optimization-step-sequence) for more information on the optimization step sequence and a list of abbreviations for each step. \ No newline at end of file From 3e5f5fccf9c542f7ed5b504d54f16f569a7ef8de Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Thu, 22 Apr 2021 09:47:12 +0200 Subject: [PATCH 039/235] [Sol->Yul] Adding cleanUpArrayEnd util function. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: chriseth Co-authored-by: Alex Beregszaszi Co-authored-by: Kamil Åšliwak Co-authored-by: Alex Beregszaszi Co-authored-by: Kamil Åšliwak --- libsolidity/codegen/YulUtilFunctions.cpp | 107 +++++++++++++++-------- libsolidity/codegen/YulUtilFunctions.h | 13 +++ 2 files changed, 82 insertions(+), 38 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 32848a091..26c7d0df2 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1168,21 +1168,7 @@ std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type) - // Size was reduced, clear end of array - if lt(newLen, oldLen) { - let oldSlotCount := (oldLen) - let newSlotCount := (newLen) - let arrayDataStart := (array) - let deleteStart := add(arrayDataStart, newSlotCount) - let deleteEnd := add(arrayDataStart, oldSlotCount) - - // if we are dealing with packed array and offset is greater than zero - // we have to partially clear last slot that is still used, so decreasing start by one - let offset := mul(mod(newLen, ), ) - if gt(offset, 0) { (sub(deleteStart, 1), offset) } - - (deleteStart, deleteEnd) - } + (array, oldLen, newLen) })"); templ("functionName", functionName); @@ -1193,19 +1179,49 @@ std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type) bool isMappingBase = _type.baseType()->category() == Type::Category::Mapping; templ("needsClearing", !isMappingBase); if (!isMappingBase) - { - templ("convertToSize", arrayConvertLengthToSize(_type)); - templ("dataPosition", arrayDataAreaFunction(_type)); - templ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())); - templ("packed", _type.baseType()->storageBytes() <= 16); - templ("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes())); - templ("storageBytes", to_string(_type.baseType()->storageBytes())); - templ("partialClearStorageSlot", partialClearStorageSlotFunction()); - } + templ("cleanUpArrayEnd", cleanUpStorageArrayEndFunction(_type)); return templ.render(); }); } +string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _type) +{ + solAssert(_type.location() == DataLocation::Storage, ""); + solAssert(_type.baseType()->category() != Type::Category::Mapping, ""); + solAssert(!_type.isByteArray(), ""); + solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, ""); + + string functionName = "cleanup_storage_array_end_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&](vector& _args, vector&) { + _args = {"array", "len", "startIndex"}; + return Whiskers(R"( + if lt(startIndex, len) { + // Size was reduced, clear end of array + let oldSlotCount := (len) + let newSlotCount := (startIndex) + let arrayDataStart := (array) + let deleteStart := add(arrayDataStart, newSlotCount) + let deleteEnd := add(arrayDataStart, oldSlotCount) + + // if we are dealing with packed array and offset is greater than zero + // we have to partially clear last slot that is still used, so decreasing start by one + let offset := mul(mod(startIndex, ), ) + if gt(offset, 0) { (sub(deleteStart, 1), offset) } + + (deleteStart, deleteEnd) + } + )") + ("convertToSize", arrayConvertLengthToSize(_type)) + ("dataPosition", arrayDataAreaFunction(_type)) + ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) + ("packed", _type.baseType()->storageBytes() <= 16) + ("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes())) + ("storageBytes", to_string(_type.baseType()->storageBytes())) + ("partialClearStorageSlot", partialClearStorageSlotFunction()) + .render(); + }); +} + string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type) { string functionName = "resize_array_" + _type.identifier(); @@ -1230,6 +1246,30 @@ string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type) }); } +string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type) +{ + solAssert(_type.isByteArray(), ""); + solAssert(_type.isDynamicallySized(), ""); + + string functionName = "clean_up_bytearray_end_slots_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&](vector& _args, vector&) { + _args = {"array", "len", "startIndex"}; + return Whiskers(R"( + if gt(len, 31) { + let dataArea := (array) + let deleteStart := add(dataArea, div((startIndex), 32)) + // If we are clearing array to be short byte array, we want to clear only data starting from array data area. + if lt(startIndex, 32) { deleteStart := dataArea } + (deleteStart, add(dataArea, div(add(len, 31), 32))) + } + )") + ("dataLocation", arrayDataAreaFunction(_type)) + ("roundUp", roundUpFunction()) + ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) + .render(); + }); +} + string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _type) { string functionName = "byte_array_decrease_size_" + _type.identifier(); @@ -1805,28 +1845,19 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy let oldLen := (sload(slot)) + // potentially truncate data + (slot, oldLen, newLen) + let srcOffset := 0 srcOffset := 0x20 - // This is not needed in all branches. - let dstDataArea - if or(gt(oldLen, 31), gt(newLen, 31)) { - dstDataArea := (slot) - } - - if gt(oldLen, 31) { - // potentially truncate data - let deleteStart := add(dstDataArea, div(add(newLen, 31), 32)) - if lt(newLen, 32) { deleteStart := dstDataArea } - (deleteStart, add(dstDataArea, div(add(oldLen, 31), 32))) - } switch gt(newLen, 31) case 1 { let loopEnd := and(newLen, not(0x1f)) src := (src) - let dstPtr := dstDataArea + let dstPtr := (slot) let i := 0 for { } lt(i, loopEnd) { i := add(i, 0x20) } { sstore(dstPtr, (add(src, srcOffset))) @@ -1860,7 +1891,7 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy templ("dstDataLocation", arrayDataAreaFunction(_toType)); if (fromStorage) templ("srcDataLocation", arrayDataAreaFunction(_fromType)); - templ("clearStorageRange", clearStorageRangeFunction(*_toType.baseType())); + templ("cleanUpEndArray", cleanUpDynamicByteArrayEndSlotsFunction(_toType)); templ("srcIncrement", to_string(fromStorage ? 1 : 0x20)); templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload"); templ("maskBytes", maskBytesFunctionDynamic()); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index be1f25d24..30f109b51 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -207,6 +207,11 @@ public: /// signature: (array, newLen) std::string resizeArrayFunction(ArrayType const& _type); + /// @returns the name of a function that zeroes all storage array elements from `startIndex` to `len` (excluding). + /// Assumes that `len` is the array length. Does nothing if `startIndex >= len`. Does not modify the stored length. + /// signature: (array, len, startIndex) + std::string cleanUpStorageArrayEndFunction(ArrayType const& _type); + /// @returns the name of a function that reduces the size of a storage array by one element /// signature: (array) std::string storageArrayPopFunction(ArrayType const& _type); @@ -543,6 +548,14 @@ private: /// signature: (array, newLen) std::string resizeDynamicByteArrayFunction(ArrayType const& _type); + /// @returns the name of a function that cleans up elements of a storage byte array starting from startIndex. + /// It will not copy elements in case of transformation to short byte array, and will not change array length. + /// In case of startIndex is greater than len, doesn't do anything. + /// In case of short byte array (< 32 bytes) doesn't do anything. + /// If the first slot to be cleaned up is partially occupied, does not touch it. Cleans up only completely unused slots. + /// signature: (array, len, startIndex) + std::string cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type); + /// @returns the name of a function that increases size of byte array /// when we resize byte array frextractUsedSetLenom < 32 elements to >= 32 elements or we push to byte array of size 31 copying of data will occur /// signature: (array, data, oldLen, newLen) From 8f6d94dbd2374a35655a6efadaf9f69c3e262d85 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Wed, 5 May 2021 08:35:17 +0200 Subject: [PATCH 040/235] Update gas expectations in semantic tests. --- .../abiEncoderV1/abi_decode_v2_storage.sol | 2 +- .../abiencodedecode/abi_decode_simple_storage.sol | 2 +- .../semanticTests/array/bytes_length_member.sol | 2 +- .../array/copying/bytes_inside_mappings.sol | 4 ++-- .../array/copying/bytes_storage_to_storage.sol | 10 +++++----- .../copying/copy_byte_array_in_struct_to_storage.sol | 2 +- .../array/copying/copy_byte_array_to_storage.sol | 2 +- .../array/copying/copy_removes_bytes_data.sol | 2 +- .../array/copying/memory_dyn_2d_bytes_to_storage.sol | 2 +- .../array/copying/storage_memory_nested_bytes.sol | 2 +- .../array/delete/bytes_delete_element.sol | 2 +- .../array/push/array_push_struct_from_calldata.sol | 2 +- .../constructor/bytes_in_constructors_packer.sol | 2 +- .../calldata_struct_with_nested_array_to_storage.sol | 2 +- .../struct_containing_bytes_copy_and_delete.sol | 2 +- .../semanticTests/various/destructuring_assignment.sol | 2 +- .../various/skip_dynamic_types_for_structs.sol | 2 +- 17 files changed, 22 insertions(+), 22 deletions(-) diff --git a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol index 6b77d15e4..121b934af 100644 --- a/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol +++ b/test/libsolidity/semanticTests/abiEncoderV1/abi_decode_v2_storage.sol @@ -24,6 +24,6 @@ contract C { // compileViaYul: also // ---- // f() -> 0x20, 0x8, 0x40, 0x3, 0x9, 0xa, 0xb -// gas irOptimized: 193543 +// gas irOptimized: 193521 // gas legacy: 196426 // gas legacyOptimized: 193405 diff --git a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol index 95af83be3..d2cc3a82e 100644 --- a/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol +++ b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol @@ -11,6 +11,6 @@ contract C { // compileViaYul: also // ---- // f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" -// gas irOptimized: 130053 +// gas irOptimized: 130031 // gas legacy: 131690 // gas legacyOptimized: 130582 diff --git a/test/libsolidity/semanticTests/array/bytes_length_member.sol b/test/libsolidity/semanticTests/array/bytes_length_member.sol index c3cad2849..498d32632 100644 --- a/test/libsolidity/semanticTests/array/bytes_length_member.sol +++ b/test/libsolidity/semanticTests/array/bytes_length_member.sol @@ -15,7 +15,7 @@ contract c { // ---- // getLength() -> 0 // set(): 1, 2 -> true -// gas irOptimized: 102993 +// gas irOptimized: 102970 // gas legacy: 103126 // gas legacyOptimized: 102967 // getLength() -> 68 diff --git a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol index b1c321992..08853d12e 100644 --- a/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol +++ b/test/libsolidity/semanticTests/array/copying/bytes_inside_mappings.sol @@ -7,11 +7,11 @@ contract c { // compileViaYul: also // ---- // set(uint256): 1, 2 -> true -// gas irOptimized: 103242 +// gas irOptimized: 103224 // gas legacy: 103491 // gas legacyOptimized: 103136 // set(uint256): 2, 2, 3, 4, 5 -> true -// gas irOptimized: 163929 +// gas irOptimized: 163911 // gas legacy: 164121 // gas legacyOptimized: 163766 // storageEmpty -> 0 diff --git a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol index 3089f00d7..0c65687e0 100644 --- a/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/bytes_storage_to_storage.sol @@ -19,25 +19,25 @@ contract c { // ---- // f(uint256): 0 -> 0x20, 0x00 // f(uint256): 31 -> 0x20, 0x1f, 0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e00 -// gas irOptimized: 221724 +// gas irOptimized: 221696 // gas legacy: 255464 // gas legacyOptimized: 250998 // f(uint256): 32 -> 0x20, 0x20, 1780731860627700044960722568376592200742329637303199754547598369979440671 -// gas irOptimized: 229335 +// gas irOptimized: 229291 // gas legacy: 267931 // gas legacyOptimized: 263329 // f(uint256): 33 -> 0x20, 33, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x2000000000000000000000000000000000000000000000000000000000000000 -// gas irOptimized: 238042 +// gas irOptimized: 238003 // gas legacy: 277538 // gas legacyOptimized: 272818 // f(uint256): 63 -> 0x20, 0x3f, 1780731860627700044960722568376592200742329637303199754547598369979440671, 14532552714582660066924456880521368950258152170031413196862950297402215316992 -// gas irOptimized: 348712 +// gas irOptimized: 348673 // gas legacy: 423428 // gas legacyOptimized: 414868 // f(uint256): 12 -> 0x20, 0x0c, 0x0102030405060708090a0b0000000000000000000000000000000000000000 // gas legacy: 106445 // gas legacyOptimized: 104379 // f(uint256): 129 -> 0x20, 0x81, 1780731860627700044960722568376592200742329637303199754547598369979440671, 0x202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f, 29063324697304692433803953038474361308315562010425523193971352996434451193439, 0x606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f, -57896044618658097711785492504343953926634992332820282019728792003956564819968 -// gas irOptimized: 802359 +// gas irOptimized: 802315 // gas legacy: 954517 // gas legacyOptimized: 937521 diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol index 7d9708703..65c5d3447 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol @@ -37,7 +37,7 @@ contract C { // compileViaYul: also // ---- // f() -> 0x40, 0x80, 6, 0x6162636465660000000000000000000000000000000000000000000000000000, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000 -// gas irOptimized: 172318 +// gas irOptimized: 172282 // gas legacy: 174794 // gas legacyOptimized: 174188 // g() -> 0x40, 0xc0, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000, 0x11, 0x3132333435363738393233343536373839000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol index f9d60b1ac..769cecae9 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol @@ -48,6 +48,6 @@ contract C { // compileViaYul: also // ---- // f() -> 0xff -// gas irOptimized: 136164 +// gas irOptimized: 136027 // gas legacy: 137645 // gas legacyOptimized: 134376 diff --git a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol index bbf1b7ddd..423398797 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_removes_bytes_data.sol @@ -9,7 +9,7 @@ contract c { // compileViaYul: also // ---- // set(): 1, 2, 3, 4, 5 -> true -// gas irOptimized: 163680 +// gas irOptimized: 163657 // gas legacy: 163756 // gas legacyOptimized: 163596 // storageEmpty -> 0 diff --git a/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol b/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol index 002801ea7..ef85da1b1 100644 --- a/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/memory_dyn_2d_bytes_to_storage.sol @@ -20,6 +20,6 @@ contract C { // compileViaYul: also // ---- // f() -> 3 -// gas irOptimized: 173135 +// gas irOptimized: 173108 // gas legacy: 179707 // gas legacyOptimized: 178763 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol index d20d7b3b1..c98f762eb 100644 --- a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol @@ -13,6 +13,6 @@ contract C { // compileViaYul: also // ---- // f() -> 0x20, 0x02, 0x40, 0x80, 3, 0x6162630000000000000000000000000000000000000000000000000000000000, 0x99, 44048183304486788312148433451363384677562265908331949128489393215789685032262, 32241931068525137014058842823026578386641954854143559838526554899205067598957, 49951309422467613961193228765530489307475214998374779756599339590522149884499, 0x54555658595a6162636465666768696a6b6c6d6e6f707172737475767778797a, 0x4142434445464748494a4b4c4d4e4f5051525354555658595a00000000000000 -// gas irOptimized: 198279 +// gas irOptimized: 198253 // gas legacy: 199159 // gas legacyOptimized: 198137 diff --git a/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol index b0589ef4b..72934fd95 100644 --- a/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol +++ b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol @@ -18,6 +18,6 @@ contract c { // compileViaYul: also // ---- // test1() -> true -// gas irOptimized: 527501 +// gas irOptimized: 527479 // gas legacy: 613377 // gas legacyOptimized: 606411 diff --git a/test/libsolidity/semanticTests/array/push/array_push_struct_from_calldata.sol b/test/libsolidity/semanticTests/array/push/array_push_struct_from_calldata.sol index 07b135f68..cc21b18b0 100644 --- a/test/libsolidity/semanticTests/array/push/array_push_struct_from_calldata.sol +++ b/test/libsolidity/semanticTests/array/push/array_push_struct_from_calldata.sol @@ -19,5 +19,5 @@ contract c { // ---- // test((uint16,uint16,uint16[3],uint16[])): 0x20, 2, 3, 0, 0, 4, 0xC0, 4, 0, 0, 5, 0, 0 -> 2, 3, 4, 5 // gas irOptimized: 147998 -// gas legacy: 152444 +// gas legacy: 152522 // gas legacyOptimized: 146671 diff --git a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol index aa6693033..60e2017f3 100644 --- a/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol +++ b/test/libsolidity/semanticTests/constructor/bytes_in_constructors_packer.sol @@ -26,6 +26,6 @@ contract Creator { // compileViaYul: also // ---- // f(uint256,bytes): 7, 0x40, 78, "abcdefghijklmnopqrstuvwxyzabcdef", "ghijklmnopqrstuvwxyzabcdefghijkl", "mnopqrstuvwxyz" -> 7, "h" -// gas irOptimized: 330976 +// gas irOptimized: 330957 // gas legacy: 422873 // gas legacyOptimized: 292281 diff --git a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol index 7a2ab84fb..0fdc5063c 100644 --- a/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol +++ b/test/libsolidity/semanticTests/structs/calldata/calldata_struct_with_nested_array_to_storage.sol @@ -19,5 +19,5 @@ contract C { // ---- // f(uint32,(uint128,uint256[][2],uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 77, 1, 2, 88 // gas irOptimized: 197768 -// gas legacy: 205149 +// gas legacy: 205266 // gas legacyOptimized: 196983 diff --git a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol index 42bb2a118..a9c345322 100644 --- a/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol +++ b/test/libsolidity/semanticTests/structs/struct_containing_bytes_copy_and_delete.sol @@ -25,7 +25,7 @@ contract c { // ---- // storageEmpty -> 1 // set(uint256,bytes,uint256): 12, 0x60, 13, 33, "12345678901234567890123456789012", "3" -> true -// gas irOptimized: 124227 +// gas irOptimized: 124205 // gas legacy: 124736 // gas legacyOptimized: 124179 // test(uint256): 32 -> "3" diff --git a/test/libsolidity/semanticTests/various/destructuring_assignment.sol b/test/libsolidity/semanticTests/various/destructuring_assignment.sol index 03b302cb0..23c937741 100644 --- a/test/libsolidity/semanticTests/various/destructuring_assignment.sol +++ b/test/libsolidity/semanticTests/various/destructuring_assignment.sol @@ -36,6 +36,6 @@ contract C { // compileViaYul: also // ---- // f(bytes): 0x20, 0x5, "abcde" -> 0 -// gas irOptimized: 241342 +// gas irOptimized: 241328 // gas legacy: 239258 // gas legacyOptimized: 238582 diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol index e61ff84c9..aabe52688 100644 --- a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol @@ -22,6 +22,6 @@ contract C { // compileViaYul: also // ---- // g() -> 2, 6 -// gas irOptimized: 169804 +// gas irOptimized: 169790 // gas legacy: 172490 // gas legacyOptimized: 171209 From e7708b6006ffb734799d5a721de3f34053977878 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 3 May 2021 12:03:41 +0200 Subject: [PATCH 041/235] Properly treat utf8-non-encodable yul literals. --- Changelog.md | 5 + libyul/AsmJsonConverter.cpp | 5 +- libyul/AsmJsonImporter.cpp | 7 +- test/libsolidity/ASTJSON/yul_hex_literal.json | 154 ++++++++++++++++++ test/libsolidity/ASTJSON/yul_hex_literal.sol | 11 ++ .../ASTJSON/yul_hex_literal_parseOnly.json | 139 ++++++++++++++++ 6 files changed, 319 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/ASTJSON/yul_hex_literal.json create mode 100644 test/libsolidity/ASTJSON/yul_hex_literal.sol create mode 100644 test/libsolidity/ASTJSON/yul_hex_literal_parseOnly.json diff --git a/Changelog.md b/Changelog.md index 0179f7524..99fed65c2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,11 @@ Compiler Features: Bugfixes: + * AST: Do not output value of Yul literal if it is not a valid UTF-8 string. + + +AST Changes: + * Add member `hexValue` for Yul string and hex literals. ### 0.8.4 (2021-04-21) diff --git a/libyul/AsmJsonConverter.cpp b/libyul/AsmJsonConverter.cpp index 582d95a82..fe595e7d2 100644 --- a/libyul/AsmJsonConverter.cpp +++ b/libyul/AsmJsonConverter.cpp @@ -24,6 +24,7 @@ #include #include #include +#include using namespace std; @@ -63,10 +64,12 @@ Json::Value AsmJsonConverter::operator()(Literal const& _node) const break; case LiteralKind::String: ret["kind"] = "string"; + ret["hexValue"] = util::toHex(util::asBytes(_node.value.str())); break; } ret["type"] = _node.type.str(); - ret["value"] = _node.value.str(); + if (util::validateUTF8(_node.value.str())) + ret["value"] = _node.value.str(); return ret; } diff --git a/libyul/AsmJsonImporter.cpp b/libyul/AsmJsonImporter.cpp index 410bdbed5..0aacbdf84 100644 --- a/libyul/AsmJsonImporter.cpp +++ b/libyul/AsmJsonImporter.cpp @@ -158,7 +158,12 @@ Literal AsmJsonImporter::createLiteral(Json::Value const& _node) auto lit = createAsmNode(_node); string kind = member(_node, "kind").asString(); - lit.value = YulString{member(_node, "value").asString()}; + solAssert(member(_node, "hexValue").isString() || member(_node, "value").isString(), ""); + if (_node.isMember("hexValue")) + lit.value = YulString{util::asString(util::fromHex(member(_node, "hexValue").asString()))}; + else + lit.value = YulString{member(_node, "value").asString()}; + lit.type= YulString{member(_node, "type").asString()}; if (kind == "number") diff --git a/test/libsolidity/ASTJSON/yul_hex_literal.json b/test/libsolidity/ASTJSON/yul_hex_literal.json new file mode 100644 index 000000000..54d37da00 --- /dev/null +++ b/test/libsolidity/ASTJSON/yul_hex_literal.json @@ -0,0 +1,154 @@ +{ + "absolutePath": "a", + "exportedSymbols": + { + "Sample": + [ + 6 + ] + }, + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "fullyImplemented": true, + "id": 6, + "linearizedBaseContracts": + [ + 6 + ], + "name": "Sample", + "nameLocation": "9:6:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "47:167:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "66:142:1", + "statements": + [ + { + "nodeType": "YulVariableDeclaration", + "src": "80:15:1", + "value": + { + "hexValue": "74657374", + "kind": "string", + "nodeType": "YulLiteral", + "src": "89:6:1", + "type": "", + "value": "test" + }, + "variables": + [ + { + "name": "a", + "nodeType": "YulTypedName", + "src": "84:1:1", + "type": "" + } + ] + }, + { + "nodeType": "YulVariableDeclaration", + "src": "108:54:1", + "value": + { + "hexValue": "112233445566778899aabbccddeeff6677889900", + "kind": "string", + "nodeType": "YulLiteral", + "src": "117:45:1", + "type": "" + }, + "variables": + [ + { + "name": "b", + "nodeType": "YulTypedName", + "src": "112:1:1", + "type": "" + } + ] + }, + { + "nodeType": "YulVariableDeclaration", + "src": "175:23:1", + "value": + { + "hexValue": "1234abcd", + "kind": "string", + "nodeType": "YulLiteral", + "src": "184:14:1", + "type": "" + }, + "variables": + [ + { + "name": "c", + "nodeType": "YulTypedName", + "src": "179:1:1", + "type": "" + } + ] + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "57:151:1" + } + ] + }, + "functionSelector": "26121ff0", + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nameLocation": "31:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "32:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "47:0:1" + }, + "scope": 6, + "src": "22:192:1", + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + } + ], + "scope": 7, + "src": "0:216:1", + "usedErrors": [] + } + ], + "src": "0:217:1" +} diff --git a/test/libsolidity/ASTJSON/yul_hex_literal.sol b/test/libsolidity/ASTJSON/yul_hex_literal.sol new file mode 100644 index 000000000..f441866c1 --- /dev/null +++ b/test/libsolidity/ASTJSON/yul_hex_literal.sol @@ -0,0 +1,11 @@ +contract Sample { + function f() public pure { + assembly { + let a := "test" + let b := hex"112233445566778899aabbccddeeff6677889900" + let c := hex"1234_abcd" + } + } +} + +// ---- diff --git a/test/libsolidity/ASTJSON/yul_hex_literal_parseOnly.json b/test/libsolidity/ASTJSON/yul_hex_literal_parseOnly.json new file mode 100644 index 000000000..a6da7e778 --- /dev/null +++ b/test/libsolidity/ASTJSON/yul_hex_literal_parseOnly.json @@ -0,0 +1,139 @@ +{ + "absolutePath": "a", + "id": 7, + "nodeType": "SourceUnit", + "nodes": + [ + { + "abstract": false, + "baseContracts": [], + "contractDependencies": [], + "contractKind": "contract", + "id": 6, + "name": "Sample", + "nameLocation": "9:6:1", + "nodeType": "ContractDefinition", + "nodes": + [ + { + "body": + { + "id": 4, + "nodeType": "Block", + "src": "47:167:1", + "statements": + [ + { + "AST": + { + "nodeType": "YulBlock", + "src": "66:142:1", + "statements": + [ + { + "nodeType": "YulVariableDeclaration", + "src": "80:15:1", + "value": + { + "hexValue": "74657374", + "kind": "string", + "nodeType": "YulLiteral", + "src": "89:6:1", + "type": "", + "value": "test" + }, + "variables": + [ + { + "name": "a", + "nodeType": "YulTypedName", + "src": "84:1:1", + "type": "" + } + ] + }, + { + "nodeType": "YulVariableDeclaration", + "src": "108:54:1", + "value": + { + "hexValue": "112233445566778899aabbccddeeff6677889900", + "kind": "string", + "nodeType": "YulLiteral", + "src": "117:45:1", + "type": "" + }, + "variables": + [ + { + "name": "b", + "nodeType": "YulTypedName", + "src": "112:1:1", + "type": "" + } + ] + }, + { + "nodeType": "YulVariableDeclaration", + "src": "175:23:1", + "value": + { + "hexValue": "1234abcd", + "kind": "string", + "nodeType": "YulLiteral", + "src": "184:14:1", + "type": "" + }, + "variables": + [ + { + "name": "c", + "nodeType": "YulTypedName", + "src": "179:1:1", + "type": "" + } + ] + } + ] + }, + "evmVersion": %EVMVERSION%, + "externalReferences": [], + "id": 3, + "nodeType": "InlineAssembly", + "src": "57:151:1" + } + ] + }, + "id": 5, + "implemented": true, + "kind": "function", + "modifiers": [], + "name": "f", + "nameLocation": "31:1:1", + "nodeType": "FunctionDefinition", + "parameters": + { + "id": 1, + "nodeType": "ParameterList", + "parameters": [], + "src": "32:2:1" + }, + "returnParameters": + { + "id": 2, + "nodeType": "ParameterList", + "parameters": [], + "src": "47:0:1" + }, + "src": "22:192:1", + "stateMutability": "pure", + "virtual": false, + "visibility": "public" + } + ], + "src": "0:216:1", + "usedErrors": [] + } + ], + "src": "0:217:1" +} From 6d41ed024aa1c6ca68d17631ff4e2c6b30d05b74 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 3 May 2021 14:27:47 +0200 Subject: [PATCH 042/235] Update existing tests. --- test/libsolidity/ASTJSON/assembly/stringlit.json | 1 + test/libsolidity/ASTJSON/assembly/stringlit_parseOnly.json | 1 + 2 files changed, 2 insertions(+) diff --git a/test/libsolidity/ASTJSON/assembly/stringlit.json b/test/libsolidity/ASTJSON/assembly/stringlit.json index c470d4790..9ca0ad8e5 100644 --- a/test/libsolidity/ASTJSON/assembly/stringlit.json +++ b/test/libsolidity/ASTJSON/assembly/stringlit.json @@ -47,6 +47,7 @@ "src": "58:14:1", "value": { + "hexValue": "616263", "kind": "string", "nodeType": "YulLiteral", "src": "67:5:1", diff --git a/test/libsolidity/ASTJSON/assembly/stringlit_parseOnly.json b/test/libsolidity/ASTJSON/assembly/stringlit_parseOnly.json index 03f757e65..e82999a78 100644 --- a/test/libsolidity/ASTJSON/assembly/stringlit_parseOnly.json +++ b/test/libsolidity/ASTJSON/assembly/stringlit_parseOnly.json @@ -35,6 +35,7 @@ "src": "58:14:1", "value": { + "hexValue": "616263", "kind": "string", "nodeType": "YulLiteral", "src": "67:5:1", From 72fc4d4a3228c6d18a4395784792606f7bd93911 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 5 May 2021 10:12:12 +0200 Subject: [PATCH 043/235] Introduce ceil division helper function. --- libsolidity/codegen/YulUtilFunctions.cpp | 25 ++++++++++++++++++------ libsolidity/codegen/YulUtilFunctions.h | 7 +++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 26c7d0df2..686f3690c 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -541,6 +541,18 @@ string YulUtilFunctions::roundUpFunction() }); } +string YulUtilFunctions::divide32CeilFunction() +{ + return m_functionCollector.createFunction( + "divide_by_32_ceil", + [&](vector& _args, vector& _ret) { + _args = {"value"}; + _ret = {"result"}; + return "result := div(add(value, 31), 32)"; + } + ); +} + string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) { string functionName = "checked_add_" + _type.identifier(); @@ -1257,14 +1269,14 @@ string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const return Whiskers(R"( if gt(len, 31) { let dataArea := (array) - let deleteStart := add(dataArea, div((startIndex), 32)) + let deleteStart := add(dataArea, (startIndex)) // If we are clearing array to be short byte array, we want to clear only data starting from array data area. if lt(startIndex, 32) { deleteStart := dataArea } - (deleteStart, add(dataArea, div(add(len, 31), 32))) + (deleteStart, add(dataArea, (len))) } )") ("dataLocation", arrayDataAreaFunction(_type)) - ("roundUp", roundUpFunction()) + ("div32Ceil", divide32CeilFunction()) ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) .render(); }); @@ -1279,13 +1291,13 @@ string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _type) switch lt(newLen, 32) case 0 { let arrayDataStart := (array) - let deleteStart := add(arrayDataStart, div(add(newLen, 31), 32)) + let deleteStart := add(arrayDataStart, (newLen)) // we have to partially clear last slot that is still used let offset := and(newLen, 0x1f) if offset { (sub(deleteStart, 1), offset) } - (deleteStart, add(arrayDataStart, div(add(oldLen, 31), 32))) + (deleteStart, add(arrayDataStart, (oldLen))) sstore(array, or(mul(2, newLen), 1)) } @@ -1294,7 +1306,7 @@ string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _type) case 1 { let arrayDataStart := (array) // clear whole old array, as we are transforming to short bytes array - (add(arrayDataStart, 1), add(arrayDataStart, div(add(oldLen, 31), 32))) + (add(arrayDataStart, 1), add(arrayDataStart, (oldLen))) (array, newLen) } default { @@ -1307,6 +1319,7 @@ string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _type) ("partialClearStorageSlot", partialClearStorageSlotFunction()) ("clearStorageRange", clearStorageRangeFunction(*_type.baseType())) ("transitLongToShort", byteArrayTransitLongToShortFunction(_type)) + ("div32Ceil", divide32CeilFunction()) ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction()) .render(); }); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 30f109b51..962d5e084 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -126,9 +126,16 @@ public: /// @returns the name of a function that rounds its input to the next multiple /// of 32 or the input if it is a multiple of 32. + /// Ignores overflow. /// signature: (value) -> result std::string roundUpFunction(); + /// @returns the name of a function that divides by 32 and rounds up during the division. + /// In other words, on input x it returns the smallest y such that y * 32 >= x. + /// Ignores overflow. + /// signature: (x) -> y + std::string divide32CeilFunction(); + /// signature: (x, y) -> sum std::string overflowCheckedIntAddFunction(IntegerType const& _type); /// signature: (x, y) -> sum From f0c5cdca9f2b8c8b6584e6af3c52481ddfcab5b1 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Wed, 5 May 2021 08:57:19 +0200 Subject: [PATCH 044/235] [Sol->Yul] Adding util function to copy literal to storage. Co-authored-by: Daniel Kirchner Co-authored-by: chriseth --- libsolidity/ast/Types.cpp | 17 ---- libsolidity/ast/Types.h | 9 -- libsolidity/codegen/ExpressionCompiler.cpp | 46 +++++++++-- libsolidity/codegen/YulUtilFunctions.cpp | 70 ++++++++++++++-- libsolidity/codegen/YulUtilFunctions.h | 4 + .../codegen/ir/IRGeneratorForStatements.cpp | 82 +++++++++---------- .../array/push/nested_bytes_push.sol | 9 +- ...string_literal_assign_to_storage_bytes.sol | 22 +++++ 8 files changed, 176 insertions(+), 83 deletions(-) create mode 100644 test/libsolidity/semanticTests/array/string_literal_assign_to_storage_bytes.sol diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c4466e398..06d6a0fd9 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2613,23 +2613,6 @@ Type const* TupleType::mobileType() const return TypeProvider::tuple(move(mobiles)); } -Type const* TupleType::closestTemporaryType(Type const* _targetType) const -{ - solAssert(!!_targetType, ""); - TypePointers const& targetComponents = dynamic_cast(*_targetType).components(); - solAssert(components().size() == targetComponents.size(), ""); - TypePointers tempComponents(targetComponents.size()); - for (size_t i = 0; i < targetComponents.size(); ++i) - { - if (components()[i] && targetComponents[i]) - { - tempComponents[i] = components()[i]->closestTemporaryType(targetComponents[i]); - solAssert(tempComponents[i], ""); - } - } - return TypeProvider::tuple(move(tempComponents)); -} - FunctionType::FunctionType(FunctionDefinition const& _function, Kind _kind): m_kind(_kind), m_stateMutability(_function.stateMutability()), diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 95aa3150f..a8cb90d44 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -324,13 +324,6 @@ public: /// @returns true if this is a non-value type and the data of this type is stored at the /// given location. virtual bool dataStoredIn(DataLocation) const { return false; } - /// @returns the type of a temporary during assignment to a variable of the given type. - /// Specifically, returns the requested itself if it can be dynamically allocated (or is a value type) - /// and the mobile type otherwise. - virtual Type const* closestTemporaryType(Type const* _targetType) const - { - return _targetType->dataStoredIn(DataLocation::Storage) ? mobileType() : _targetType; - } /// Returns the list of all members of this type. Default implementation: no members apart from bound. /// @param _currentScope scope in which the members are accessed. @@ -1103,8 +1096,6 @@ public: u256 storageSize() const override; bool hasSimpleZeroValueInMemory() const override { return false; } Type const* mobileType() const override; - /// Converts components to their temporary types and performs some wildcard matching. - Type const* closestTemporaryType(Type const* _targetType) const override; std::vector const& components() const { return m_components; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index a8e7dd5b7..5b092363b 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -49,6 +49,34 @@ using namespace solidity::frontend; using namespace solidity::langutil; using namespace solidity::util; +namespace +{ + +Type const* closestType(Type const* _type, Type const* _targetType, bool _isShiftOp) +{ + if (_isShiftOp) + return _type->mobileType(); + else if (auto const* tupleType = dynamic_cast(_type)) + { + solAssert(_targetType, ""); + TypePointers const& targetComponents = dynamic_cast(*_targetType).components(); + solAssert(tupleType->components().size() == targetComponents.size(), ""); + TypePointers tempComponents(targetComponents.size()); + for (size_t i = 0; i < targetComponents.size(); ++i) + { + if (tupleType->components()[i] && targetComponents[i]) + { + tempComponents[i] = closestType(tupleType->components()[i], targetComponents[i], _isShiftOp); + solAssert(tempComponents[i], ""); + } + } + return TypeProvider::tuple(move(tempComponents)); + } + else + return _targetType->dataStoredIn(DataLocation::Storage) ? _type->mobileType() : _targetType; +} + +} void ExpressionCompiler::compile(Expression const& _expression) { @@ -280,13 +308,12 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) _assignment.rightHandSide().accept(*this); // Perform some conversion already. This will convert storage types to memory and literals // to their actual type, but will not convert e.g. memory to storage. - Type const* rightIntermediateType; - if (op != Token::Assign && TokenTraits::isShiftOp(binOp)) - rightIntermediateType = _assignment.rightHandSide().annotation().type->mobileType(); - else - rightIntermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType( - _assignment.leftHandSide().annotation().type - ); + Type const* rightIntermediateType = closestType( + _assignment.rightHandSide().annotation().type, + _assignment.leftHandSide().annotation().type, + op != Token::Assign && TokenTraits::isShiftOp(binOp) + ); + solAssert(rightIntermediateType, ""); utils().convertType(*_assignment.rightHandSide().annotation().type, *rightIntermediateType, cleanupNeeded); @@ -1016,7 +1043,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // stack: argValue storageSlot slotOffset utils().moveToStackTop(2, argType->sizeOnStack()); // stack: storageSlot slotOffset argValue - Type const* type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType()); + Type const* type = + arrayType->baseType()->dataStoredIn(DataLocation::Storage) ? + arguments[0]->annotation().type->mobileType() : + arrayType->baseType(); solAssert(type, ""); utils().convertType(*argType, *type); utils().moveToStackTop(1 + type->sizeOnStack()); diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 26c7d0df2..d0bde42c1 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -152,6 +152,54 @@ string YulUtilFunctions::storeLiteralInMemoryFunction(string const& _literal) }); } +string YulUtilFunctions::copyLiteralToStorageFunction(string const& _literal) +{ + string functionName = "copy_literal_to_storage_" + util::toHex(util::keccak256(_literal).asBytes()); + + return m_functionCollector.createFunction(functionName, [&](vector& _args, vector&) { + _args = {"slot"}; + + if (_literal.size() >= 32) + { + size_t words = (_literal.length() + 31) / 32; + vector> wordParams(words); + for (size_t i = 0; i < words; ++i) + { + wordParams[i]["offset"] = to_string(i); + wordParams[i]["wordValue"] = formatAsStringOrNumber(_literal.substr(32 * i, 32)); + } + return Whiskers(R"( + let oldLen := (sload(slot)) + (slot, oldLen, ) + sstore(slot, ) + let dstPtr := (slot) + <#word> + sstore(add(dstPtr, ), ) + + )") + ("byteArrayLength", extractByteArrayLengthFunction()) + ("cleanUpArrayEnd", cleanUpDynamicByteArrayEndSlotsFunction(*TypeProvider::bytesStorage())) + ("dataArea", arrayDataAreaFunction(*TypeProvider::bytesStorage())) + ("word", wordParams) + ("length", to_string(_literal.size())) + ("encodedLen", to_string(2 * _literal.size() + 1)) + .render(); + } + else + return Whiskers(R"( + let oldLen := (sload(slot)) + (slot, oldLen, ) + sstore(slot, add(, )) + )") + ("byteArrayLength", extractByteArrayLengthFunction()) + ("cleanUpArrayEnd", cleanUpDynamicByteArrayEndSlotsFunction(*TypeProvider::bytesStorage())) + ("wordValue", formatAsStringOrNumber(_literal)) + ("length", to_string(_literal.size())) + ("encodedLen", to_string(2 * _literal.size())) + .render(); + }); +} + string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _messageType) { string functionName = @@ -2680,15 +2728,13 @@ string YulUtilFunctions::updateStorageValueFunction( return Whiskers(R"( function (slot, offset) { if offset { () } - let value := () - (slot, value) + (slot) } )") ("functionName", functionName) ("dynamicOffset", !_offset.has_value()) ("panic", panicFunction(PanicCode::Generic)) - ("copyLiteralToMemory", copyLiteralToMemoryFunction(dynamic_cast(_fromType).value())) - ("copyToStorage", copyArrayToStorageFunction(*TypeProvider::bytesMemory(), toArrayType)) + ("copyToStorage", copyLiteralToStorageFunction(dynamic_cast(_fromType).value())) .render(); } @@ -2697,7 +2743,10 @@ string YulUtilFunctions::updateStorageValueFunction( fromReferenceType->isPointer() ).get() == *fromReferenceType, ""); - solAssert(toReferenceType->category() == fromReferenceType->category(), ""); + if (fromReferenceType->category() == Type::Category::ArraySlice) + solAssert(toReferenceType->category() == Type::Category::Array, ""); + else + solAssert(toReferenceType->category() == fromReferenceType->category(), ""); solAssert(_offset.value_or(0) == 0, ""); Whiskers templ(R"( @@ -2715,6 +2764,17 @@ string YulUtilFunctions::updateStorageValueFunction( dynamic_cast(_fromType), dynamic_cast(_toType) )); + else if (_fromType.category() == Type::Category::ArraySlice) + { + solAssert( + _fromType.dataStoredIn(DataLocation::CallData), + "Currently only calldata array slices are supported!" + ); + templ("copyToStorage", copyArrayToStorageFunction( + dynamic_cast(_fromType).arrayType(), + dynamic_cast(_toType) + )); + } else templ("copyToStorage", copyStructToStorageFunction( dynamic_cast(_fromType), diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 30f109b51..b71f0f9a7 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -81,6 +81,10 @@ public: /// signature: (memPtr) -> std::string storeLiteralInMemoryFunction(std::string const& _literal); + /// @returns the name of a function that stores a string literal at a specific location in storage + /// signature: (slot) -> + std::string copyLiteralToStorageFunction(std::string const& _literal); + // @returns the name of a function that has the equivalent logic of an // `assert` or `require` call. std::string requireOrAssertFunction(bool _assert, Type const* _messageType = nullptr); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index aeda4a794..7c00338b1 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -238,9 +238,6 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va _varDecl.value()->accept(*this); - Type const* rightIntermediateType = _varDecl.value()->annotation().type->closestTemporaryType(_varDecl.type()); - solAssert(rightIntermediateType, ""); - IRVariable value = convert(*_varDecl.value(), *rightIntermediateType); writeToLValue( _varDecl.immutable() ? IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} : @@ -248,7 +245,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first), m_context.storageLocationOfStateVariable(_varDecl).second }}, - value + *_varDecl.value() ); } catch (langutil::UnimplementedFeatureError const& _error) @@ -407,55 +404,49 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) assignmentOperator : TokenTraits::AssignmentToBinaryOp(assignmentOperator); - Type const* rightIntermediateType = - TokenTraits::isShiftOp(binaryOperator) ? - type(_assignment.rightHandSide()).mobileType() : - type(_assignment.rightHandSide()).closestTemporaryType( - &type(_assignment.leftHandSide()) - ); - solAssert(rightIntermediateType, ""); - IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType); + if (TokenTraits::isShiftOp(binaryOperator)) + solAssert(type(_assignment.rightHandSide()).mobileType(), ""); + IRVariable value = + type(_assignment.leftHandSide()).isValueType() ? + convert( + _assignment.rightHandSide(), + TokenTraits::isShiftOp(binaryOperator) ? *type(_assignment.rightHandSide()).mobileType() : type(_assignment) + ) : + _assignment.rightHandSide(); + _assignment.leftHandSide().accept(*this); + solAssert(!!m_currentLValue, "LValue not retrieved."); setLocation(_assignment); if (assignmentOperator != Token::Assign) { solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types."); - solAssert(rightIntermediateType->isValueType(), "Compound operators only available for value types."); - IRVariable leftIntermediate = readFromLValue(*m_currentLValue); solAssert(binaryOperator != Token::Exp, ""); - if (TokenTraits::isShiftOp(binaryOperator)) - { - solAssert(type(_assignment) == leftIntermediate.type(), ""); - solAssert(type(_assignment) == type(_assignment.leftHandSide()), ""); - define(_assignment) << shiftOperation(binaryOperator, leftIntermediate, value) << "\n"; + solAssert(type(_assignment) == type(_assignment.leftHandSide()), ""); - writeToLValue(*m_currentLValue, IRVariable(_assignment)); - m_currentLValue.reset(); - return false; - } - else - { - solAssert(type(_assignment.leftHandSide()) == *rightIntermediateType, ""); - m_code << value.name() << " := " << binaryOperation( - binaryOperator, - *rightIntermediateType, - leftIntermediate.name(), - value.name() - ); - } + IRVariable leftIntermediate = readFromLValue(*m_currentLValue); + solAssert(type(_assignment) == leftIntermediate.type(), ""); + + define(_assignment) << ( + TokenTraits::isShiftOp(binaryOperator) ? + shiftOperation(binaryOperator, leftIntermediate, value) : + binaryOperation(binaryOperator, type(_assignment), leftIntermediate.name(), value.name()) + ) << "\n"; + + writeToLValue(*m_currentLValue, IRVariable(_assignment)); + } + else + { + writeToLValue(*m_currentLValue, value); + + if (dynamic_cast(&m_currentLValue->type)) + define(_assignment, readFromLValue(*m_currentLValue)); + else if (*_assignment.annotation().type != *TypeProvider::emptyTuple()) + define(_assignment, value); } - writeToLValue(*m_currentLValue, value); - - if (dynamic_cast(&m_currentLValue->type)) - define(_assignment, readFromLValue(*m_currentLValue)); - else if (*_assignment.annotation().type != *TypeProvider::emptyTuple()) - define(_assignment, value); - m_currentLValue.reset(); - return false; } @@ -2857,10 +2848,17 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable prepared.commaSeparatedList() << ")\n"; } + else if (auto const* literalType = dynamic_cast(&_value.type())) + m_code << + m_utils.writeToMemoryFunction(*TypeProvider::uint256()) << + "(" << + _memory.address << + ", " << + m_utils.copyLiteralToMemoryFunction(literalType->value()) + "()" << + ")\n"; else { solAssert(_lvalue.type.sizeOnStack() == 1, ""); - solAssert(dynamic_cast(&_lvalue.type), ""); auto const* valueReferenceType = dynamic_cast(&_value.type()); solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory), ""); m_code << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n"; diff --git a/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol b/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol index f7cc0cc59..23dce6f6c 100644 --- a/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol +++ b/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol @@ -4,12 +4,17 @@ contract C { function f() public { a.push("abc"); - a.push("def"); + a.push("abcdefghabcdefghabcdefghabcdefgh"); + a.push("abcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefghabcdefgh"); assert(a[0][0] == "a"); - assert(a[1][0] == "d"); + assert(a[1][31] == "h"); + assert(a[2][32] == "a"); } } // ==== // compileViaYul: also // ---- // f() -> +// gas irOptimized: 181480 +// gas legacy: 180320 +// gas legacyOptimized: 180103 diff --git a/test/libsolidity/semanticTests/array/string_literal_assign_to_storage_bytes.sol b/test/libsolidity/semanticTests/array/string_literal_assign_to_storage_bytes.sol new file mode 100644 index 000000000..ee3cda2d1 --- /dev/null +++ b/test/libsolidity/semanticTests/array/string_literal_assign_to_storage_bytes.sol @@ -0,0 +1,22 @@ +contract C { + bytes public s = "abc"; + bytes public s1 = "abcd"; + function f() public { + s = "abcd"; + s1 = "abc"; + } + function g() public { + (s, s1) = ("abc", "abcd"); + } +} +// ==== +// compileViaYul: also +// ---- +// s() -> 0x20, 3, "abc" +// s1() -> 0x20, 4, "abcd" +// f() -> +// s() -> 0x20, 4, "abcd" +// s1() -> 0x20, 3, "abc" +// g() -> +// s() -> 0x20, 3, "abc" +// s1() -> 0x20, 4, "abcd" From 53e3081e8110ac3601d2505858a5d43f832f1a85 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Wed, 5 May 2021 08:59:05 +0200 Subject: [PATCH 045/235] Update gas costs in tests. --- .../array/copying/copy_byte_array_in_struct_to_storage.sol | 2 +- .../array/copying/copy_byte_array_to_storage.sol | 2 +- .../array/copying/storage_memory_nested_bytes.sol | 2 +- .../semanticTests/array/push/nested_bytes_push.sol | 2 +- .../semanticTests/externalContracts/ramanujan_pi.sol | 4 ++-- .../semanticTests/various/skip_dynamic_types_for_structs.sol | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol index 65c5d3447..714dbd859 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_in_struct_to_storage.sol @@ -37,7 +37,7 @@ contract C { // compileViaYul: also // ---- // f() -> 0x40, 0x80, 6, 0x6162636465660000000000000000000000000000000000000000000000000000, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000 -// gas irOptimized: 172282 +// gas irOptimized: 172274 // gas legacy: 174794 // gas legacyOptimized: 174188 // g() -> 0x40, 0xc0, 0x49, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738393031323334353637383930313233343536373839303120, 0x3132333435363738390000000000000000000000000000000000000000000000, 0x11, 0x3132333435363738393233343536373839000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol index 769cecae9..1ad608065 100644 --- a/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/copy_byte_array_to_storage.sol @@ -48,6 +48,6 @@ contract C { // compileViaYul: also // ---- // f() -> 0xff -// gas irOptimized: 136027 +// gas irOptimized: 132909 // gas legacy: 137645 // gas legacyOptimized: 134376 diff --git a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol index c98f762eb..19b3fad38 100644 --- a/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol +++ b/test/libsolidity/semanticTests/array/copying/storage_memory_nested_bytes.sol @@ -13,6 +13,6 @@ contract C { // compileViaYul: also // ---- // f() -> 0x20, 0x02, 0x40, 0x80, 3, 0x6162630000000000000000000000000000000000000000000000000000000000, 0x99, 44048183304486788312148433451363384677562265908331949128489393215789685032262, 32241931068525137014058842823026578386641954854143559838526554899205067598957, 49951309422467613961193228765530489307475214998374779756599339590522149884499, 0x54555658595a6162636465666768696a6b6c6d6e6f707172737475767778797a, 0x4142434445464748494a4b4c4d4e4f5051525354555658595a00000000000000 -// gas irOptimized: 198253 +// gas irOptimized: 197063 // gas legacy: 199159 // gas legacyOptimized: 198137 diff --git a/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol b/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol index 23dce6f6c..48cac5145 100644 --- a/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol +++ b/test/libsolidity/semanticTests/array/push/nested_bytes_push.sol @@ -15,6 +15,6 @@ contract C { // compileViaYul: also // ---- // f() -> -// gas irOptimized: 181480 +// gas irOptimized: 178367 // gas legacy: 180320 // gas legacyOptimized: 180103 diff --git a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol index b280cfa33..bb56101aa 100644 --- a/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol +++ b/test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol @@ -35,10 +35,10 @@ contract test { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 590192 +// gas irOptimized: 591094 // gas legacy: 733634 // gas legacyOptimized: 498033 // prb_pi() -> 3141592656369545286 -// gas irOptimized: 66098 +// gas irOptimized: 66108 // gas legacy: 98903 // gas legacyOptimized: 75735 diff --git a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol index aabe52688..60b7c21f4 100644 --- a/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol +++ b/test/libsolidity/semanticTests/various/skip_dynamic_types_for_structs.sol @@ -22,6 +22,6 @@ contract C { // compileViaYul: also // ---- // g() -> 2, 6 -// gas irOptimized: 169790 +// gas irOptimized: 169460 // gas legacy: 172490 // gas legacyOptimized: 171209 From 39b23420ec93d7e0f72ef53dea103c9bc3af9c8e Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Thu, 6 May 2021 11:22:35 +0200 Subject: [PATCH 046/235] Extracted the class SMT Solver from ReasoningBasedSimplifier --- libyul/CMakeLists.txt | 2 + libyul/optimiser/ReasoningBasedSimplifier.cpp | 132 ++------------- libyul/optimiser/ReasoningBasedSimplifier.h | 31 +--- libyul/optimiser/SMTSolver.cpp | 160 ++++++++++++++++++ libyul/optimiser/SMTSolver.h | 92 ++++++++++ 5 files changed, 271 insertions(+), 146 deletions(-) create mode 100644 libyul/optimiser/SMTSolver.cpp create mode 100644 libyul/optimiser/SMTSolver.h diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 77d09706a..80c83ebc2 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -166,6 +166,8 @@ add_library(yul optimiser/RedundantAssignEliminator.h optimiser/Rematerialiser.cpp optimiser/Rematerialiser.h + optimiser/SMTSolver.cpp + optimiser/SMTSolver.h optimiser/SSAReverser.cpp optimiser/SSAReverser.h optimiser/SSATransform.cpp diff --git a/libyul/optimiser/ReasoningBasedSimplifier.cpp b/libyul/optimiser/ReasoningBasedSimplifier.cpp index e75ca3cf5..96924a06d 100644 --- a/libyul/optimiser/ReasoningBasedSimplifier.cpp +++ b/libyul/optimiser/ReasoningBasedSimplifier.cpp @@ -16,8 +16,8 @@ */ #include +#include -#include #include #include #include @@ -56,14 +56,7 @@ std::optional ReasoningBasedSimplifier::invalidInCurrentEnvironment() void ReasoningBasedSimplifier::operator()(VariableDeclaration& _varDecl) { - if (_varDecl.variables.size() != 1 || !_varDecl.value) - return; - YulString varName = _varDecl.variables.front().name; - if (!m_ssaVariables.count(varName)) - return; - bool const inserted = m_variables.insert({varName, m_solver->newVariable("yul_" + varName.str(), defaultSort())}).second; - yulAssert(inserted, ""); - m_solver->addAssertion(m_variables.at(varName) == encodeExpression(*_varDecl.value)); + SMTSolver::encodeVariableDeclaration(_varDecl); } void ReasoningBasedSimplifier::operator()(If& _if) @@ -111,37 +104,11 @@ ReasoningBasedSimplifier::ReasoningBasedSimplifier( Dialect const& _dialect, set const& _ssaVariables ): - m_dialect(_dialect), - m_ssaVariables(_ssaVariables), - m_solver(make_unique()) + SMTSolver(_ssaVariables, _dialect), + m_dialect(_dialect) { } -smtutil::Expression ReasoningBasedSimplifier::encodeExpression(yul::Expression const& _expression) -{ - return std::visit(GenericVisitor{ - [&](FunctionCall const& _functionCall) - { - if (auto instruction = toEVMInstruction(m_dialect, _functionCall.functionName.name)) - return encodeEVMBuiltin(*instruction, _functionCall.arguments); - return newRestrictedVariable(); - }, - [&](Identifier const& _identifier) - { - if ( - m_ssaVariables.count(_identifier.name) && - m_variables.count(_identifier.name) - ) - return m_variables.at(_identifier.name); - else - return newRestrictedVariable(); - }, - [&](Literal const& _literal) - { - return literalValue(_literal); - } - }, _expression); -} smtutil::Expression ReasoningBasedSimplifier::encodeEVMBuiltin( evmasm::Instruction _instruction, @@ -172,10 +139,10 @@ smtutil::Expression ReasoningBasedSimplifier::encodeEVMBuiltin( constantValue(0), // No `wrap()` needed here, because -2**255 / -1 results // in 2**255 which is "converted" to its two's complement - // representation 2**255 in `signedToUnsigned` - signedToUnsigned(smtutil::signedDivisionEVM( - unsignedToSigned(arguments.at(0)), - unsignedToSigned(arguments.at(1)) + // representation 2**255 in `signedToTwosComplement` + signedToTwosComplement(smtutil::signedDivisionEVM( + twosComplementToSigned(arguments.at(0)), + twosComplementToSigned(arguments.at(1)) )) ); case evmasm::Instruction::MOD: @@ -188,19 +155,19 @@ smtutil::Expression ReasoningBasedSimplifier::encodeEVMBuiltin( return smtutil::Expression::ite( arguments.at(1) == constantValue(0), constantValue(0), - signedToUnsigned(signedModuloEVM( - unsignedToSigned(arguments.at(0)), - unsignedToSigned(arguments.at(1)) + signedToTwosComplement(signedModuloEVM( + twosComplementToSigned(arguments.at(0)), + twosComplementToSigned(arguments.at(1)) )) ); case evmasm::Instruction::LT: return booleanValue(arguments.at(0) < arguments.at(1)); case evmasm::Instruction::SLT: - return booleanValue(unsignedToSigned(arguments.at(0)) < unsignedToSigned(arguments.at(1))); + return booleanValue(twosComplementToSigned(arguments.at(0)) < twosComplementToSigned(arguments.at(1))); case evmasm::Instruction::GT: return booleanValue(arguments.at(0) > arguments.at(1)); case evmasm::Instruction::SGT: - return booleanValue(unsignedToSigned(arguments.at(0)) > unsignedToSigned(arguments.at(1))); + return booleanValue(twosComplementToSigned(arguments.at(0)) > twosComplementToSigned(arguments.at(1))); case evmasm::Instruction::EQ: return booleanValue(arguments.at(0) == arguments.at(1)); case evmasm::Instruction::ISZERO: @@ -259,76 +226,3 @@ smtutil::Expression ReasoningBasedSimplifier::encodeEVMBuiltin( } return newRestrictedVariable(); } - -smtutil::Expression ReasoningBasedSimplifier::int2bv(smtutil::Expression _arg) const -{ - return smtutil::Expression::int2bv(std::move(_arg), 256); -} - -smtutil::Expression ReasoningBasedSimplifier::bv2int(smtutil::Expression _arg) const -{ - return smtutil::Expression::bv2int(std::move(_arg)); -} - -smtutil::Expression ReasoningBasedSimplifier::newVariable() -{ - return m_solver->newVariable(uniqueName(), defaultSort()); -} - -smtutil::Expression ReasoningBasedSimplifier::newRestrictedVariable() -{ - smtutil::Expression var = newVariable(); - m_solver->addAssertion(0 <= var && var < smtutil::Expression(bigint(1) << 256)); - return var; -} - -string ReasoningBasedSimplifier::uniqueName() -{ - return "expr_" + to_string(m_varCounter++); -} - -shared_ptr ReasoningBasedSimplifier::defaultSort() const -{ - return SortProvider::intSort(); -} - -smtutil::Expression ReasoningBasedSimplifier::booleanValue(smtutil::Expression _value) const -{ - return smtutil::Expression::ite(_value, constantValue(1), constantValue(0)); -} - -smtutil::Expression ReasoningBasedSimplifier::constantValue(size_t _value) const -{ - return _value; -} - -smtutil::Expression ReasoningBasedSimplifier::literalValue(Literal const& _literal) const -{ - return smtutil::Expression(valueOfLiteral(_literal)); -} - -smtutil::Expression ReasoningBasedSimplifier::unsignedToSigned(smtutil::Expression _value) -{ - return smtutil::Expression::ite( - _value < smtutil::Expression(bigint(1) << 255), - _value, - _value - smtutil::Expression(bigint(1) << 256) - ); -} - -smtutil::Expression ReasoningBasedSimplifier::signedToUnsigned(smtutil::Expression _value) -{ - return smtutil::Expression::ite( - _value >= 0, - _value, - _value + smtutil::Expression(bigint(1) << 256) - ); -} - -smtutil::Expression ReasoningBasedSimplifier::wrap(smtutil::Expression _value) -{ - smtutil::Expression rest = newRestrictedVariable(); - smtutil::Expression multiplier = newVariable(); - m_solver->addAssertion(_value == multiplier * smtutil::Expression(bigint(1) << 256) + rest); - return rest; -} diff --git a/libyul/optimiser/ReasoningBasedSimplifier.h b/libyul/optimiser/ReasoningBasedSimplifier.h index 18fcf3583..c458a36d0 100644 --- a/libyul/optimiser/ReasoningBasedSimplifier.h +++ b/libyul/optimiser/ReasoningBasedSimplifier.h @@ -16,6 +16,7 @@ */ #pragma once +#include #include #include #include @@ -46,7 +47,7 @@ namespace solidity::yul * * Prerequisite: Disambiguator, SSATransform. */ -class ReasoningBasedSimplifier: public ASTModifier +class ReasoningBasedSimplifier: public ASTModifier, SMTSolver { public: static constexpr char const* name{"ReasoningBasedSimplifier"}; @@ -63,36 +64,12 @@ private: std::set const& _ssaVariables ); - smtutil::Expression encodeExpression( - Expression const& _expression - ); - - virtual smtutil::Expression encodeEVMBuiltin( + smtutil::Expression encodeEVMBuiltin( evmasm::Instruction _instruction, std::vector const& _arguments - ); - - smtutil::Expression int2bv(smtutil::Expression _arg) const; - smtutil::Expression bv2int(smtutil::Expression _arg) const; - - smtutil::Expression newVariable(); - virtual smtutil::Expression newRestrictedVariable(); - std::string uniqueName(); - - virtual std::shared_ptr defaultSort() const; - virtual smtutil::Expression booleanValue(smtutil::Expression _value) const; - virtual smtutil::Expression constantValue(size_t _value) const; - virtual smtutil::Expression literalValue(Literal const& _literal) const; - virtual smtutil::Expression unsignedToSigned(smtutil::Expression _value); - virtual smtutil::Expression signedToUnsigned(smtutil::Expression _value); - virtual smtutil::Expression wrap(smtutil::Expression _value); + ) override; Dialect const& m_dialect; - std::set const& m_ssaVariables; - std::unique_ptr m_solver; - std::map m_variables; - - size_t m_varCounter = 0; }; } diff --git a/libyul/optimiser/SMTSolver.cpp b/libyul/optimiser/SMTSolver.cpp new file mode 100644 index 000000000..c0c6bcb5e --- /dev/null +++ b/libyul/optimiser/SMTSolver.cpp @@ -0,0 +1,160 @@ +/* + 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 . +*/ + +#include +#include + +#include +#include + +#include +#include + +#include +#include + +using namespace solidity::yul; +using namespace solidity::util; +using namespace solidity::smtutil; +using namespace solidity; +using namespace std; + +SMTSolver::SMTSolver(set const& _ssaVariables, Dialect const& _dialect): + m_ssaVariables(_ssaVariables), + m_solver(make_unique()), + m_dialect(_dialect) +{ } + +smtutil::Expression SMTSolver::encodeExpression(Expression const& _expression) +{ + return std::visit(GenericVisitor{ + [&](FunctionCall const& _functionCall) + { + if (auto instruction = toEVMInstruction(m_dialect, _functionCall.functionName.name)) + return encodeEVMBuiltin(*instruction, _functionCall.arguments); + + return newRestrictedVariable(); + }, + [&](Identifier const& _identifier) + { + if ( + m_ssaVariables.count(_identifier.name) && + m_variables.count(_identifier.name) + ) + return m_variables.at(_identifier.name); + else + return newRestrictedVariable(); + }, + [&](Literal const& _literal) + { + return literalValue(_literal); + } + }, _expression); +} + +smtutil::Expression SMTSolver::int2bv(smtutil::Expression _arg) +{ + return smtutil::Expression::int2bv(std::move(_arg), 256); +} + +smtutil::Expression SMTSolver::bv2int(smtutil::Expression _arg) +{ + return smtutil::Expression::bv2int(std::move(_arg)); +} + +smtutil::Expression SMTSolver::newVariable() +{ + return m_solver->newVariable(uniqueName(), defaultSort()); +} + +smtutil::Expression SMTSolver::newRestrictedVariable(bigint _maxValue) +{ + smtutil::Expression var = newVariable(); + m_solver->addAssertion(0 <= var && var <= smtutil::Expression(_maxValue)); + return var; +} + +string SMTSolver::uniqueName() +{ + return "expr_" + to_string(m_varCounter++); +} + +shared_ptr SMTSolver::defaultSort() const +{ + return SortProvider::intSort(); +} + +smtutil::Expression SMTSolver::booleanValue(smtutil::Expression _value) +{ + return smtutil::Expression::ite(_value, constantValue(1), constantValue(0)); +} + +smtutil::Expression SMTSolver::constantValue(bigint _value) +{ + return _value; +} + +smtutil::Expression SMTSolver::literalValue(Literal const& _literal) +{ + return smtutil::Expression(valueOfLiteral(_literal)); +} + +smtutil::Expression SMTSolver::twosComplementToSigned(smtutil::Expression _value) +{ + return smtutil::Expression::ite( + _value < smtutil::Expression(bigint(1) << 255), + _value, + _value - smtutil::Expression(bigint(1) << 256) + ); +} + +smtutil::Expression SMTSolver::signedToTwosComplement(smtutil::Expression _value) +{ + return smtutil::Expression::ite( + _value >= 0, + _value, + _value + smtutil::Expression(bigint(1) << 256) + ); +} + +smtutil::Expression SMTSolver::wrap(smtutil::Expression _value) +{ + smtutil::Expression rest = newRestrictedVariable(); + smtutil::Expression multiplier = newVariable(); + m_solver->addAssertion(_value == multiplier * smtutil::Expression(bigint(1) << 256) + rest); + return rest; +} + +void SMTSolver::encodeVariableDeclaration(VariableDeclaration const& _varDecl) +{ + if ( + _varDecl.variables.size() == 1 && + _varDecl.value && + m_ssaVariables.count(_varDecl.variables.front().name) + ) + { + YulString const& variableName = _varDecl.variables.front().name; + bool const inserted = m_variables.insert({ + variableName, + m_solver->newVariable("yul_" + variableName.str(), defaultSort()) + }).second; + yulAssert(inserted, ""); + m_solver->addAssertion( + m_variables.at(variableName) == encodeExpression(*_varDecl.value) + ); + } +} diff --git a/libyul/optimiser/SMTSolver.h b/libyul/optimiser/SMTSolver.h new file mode 100644 index 000000000..d92a84c1d --- /dev/null +++ b/libyul/optimiser/SMTSolver.h @@ -0,0 +1,92 @@ +/* + 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 . +*/ +#pragma once + +#include +#include +#include + +// because of instruction +#include + +#include + +#include + +namespace solidity::smtutil +{ + +class SolverInterface; +class Expression; +struct Sort; + +} + +namespace solidity::yul +{ + +/** + * Base class for SMT based optimization steps. + * + * Works best when used with AST in SSA form. + */ +class SMTSolver +{ +protected: + SMTSolver( + std::set const& _ssaVariables, + Dialect const& _dialect + ); + + /// Helper function that encodes VariableDeclaration + void encodeVariableDeclaration(VariableDeclaration const& _varDecl); + + /// The encoding for a builtin. The type of encoding determines what we are + /// solving for. + virtual smtutil::Expression encodeEVMBuiltin( + evmasm::Instruction _instruction, + std::vector const& _arguments + ) = 0; + + smtutil::Expression encodeExpression(Expression const& _expression); + + static smtutil::Expression int2bv(smtutil::Expression _arg); + static smtutil::Expression bv2int(smtutil::Expression _arg); + + smtutil::Expression newVariable(); + virtual smtutil::Expression newRestrictedVariable(bigint _maxValue = (bigint(1) << 256) - 1); + std::string uniqueName(); + + virtual std::shared_ptr defaultSort() const; + static smtutil::Expression booleanValue(smtutil::Expression _value); + static smtutil::Expression constantValue(bigint _value); + static smtutil::Expression literalValue(Literal const& _literal); + static smtutil::Expression twosComplementToSigned(smtutil::Expression _value); + static smtutil::Expression signedToTwosComplement(smtutil::Expression _value); + smtutil::Expression wrap(smtutil::Expression _value); + + std::set const& m_ssaVariables; + std::unique_ptr m_solver; + std::map m_variables; + + Dialect const& m_dialect; + +private: + size_t m_varCounter = 0; +}; + +} From c69add1682e76f83a64058ddbb931acd07661f89 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 7 May 2021 13:44:14 +0200 Subject: [PATCH 047/235] Remove import of ranges namespace. --- libsolidity/codegen/ir/IRGenerator.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 20a434e13..14541e516 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -46,7 +46,6 @@ #include using namespace std; -using namespace ranges; using namespace solidity; using namespace solidity::util; using namespace solidity::frontend; @@ -80,7 +79,7 @@ set collectReachableCallables( ) { set reachableCallables; - for (CallGraph::Node const& reachableNode: _graph.edges | views::keys) + for (CallGraph::Node const& reachableNode: _graph.edges | ranges::views::keys) if (holds_alternative(reachableNode)) reachableCallables.emplace(get(reachableNode)); From 6104ac1cdfc11c523d99c2f7af196d98d6201eda Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 7 May 2021 15:42:17 +0200 Subject: [PATCH 048/235] Remove more imports of ranges namespace. --- libsolidity/analysis/FunctionCallGraph.cpp | 7 +++--- libsolidity/ast/ASTJsonConverter.cpp | 3 +-- libsolidity/formal/ModelCheckerSettings.cpp | 9 ++++--- .../analysis/FunctionCallGraph.cpp | 24 +++++++++++-------- test/tools/yulopti.cpp | 15 ++++++------ 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/libsolidity/analysis/FunctionCallGraph.cpp b/libsolidity/analysis/FunctionCallGraph.cpp index 92cd10cd9..0a4a4b437 100644 --- a/libsolidity/analysis/FunctionCallGraph.cpp +++ b/libsolidity/analysis/FunctionCallGraph.cpp @@ -25,7 +25,6 @@ #include using namespace std; -using namespace ranges; using namespace solidity::frontend; using namespace solidity::util; @@ -35,7 +34,7 @@ CallGraph FunctionCallGraphBuilder::buildCreationGraph(ContractDefinition const& solAssert(builder.m_currentNode == CallGraph::Node(CallGraph::SpecialNode::Entry), ""); // Create graph for constructor, state vars, etc - for (ContractDefinition const* base: _contract.annotation().linearizedBaseContracts | views::reverse) + for (ContractDefinition const* base: _contract.annotation().linearizedBaseContracts | ranges::views::reverse) { // The constructor and functions called in state variable initial assignments should have // an edge from Entry @@ -76,7 +75,7 @@ CallGraph FunctionCallGraphBuilder::buildDeployedGraph( auto getSecondElement = [](auto const& _tuple){ return get<1>(_tuple); }; // Create graph for all publicly reachable functions - for (FunctionTypePointer functionType: _contract.interfaceFunctionList() | views::transform(getSecondElement)) + for (FunctionTypePointer functionType: _contract.interfaceFunctionList() | ranges::views::transform(getSecondElement)) { auto const* function = dynamic_cast(&functionType->declaration()); auto const* variable = dynamic_cast(&functionType->declaration()); @@ -305,7 +304,7 @@ ostream& solidity::frontend::operator<<(ostream& _out, CallGraph::Node const& _n auto const* modifier = dynamic_cast(callableDeclaration); auto typeToString = [](auto const& _var) -> string { return _var->type()->toString(true); }; - vector parameters = callableDeclaration->parameters() | views::transform(typeToString) | to>(); + vector parameters = callableDeclaration->parameters() | ranges::views::transform(typeToString) | ranges::to>(); string scopeName; if (!function || !function->isFree()) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 40cef6b8e..ea8662010 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -43,7 +43,6 @@ #include #include -using namespace ranges; using namespace std; using namespace solidity::langutil; @@ -271,7 +270,7 @@ bool ASTJsonConverter::visit(ContractDefinition const& _node) make_pair("contractKind", contractKind(_node.contractKind())), make_pair("abstract", _node.abstract()), make_pair("baseContracts", toJson(_node.baseContracts())), - make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies | views::keys)), + make_pair("contractDependencies", getContainerIds(_node.annotation().contractDependencies | ranges::views::keys)), make_pair("usedErrors", getContainerIds(_node.interfaceErrors(false))), make_pair("nodes", toJson(_node.subNodes())), make_pair("scope", idOrNull(_node.scope())) diff --git a/libsolidity/formal/ModelCheckerSettings.cpp b/libsolidity/formal/ModelCheckerSettings.cpp index f815ae95b..2f98b0697 100644 --- a/libsolidity/formal/ModelCheckerSettings.cpp +++ b/libsolidity/formal/ModelCheckerSettings.cpp @@ -22,7 +22,6 @@ #include using namespace std; -using namespace ranges; using namespace solidity; using namespace solidity::frontend; @@ -42,10 +41,10 @@ std::optional ModelCheckerTargets::fromString(string const& { set chosenTargets; if (_targets == "default") - for (auto&& v: targetStrings | views::values) + for (auto&& v: targetStrings | ranges::views::values) chosenTargets.insert(v); else - for (auto&& t: _targets | views::split(',') | ranges::to>()) + for (auto&& t: _targets | ranges::views::split(',') | ranges::to>()) { if (!targetStrings.count(t)) return {}; @@ -69,9 +68,9 @@ std::optional ModelCheckerContracts::fromString(string co if (_contracts == "default") return ModelCheckerContracts::Default(); - for (auto&& sourceContract: _contracts | views::split(',') | ranges::to>()) + for (auto&& sourceContract: _contracts | ranges::views::split(',') | ranges::to>()) { - auto&& names = sourceContract | views::split(':') | ranges::to>(); + auto&& names = sourceContract | ranges::views::split(':') | ranges::to>(); if (names.size() != 2 || names.at(0).empty() || names.at(1).empty()) return {}; chosen[names.at(0)].insert(names.at(1)); diff --git a/test/libsolidity/analysis/FunctionCallGraph.cpp b/test/libsolidity/analysis/FunctionCallGraph.cpp index 44687a7b4..e4cd3d066 100644 --- a/test/libsolidity/analysis/FunctionCallGraph.cpp +++ b/test/libsolidity/analysis/FunctionCallGraph.cpp @@ -47,7 +47,6 @@ #include using namespace std; -using namespace ranges; using namespace solidity::langutil; using namespace solidity::frontend; @@ -134,18 +133,18 @@ void checkCallGraphExpectations( auto notEmpty = [](set const& _set){ return !_set.empty(); }; soltestAssert( - (_expectedCreatedContractSets | views::values | views::remove_if(notEmpty)).empty(), + (_expectedCreatedContractSets | ranges::views::values | ranges::views::remove_if(notEmpty)).empty(), "Contracts that are not expected to create other contracts should not be included in _expectedCreatedContractSets." ); soltestAssert( - (_expectedEdges | views::keys | to()) == (_callGraphs | views::keys | to()) && - (ranges::views::set_difference(_expectedCreatedContractSets | views::keys, _expectedEdges | views::keys)).empty(), + (_expectedEdges | ranges::views::keys | ranges::to()) == (_callGraphs | ranges::views::keys | ranges::to()) && + (ranges::views::set_difference(_expectedCreatedContractSets | ranges::views::keys, _expectedEdges | ranges::views::keys)).empty(), "Contracts listed in expectations do not match contracts actually found in the source file or in other expectations." ); - for (string const& contractName: _expectedEdges | views::keys) + for (string const& contractName: _expectedEdges | ranges::views::keys) { soltestAssert( - (ranges::views::set_difference(valueOrDefault(_expectedCreatedContractSets, contractName, {}), _expectedEdges | views::keys)).empty(), + (ranges::views::set_difference(valueOrDefault(_expectedCreatedContractSets, contractName, {}), _expectedEdges | ranges::views::keys)).empty(), "Inconsistent expectations: contract expected to be created but not to be present in the source file." ); } @@ -153,16 +152,21 @@ void checkCallGraphExpectations( map edges; map> createdContractSets; map> emittedEventSets; - for (string const& contractName: _expectedEdges | views::keys) + for (string const& contractName: _expectedEdges | ranges::views::keys) { soltestAssert(_callGraphs.at(contractName) != nullptr, ""); CallGraph const& callGraph = *_callGraphs.at(contractName); edges[contractName] = edgeNames(callGraph.edges); if (!callGraph.bytecodeDependency.empty()) - createdContractSets[contractName] = callGraph.bytecodeDependency | views::keys | views::transform(getContractName) | to>(); + createdContractSets[contractName] = callGraph.bytecodeDependency | + ranges::views::keys | + ranges::views::transform(getContractName) | + ranges::to>(); if (!callGraph.emittedEvents.empty()) - emittedEventSets[contractName] = callGraph.emittedEvents | views::transform(eventToString) | to>(); + emittedEventSets[contractName] = callGraph.emittedEvents | + ranges::views::transform(eventToString) | + ranges::to>(); } BOOST_CHECK_EQUAL(edges, _expectedEdges); @@ -179,7 +183,7 @@ ostream& operator<<(ostream& _out, EdgeNames const& _edgeNames) ostream& operator<<(ostream& _out, set const& _set) { - _out << "{" << (_set | views::join(", ") | to()) << "}"; + _out << "{" << (_set | ranges::views::join(", ") | ranges::to()) << "}"; return _out; } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index 594d4c19c..3e403976f 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -65,7 +65,6 @@ #include using namespace std; -using namespace ranges; using namespace solidity; using namespace solidity::util; using namespace solidity::langutil; @@ -126,9 +125,9 @@ public: ); vector overlappingAbbreviations = - ranges::views::set_intersection(_extraOptions | views::keys, _optimizationSteps | views::keys) | - views::transform([](char _abbreviation){ return string(1, _abbreviation); }) | - to(); + ranges::views::set_intersection(_extraOptions | ranges::views::keys, _optimizationSteps | ranges::views::keys) | + ranges::views::transform([](char _abbreviation){ return string(1, _abbreviation); }) | + ranges::to(); yulAssert( overlappingAbbreviations.empty(), @@ -140,9 +139,9 @@ public: ); vector> sortedOptions = - views::concat(_optimizationSteps, _extraOptions) | - to>>() | - actions::sort([](tuple const& _a, tuple const& _b) { + ranges::views::concat(_optimizationSteps, _extraOptions) | + ranges::to>>() | + ranges::actions::sort([](tuple const& _a, tuple const& _b) { return ( !boost::algorithm::iequals(get<1>(_a), get<1>(_b)) ? boost::algorithm::lexicographical_compare(get<1>(_a), get<1>(_b), boost::algorithm::is_iless()) : @@ -154,7 +153,7 @@ public: size_t rows = (sortedOptions.size() - 1) / _columns + 1; for (size_t row = 0; row < rows; ++row) { - for (auto const& [key, name]: sortedOptions | views::drop(row) | views::stride(rows)) + for (auto const& [key, name]: sortedOptions | ranges::views::drop(row) | ranges::views::stride(rows)) cout << key << ": " << setw(static_cast(longestDescriptionLength)) << setiosflags(ios::left) << name << " "; cout << endl; From 88e33c9ea38df8c45d02d6ccb89601c630583104 Mon Sep 17 00:00:00 2001 From: William Entriken Date: Sat, 8 May 2021 01:14:22 -0400 Subject: [PATCH 049/235] Match @return word styling to match @param (i.e. sentence case) --- docs/natspec-format.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/natspec-format.rst b/docs/natspec-format.rst index de05f6858..c9d4a9386 100644 --- a/docs/natspec-format.rst +++ b/docs/natspec-format.rst @@ -72,7 +72,7 @@ The following example shows a contract and a function using all available tags. /// @notice Calculate tree age in years, rounded up, for live trees /// @dev The Alexandr N. Tetearing algorithm could increase precision /// @param rings The number of rings from dendrochronological sample - /// @return age in years, rounded up for partial years + /// @return Age in years, rounded up for partial years function age(uint256 rings) external virtual pure returns (uint256) { return rings + 1; } From ea13ad711e330c3159361096f9b0c243de2562fd Mon Sep 17 00:00:00 2001 From: William Entriken Date: Sat, 8 May 2021 15:28:28 -0400 Subject: [PATCH 050/235] This can help a little with triage --- .github/ISSUE_TEMPLATE/documentation_issue.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/documentation_issue.md diff --git a/.github/ISSUE_TEMPLATE/documentation_issue.md b/.github/ISSUE_TEMPLATE/documentation_issue.md new file mode 100644 index 000000000..c0706b5f3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_issue.md @@ -0,0 +1,22 @@ +--- +name: Documentation Issue +about: Solidity documentation. +--- + +## Page + + + +## Abstract + + + +## Pull request + + From 452c17c15e10a7b284967983e8c78e251eb299c6 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 10 May 2021 11:30:11 +0200 Subject: [PATCH 051/235] Change windows boost binary download URL. --- scripts/install_deps.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install_deps.ps1 b/scripts/install_deps.ps1 index ca204729d..e2875f9e0 100644 --- a/scripts/install_deps.ps1 +++ b/scripts/install_deps.ps1 @@ -10,7 +10,7 @@ if ( -not (Test-Path "$PSScriptRoot\..\deps\boost") ) { tar -xf cmake.zip mv cmake-3.18.2-win64-x64 "$PSScriptRoot\..\deps\cmake" - Invoke-WebRequest -URI "https://dl.bintray.com/boostorg/release/1.74.0/source/boost_1_74_0.zip" -OutFile boost.zip + Invoke-WebRequest -URI "https://boostorg.jfrog.io/artifactory/main/release/1.74.0/source/boost_1_74_0.zip" -OutFile boost.zip tar -xf boost.zip cd boost_1_74_0 .\bootstrap.bat From a0795cbc98c3b24633a5711725dcd33422647a51 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Mon, 10 May 2021 11:34:29 +0200 Subject: [PATCH 052/235] Use a local instead of a temporary as an argument to valueOrDefault --- libsolidity/analysis/FunctionCallGraph.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/FunctionCallGraph.cpp b/libsolidity/analysis/FunctionCallGraph.cpp index 0a4a4b437..265237128 100644 --- a/libsolidity/analysis/FunctionCallGraph.cpp +++ b/libsolidity/analysis/FunctionCallGraph.cpp @@ -96,7 +96,8 @@ CallGraph FunctionCallGraphBuilder::buildDeployedGraph( // All functions present in internal dispatch at creation time could potentially be pointers // assigned to state variables and as such may be reachable after deployment as well. builder.m_currentNode = CallGraph::SpecialNode::InternalDispatch; - for (CallGraph::Node const& dispatchTarget: valueOrDefault(_creationGraph.edges, CallGraph::SpecialNode::InternalDispatch, {})) + set defaultNode; + for (CallGraph::Node const& dispatchTarget: valueOrDefault(_creationGraph.edges, CallGraph::SpecialNode::InternalDispatch, defaultNode)) { solAssert(!holds_alternative(dispatchTarget), ""); solAssert(get(dispatchTarget) != nullptr, ""); From c7f321c12d495c6f134d14bd1f63a53c14bccacb Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 10 May 2021 13:25:39 +0200 Subject: [PATCH 053/235] Update evmone and boost download links in docker images. --- scripts/docker/buildpack-deps/Dockerfile.emscripten | 5 +++-- .../buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz | 6 +++--- scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 | 4 ++-- scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/scripts/docker/buildpack-deps/Dockerfile.emscripten b/scripts/docker/buildpack-deps/Dockerfile.emscripten index 34eb0c089..cdd973fbf 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.emscripten +++ b/scripts/docker/buildpack-deps/Dockerfile.emscripten @@ -29,7 +29,7 @@ # make version=2.0.12 build # FROM emscripten/emsdk:2.0.12 AS base -LABEL version="4" +LABEL version="5" ADD emscripten.jam /usr/src RUN set -ex; \ @@ -51,7 +51,8 @@ RUN set -ex; \ make ; make install; \ rm -r /usr/src/z3; \ cd /usr/src; \ - wget -q 'https://dl.bintray.com/boostorg/release/1.75.0/source/boost_1_75_0.tar.bz2' -O boost.tar.bz2; \ + + wget -q 'https://boostorg.jfrog.io/artifactory/main/release/1.75.0/source/boost_1_75_0.tar.bz2' -O boost.tar.bz2; \ test "$(sha256sum boost.tar.bz2)" = "953db31e016db7bb207f11432bef7df100516eeb746843fa0486a222e3fd49cb boost.tar.bz2"; \ tar -xf boost.tar.bz2; \ rm boost.tar.bz2; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz index 627ca998e..35f421ba2 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu1604.clang.ossfuzz @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM gcr.io/oss-fuzz-base/base-clang as base -LABEL version="8" +LABEL version="9" ARG DEBIAN_FRONTEND=noninteractive @@ -45,7 +45,7 @@ FROM base AS libraries # Boost RUN set -ex; \ cd /usr/src; \ - wget -q 'https://dl.bintray.com/boostorg/release/1.73.0/source/boost_1_73_0.tar.bz2' -O boost.tar.bz2; \ + wget -q 'https://boostorg.jfrog.io/artifactory/main/release/1.73.0/source/boost_1_73_0.tar.bz2' -O boost.tar.bz2; \ test "$(sha256sum boost.tar.bz2)" = "4eb3b8d442b426dc35346235c8733b5ae35ba431690e38c6a8263dce9fcbb402 boost.tar.bz2"; \ tar -xf boost.tar.bz2; \ rm boost.tar.bz2; \ @@ -92,7 +92,7 @@ RUN set -ex; \ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.4.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.7.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 index ea0cc8222..310ba7a45 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004 @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="5" +LABEL version="6" ARG DEBIAN_FRONTEND=noninteractive @@ -48,7 +48,7 @@ FROM base AS libraries # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.4.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.7.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ diff --git a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang index 1238d1ada..063ca9c0b 100644 --- a/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang +++ b/scripts/docker/buildpack-deps/Dockerfile.ubuntu2004.clang @@ -22,7 +22,7 @@ # (c) 2016-2019 solidity contributors. #------------------------------------------------------------------------------ FROM buildpack-deps:focal AS base -LABEL version="5" +LABEL version="6" ARG DEBIAN_FRONTEND=noninteractive @@ -50,7 +50,7 @@ ENV CXX clang++ # EVMONE RUN set -ex; \ cd /usr/src; \ - git clone --branch="v0.4.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ + git clone --branch="v0.7.0" --recurse-submodules https://github.com/ethereum/evmone.git; \ cd evmone; \ mkdir build; \ cd build; \ From c70bc0c2468dc9e20f29b143a6b75b841811074f Mon Sep 17 00:00:00 2001 From: William Entriken Date: Mon, 10 May 2021 16:26:08 -0400 Subject: [PATCH 054/235] Specify how blockhash will act for invalid inputs, fixes #11364 --- docs/units-and-global-variables.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 1cad59e42..dc6cefe91 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -68,7 +68,7 @@ or are general-use utility functions. Block and Transaction Properties -------------------------------- -- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks +- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block when ``blocknumber`` is one of the 256 most recent blocks; otherwise returns zero - ``block.chainid`` (``uint``): current chain id - ``block.coinbase`` (``address payable``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty From e2959ce55ce22b32b08bd36015596cd2bda77bd7 Mon Sep 17 00:00:00 2001 From: Leo Alt Date: Tue, 11 May 2021 14:07:02 +0200 Subject: [PATCH 055/235] Assign cast from constants directly --- libsolidity/formal/SMTEncoder.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index bc53c7126..36b07f0a5 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -1113,6 +1113,14 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall) { solAssert(smt::isNumber(*funCallType), ""); + RationalNumberType const* rationalType = isConstant(*argument); + if (rationalType) + { + // The TypeChecker guarantees that a constant fits in the cast size. + defineExpr(_funCall, symbArg); + return; + } + auto const* fixedCast = dynamic_cast(funCallType); auto const* fixedArg = dynamic_cast(argType); if (fixedCast && fixedArg) From 4b2ccf2f3798c8db01e294edfae88c5178289fa0 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 27 Apr 2021 13:30:19 +0200 Subject: [PATCH 056/235] Abstract function smtchecker natspec --- libsolidity/formal/CHC.cpp | 64 +++++++++++++++++++++++++++---- libsolidity/formal/CHC.h | 21 ++++++++++ libsolidity/formal/SMTEncoder.cpp | 4 +- libsolidity/formal/SMTEncoder.h | 4 +- 4 files changed, 82 insertions(+), 11 deletions(-) diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 56066636b..e185849da 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -219,9 +219,20 @@ bool CHC::visit(FunctionDefinition const& _function) if (!m_currentContract) return false; - if (!_function.isImplemented()) + if ( + !_function.isImplemented() || + abstractAsNondet(_function) + ) { - addRule(summary(_function), "summary_function_" + to_string(_function.id())); + smtutil::Expression conj(true); + if ( + _function.stateMutability() == StateMutability::Pure || + _function.stateMutability() == StateMutability::View + ) + conj = conj && currentEqualInitialVarsConstraints(stateVariablesIncludingInheritedAndPrivate(_function)); + + conj = conj && errorFlag().currentValue() == 0; + addRule(smtutil::Expression::implies(conj, summary(_function)), "summary_function_" + to_string(_function.id())); return false; } @@ -262,7 +273,10 @@ void CHC::endVisit(FunctionDefinition const& _function) if (!m_currentContract) return; - if (!_function.isImplemented()) + if ( + !_function.isImplemented() || + abstractAsNondet(_function) + ) return; solAssert(m_currentFunction && m_currentContract, ""); @@ -1001,6 +1015,37 @@ set CHC::transactionVerificationTargetsIds(ASTNode const* _txRoot) return verificationTargetsIds; } +optional CHC::natspecOptionFromString(string const& _option) +{ + static map options{ + {"abstract-function-nondet", CHCNatspecOption::AbstractFunctionNondet} + }; + if (options.count(_option)) + return options.at(_option); + return {}; +} + +set CHC::smtNatspecTags(FunctionDefinition const& _function) +{ + set options; + string smtStr = "custom:smtchecker"; + for (auto const& [tag, value]: _function.annotation().docTags) + if (tag == smtStr) + { + string const& content = value.content; + if (auto option = natspecOptionFromString(content)) + options.insert(*option); + else + m_errorReporter.warning(3130_error, _function.location(), "Unknown option for \"" + smtStr + "\": \"" + content + "\""); + } + return options; +} + +bool CHC::abstractAsNondet(FunctionDefinition const& _function) +{ + return smtNatspecTags(_function).count(CHCNatspecOption::AbstractFunctionNondet); +} + SortPointer CHC::sort(FunctionDefinition const& _function) { return functionBodySort(_function, m_currentContract, state()); @@ -1213,13 +1258,11 @@ smtutil::Expression CHC::initialConstraints(ContractDefinition const& _contract, { smtutil::Expression conj = state().state() == state().state(0); conj = conj && errorFlag().currentValue() == 0; - for (auto var: stateVariablesIncludingInheritedAndPrivate(_contract)) - conj = conj && m_context.variable(*var)->valueAtIndex(0) == currentValue(*var); + conj = conj && currentEqualInitialVarsConstraints(stateVariablesIncludingInheritedAndPrivate(_contract)); FunctionDefinition const* function = _function ? _function : _contract.constructor(); if (function) - for (auto var: function->parameters()) - conj = conj && m_context.variable(*var)->valueAtIndex(0) == currentValue(*var); + conj = conj && currentEqualInitialVarsConstraints(applyMap(function->parameters(), [](auto&& _var) -> VariableDeclaration const* { return _var.get(); })); return conj; } @@ -1254,6 +1297,13 @@ vector CHC::currentStateVariables(ContractDefinition const& return applyMap(SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), [this](auto _var) { return currentValue(*_var); }); } +smtutil::Expression CHC::currentEqualInitialVarsConstraints(vector const& _vars) const +{ + return fold(_vars, smtutil::Expression(true), [this](auto&& _conj, auto _var) { + return move(_conj) && currentValue(*_var) == m_context.variable(*_var)->valueAtIndex(0); + }); +} + string CHC::predicateName(ASTNode const* _node, ContractDefinition const* _contract) { string prefix; diff --git a/libsolidity/formal/CHC.h b/libsolidity/formal/CHC.h index 16513ccaa..7b6a50f14 100644 --- a/libsolidity/formal/CHC.h +++ b/libsolidity/formal/CHC.h @@ -70,6 +70,11 @@ public: /// the constructor. std::vector unhandledQueries() const; + enum class CHCNatspecOption + { + AbstractFunctionNondet + }; + private: /// Visitor functions. //@{ @@ -123,6 +128,19 @@ private: std::set transactionVerificationTargetsIds(ASTNode const* _txRoot); //@} + /// SMT Natspec and abstraction helpers. + //@{ + /// @returns a CHCNatspecOption enum if _option is a valid SMTChecker Natspec value + /// or nullopt otherwise. + static std::optional natspecOptionFromString(std::string const& _option); + /// @returns which SMTChecker options are enabled by @a _function's Natspec via + /// `@custom:smtchecker