From 153b0bf4baebd65757781b2a2eb7a5214efb466c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 13 Sep 2021 20:37:16 +0200 Subject: [PATCH] Change semantics tests to use the proper via IR pipeline and make optimizer requirements explicit. --- test/libsolidity/SemanticTest.cpp | 77 ++++++++++++++++++- test/libsolidity/SemanticTest.h | 14 +++- .../SolidityExecutionFramework.cpp | 53 ++----------- 3 files changed, 94 insertions(+), 50 deletions(-) diff --git a/test/libsolidity/SemanticTest.cpp b/test/libsolidity/SemanticTest.cpp index 50d7dfcc0..383298647 100644 --- a/test/libsolidity/SemanticTest.cpp +++ b/test/libsolidity/SemanticTest.cpp @@ -81,6 +81,16 @@ SemanticTest::SemanticTest( m_enforceCompileToEwasm = false; } + string requiresYulOptimizer = m_reader.stringSetting("requiresYulOptimizer", "false"); + if (requiresYulOptimizer == "false") + m_requiresYulOptimizer = RequiresYulOptimizer::False; + else if (requiresYulOptimizer == "minimalStack") + m_requiresYulOptimizer = RequiresYulOptimizer::MinimalStack; + else if (requiresYulOptimizer == "full") + m_requiresYulOptimizer = RequiresYulOptimizer::Full; + else + BOOST_THROW_EXCEPTION(runtime_error("Invalid requiresYulOptimizer value: " + requiresYulOptimizer + ".")); + string compileToEwasm = m_reader.stringSetting("compileToEwasm", "false"); if (compileToEwasm == "also") m_testCaseWantsEwasmRun = true; @@ -307,7 +317,52 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref result = runTest(_stream, _linePrefix, _formatted, false, false); if ((m_testCaseWantsYulRun || m_enforceViaYul) && result == TestResult::Success) - result = runTest(_stream, _linePrefix, _formatted, true, false); + { + if (solidity::test::CommonOptions::get().optimize) + result = runTest(_stream, _linePrefix, _formatted, true, false); + else + for (auto requiresYulOptimizer: {RequiresYulOptimizer::False, RequiresYulOptimizer::MinimalStack, RequiresYulOptimizer::Full}) + { + ScopedSaveAndRestore optimizerSettings(m_optimiserSettings, OptimiserSettings::minimal()); + if (requiresYulOptimizer == RequiresYulOptimizer::MinimalStack) + { + m_optimiserSettings.runYulOptimiser = true; + m_optimiserSettings.yulOptimiserSteps = "uljmul jmul"; + } + else if (requiresYulOptimizer == RequiresYulOptimizer::Full) + m_optimiserSettings = OptimiserSettings::full(); + try + { + result = runTest(_stream, _linePrefix, _formatted, true, false); + if (m_requiresYulOptimizer != requiresYulOptimizer) + { + static constexpr auto settingToString = [](RequiresYulOptimizer _setting) { + switch (_setting) + { + case RequiresYulOptimizer::False: + return "false"; + case RequiresYulOptimizer::MinimalStack: + return "minimalStack"; + case RequiresYulOptimizer::Full: + return "full"; + } + return "unknown"; + }; + m_canUpdateYulOptimizerRequirement = requiresYulOptimizer; + AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl + << _linePrefix << "requiresYulOptimizer is set to " << settingToString(m_requiresYulOptimizer) + << " but should be " << settingToString(requiresYulOptimizer) << endl; + return TestResult::Failure; + } + break; + } + catch (yul::StackTooDeepError const&) + { + if (requiresYulOptimizer == RequiresYulOptimizer::Full) + throw; + } + } + } if ((m_testCaseWantsEwasmRun || m_enforceCompileToEwasm) && result == TestResult::Success) { @@ -330,7 +385,8 @@ TestCase::TestResult SemanticTest::runTest( string const& _linePrefix, bool _formatted, bool _isYulRun, - bool _isEwasmRun) + bool _isEwasmRun +) { bool success = true; m_gasCostFailure = false; @@ -666,11 +722,26 @@ void SemanticTest::printUpdatedSettings(ostream& _stream, string const& _linePre } else if (m_canEnableYulRun) _stream << _linePrefix << "// compileViaYul: also\n"; + if (m_canUpdateYulOptimizerRequirement) + { + switch (*m_canUpdateYulOptimizerRequirement) + { + case RequiresYulOptimizer::False: + break; + case RequiresYulOptimizer::MinimalStack: + _stream << _linePrefix << "// requiresYulOptimizer: minimalStack\n"; + break; + case RequiresYulOptimizer::Full: + _stream << _linePrefix << "// requiresYulOptimizer: full\n"; + break; + } + } for (auto const& [settingName, settingValue]: settings) if ( !(settingName == "compileToEwasm" && m_canEnableEwasmRun) && - !(settingName == "compileViaYul" && (m_canEnableYulRun || m_canEnableEwasmRun)) + !(settingName == "compileViaYul" && (m_canEnableYulRun || m_canEnableEwasmRun)) && + !(settingName == "requiresYulOptimizer" && m_canUpdateYulOptimizerRequirement) ) _stream << _linePrefix << "// " << settingName << ": " << settingValue<< endl; } diff --git a/test/libsolidity/SemanticTest.h b/test/libsolidity/SemanticTest.h index b8093db22..5f05c1941 100644 --- a/test/libsolidity/SemanticTest.h +++ b/test/libsolidity/SemanticTest.h @@ -85,7 +85,17 @@ 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 _isYulRun, bool _isEwasmRun); + enum class RequiresYulOptimizer + { + False, MinimalStack, Full + }; + TestResult runTest( + std::ostream& _stream, + std::string const& _linePrefix, + bool _formatted, + bool _isYulRun, + bool _isEwasmRun + ); bool checkGasCostExpectation(TestFunctionCall& io_test, bool _compileViaYul) const; std::map makeBuiltins(); std::vector makeSideEffectHooks() const; @@ -108,6 +118,8 @@ private: bool m_canEnableEwasmRun = false; bool m_gasCostFailure = false; bool m_enforceGasCost = false; + RequiresYulOptimizer m_requiresYulOptimizer = RequiresYulOptimizer::False; + std::optional m_canUpdateYulOptimizerRequirement; u256 m_enforceGasCostMinValue; }; diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 349baf481..dc2a67ccc 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -49,14 +50,15 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( m_compiler.reset(); m_compiler.enableEwasmGeneration(m_compileToEwasm); - m_compiler.setSources(sourcesWithPreamble); + m_compiler.enableEvmBytecodeGeneration(!m_compileToEwasm); + m_compiler.setViaIR(m_compileViaYul || m_compileToEwasm); + m_compiler.setSources(move(sourcesWithPreamble)); m_compiler.setLibraries(_libraryAddresses); m_compiler.setRevertStringBehaviour(m_revertStrings); m_compiler.setEVMVersion(m_evmVersion); m_compiler.setOptimiserSettings(m_optimiserSettings); - m_compiler.enableEvmBytecodeGeneration(!m_compileViaYul); - m_compiler.enableIRGeneration(m_compileViaYul); m_compiler.setRevertStringBehaviour(m_revertStrings); + if (!m_compiler.compile()) { // The testing framework expects an exception for @@ -69,50 +71,9 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( .printErrorInformation(m_compiler.errors()); BOOST_ERROR("Compiling contract failed"); } + string contractName(_contractName.empty() ? m_compiler.lastContractName(_mainSourceName) : _contractName); - evmasm::LinkerObject obj; - if (m_compileViaYul) - { - if (m_compileToEwasm) - obj = m_compiler.ewasmObject(contractName); - else - { - // Try compiling twice: If the first run fails due to stack errors, forcefully enable - // the optimizer. - for (bool forceEnableOptimizer: {false, true}) - { - OptimiserSettings optimiserSettings = m_optimiserSettings; - if (!forceEnableOptimizer && !optimiserSettings.runYulOptimiser) - { - // Enable some optimizations on the first run - optimiserSettings.runYulOptimiser = true; - optimiserSettings.yulOptimiserSteps = "uljmul jmul"; - } - else if (forceEnableOptimizer) - optimiserSettings = OptimiserSettings::full(); - - yul::AssemblyStack - asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, optimiserSettings); - bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName)); - solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors"); - - try - { - asmStack.optimize(); - obj = move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode); - obj.link(_libraryAddresses); - break; - } - catch (...) - { - if (forceEnableOptimizer || optimiserSettings == OptimiserSettings::full()) - throw; - } - } - } - } - else - obj = m_compiler.object(contractName); + evmasm::LinkerObject obj = m_compileToEwasm ? m_compiler.ewasmObject(contractName) : m_compiler.object(contractName); BOOST_REQUIRE(obj.linkReferences.empty()); if (m_showMetadata) cout << "metadata: " << m_compiler.metadata(contractName) << endl;