diff --git a/Changelog.md b/Changelog.md index aa3e2951f0d5..bac62ba185a2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Language Features: Compiler Features: * Commandline Interface: Introduce `--experimental` flag required for enabling the experimental mode. +* General: Introduce the SSA CFG codegen (experimental). * General: Restrict the existing experimental features (`generic-solidity`, `lsp`, `ethdebug`, `eof`, `evm`, `ast-import`, `evmasm-import`, `ir-ast`, `ssa-cfg`) to experimental mode. * Metadata: Store the state of the experimental mode in JSON and CBOR metadata. In CBOR this broadens the meaning of the existing `experimental` field, which used to indicate only the presence of certain experimental pragmas in the source. * Standard JSON Interface: Introduce `settings.experimental` setting required for enabling the experimental mode. diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index ce82aa077817..604f8b05ad40 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -369,6 +369,9 @@ Input Description // Optional: Change compilation pipeline to go through the Yul intermediate representation. // This is false by default. "viaIR": true, + // Optional: Turn on SSA CFG-based code generation via the IR (experimental). + // Implies viaIR: true. This is false by default. + "viaSSACFG": false, // Optional: Debugging settings "debug": { // How to treat revert (and require) reason strings. Settings are @@ -766,5 +769,7 @@ The table below details all currently available experimental features. +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ | Ethdebug | ``ethdebug`` | no | ``--ethdebug``, ``--ethdebug-runtime``, ``--debug-info ethdebug`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ -| Yul SSA CFG exporter | ``ssa-cfg`` | no | ``--yul-cfg-json`` | +| | | no | ``--yul-cfg-json`` | +| SSA CFG + ``ssa-cfg`` +------------------+-------------------------------------------------------------------+ +| | | yes | ``--via-ssa-cfg`` | +-----------------------+--------------------------+------------------+-------------------------------------------------------------------+ diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 96c0d3f6c2da..152a0059adf6 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -1842,6 +1842,8 @@ std::string CompilerStack::createMetadata(Contract const& _contract, bool _forIR meta["settings"]["experimental"] = m_experimental; if (m_eofVersion.has_value()) meta["settings"]["eofVersion"] = *m_eofVersion; + if (m_viaSSACFG) + meta["settings"]["viaSSACFG"] = m_viaSSACFG; meta["settings"]["compilationTarget"][_contract.contract->sourceUnitName()] = *_contract.contract->annotation().canonicalName; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 74307bbc059c..a556c74db1b1 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -450,7 +450,7 @@ std::optional checkAuxiliaryInputKeys(Json const& _input) std::optional checkSettingsKeys(Json const& _input) { - static std::set keys{"debug", "evmVersion", "experimental", "eofVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR"}; + static std::set keys{"debug", "evmVersion", "experimental", "eofVersion", "libraries", "metadata", "modelChecker", "optimizer", "outputSelection", "remappings", "stopAfter", "viaIR", "viaSSACFG"}; return checkKeys(_input, keys, "settings"); } @@ -848,6 +848,22 @@ std::variant StandardCompiler::parseI ret.viaIR = settings["viaIR"].get(); } + if (settings.contains("viaSSACFG")) + { + if (!settings["viaSSACFG"].is_boolean()) + return formatFatalError(Error::Type::JSONError, "\"settings.viaSSACFG\" must be a Boolean."); + ret.viaSSACFG = settings["viaSSACFG"].get(); + if (ret.viaSSACFG) + { + if (settings.contains("viaIR") && !ret.viaIR) + return formatFatalError( + Error::Type::JSONError, + "\"settings.viaSSACFG\" requires compilation via IR." + ); + ret.viaIR = true; + } + } + if (settings.contains("evmVersion")) { if (!settings["evmVersion"].is_string()) @@ -1239,8 +1255,12 @@ std::variant StandardCompiler::parseI } if (isEthdebugRequested(ret.outputSelection)) + { if (ret.optimiserSettings.runYulOptimiser) solUnimplemented("Optimization is not yet supported with ethdebug."); + if (ret.viaSSACFG) + solUnimplemented("SSA CFG codegen does not yet support ethdebug."); + } if (!ret.experimental) { @@ -1256,6 +1276,9 @@ std::variant StandardCompiler::parseI if (ret.eofVersion.has_value()) return formatFatalError(Error::Type::FatalError, "'eofVersion' setting is experimental and can only be used with the 'settings.experimental' option enabled."); + + if (ret.viaSSACFG) + return formatFatalError(Error::Type::FatalError, "'viaSSACFG' setting is experimental and can only be used with the 'settings.experimental' option enabled."); } return {std::move(ret)}; @@ -1406,6 +1429,7 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu for (auto const& smtLib2Response: _inputsAndSettings.smtLib2Responses) compilerStack.addSMTLib2Response(smtLib2Response.first, smtLib2Response.second); compilerStack.setViaIR(_inputsAndSettings.viaIR); + compilerStack.setViaSSACFG(_inputsAndSettings.viaSSACFG); compilerStack.setEVMVersion(_inputsAndSettings.evmVersion); compilerStack.setEOFVersion(_inputsAndSettings.eofVersion); compilerStack.setRemappings(std::move(_inputsAndSettings.remappings)); @@ -1763,7 +1787,7 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) output["sources"][sourceName] = sourceResult; } stack.optimize(); - std::tie(object, deployedObject) = stack.assembleWithDeployed(); + std::tie(object, deployedObject) = stack.assembleWithDeployed({}, _inputsAndSettings.viaSSACFG); if (object.bytecode) object.bytecode->link(_inputsAndSettings.libraries); if (deployedObject.bytecode) diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 493e637cd64b..0d184e139ec6 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -89,6 +89,7 @@ class StandardCompiler Json outputSelection; ModelCheckerSettings modelCheckerSettings = ModelCheckerSettings{}; bool viaIR = false; + bool viaSSACFG = false; bool experimental = false; }; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 90c67868d55f..a40853e49dfd 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -937,6 +937,7 @@ void CommandLineInterface::compile() m_compiler->setRemappings(m_options.input.remappings); m_compiler->setLibraries(m_options.linker.libraries); m_compiler->setViaIR(m_options.output.viaIR); + m_compiler->setViaSSACFG(m_options.output.viaSSACFG); m_compiler->setEVMVersion(m_options.output.evmVersion); m_compiler->setEOFVersion(m_options.output.eofVersion); m_compiler->setRevertStringBehaviour(m_options.output.revertStrings); @@ -1322,7 +1323,7 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y stack.optimize(); - yul::MachineAssemblyObject object = stack.assemble(_targetMachine); + yul::MachineAssemblyObject object = stack.assemble(_targetMachine, m_options.output.viaSSACFG); if (object.bytecode) object.bytecode->link(m_options.linker.libraries); objects.insert({sourceUnitName, std::move(object)}); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 7f143695d53d..c40840a07567 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -49,6 +49,7 @@ static std::string const g_strEVM = "evm"; static std::string const g_strEVMVersion = "evm-version"; static std::string const g_strEOFVersion = "experimental-eof-version"; static std::string const g_strViaIR = "via-ir"; +static std::string const g_strViaSSACFG = "via-ssa-cfg"; static std::string const g_strExperimentalViaIR = "experimental-via-ir"; static std::string const g_strExperimental = "experimental"; static std::string const g_strGas = "gas"; @@ -623,7 +624,11 @@ General Information)").c_str(), ) ( g_strViaIR.c_str(), - "Turn on compilation mode via the IR." + "Turn on compilation via IR." + ) + ( + g_strViaSSACFG.c_str(), + "(experimental) Turn on SSA CFG-based code generation. Implies compilation via IR." ) ( g_strRevertStrings.c_str(), @@ -999,6 +1004,7 @@ void CommandLineParser::processArgs() "ethdebug", "ethdebug-runtime", g_strEOFVersion, + g_strViaSSACFG, }); if (m_args.count(g_strHelp) > 0) @@ -1063,7 +1069,8 @@ void CommandLineParser::processArgs() {g_strModelCheckerTimeout, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, {g_strModelCheckerBMCLoopIterations, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, {g_strModelCheckerContracts, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, - {g_strModelCheckerTargets, {InputMode::Compiler, InputMode::CompilerWithASTImport}} + {g_strModelCheckerTargets, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, + {g_strViaSSACFG, {InputMode::Compiler, InputMode::CompilerWithASTImport, InputMode::Assembler}} }; std::vector invalidOptionsForCurrentInputMode; for (auto const& [optionName, inputModes]: validOptionInputModeCombinations) @@ -1372,8 +1379,17 @@ void CommandLineParser::processArgs() "Optimizer can only be used for strict assembly. Use --" + g_strStrictAssembly + "." ); + m_options.output.viaSSACFG = m_args.contains(g_strViaSSACFG); + if (m_options.output.viaSSACFG) + if (m_options.assembly.inputLanguage != Input::StrictAssembly) + solThrow( + CommandLineValidationError, + "--" + g_strViaSSACFG + " can only be used with strict assembly. Use --" + g_strStrictAssembly + "." + ); if (m_options.compiler.outputs.ethdebug || m_options.compiler.outputs.ethdebugRuntime) { + if (m_options.output.viaSSACFG) + solUnimplemented("ethdebug is not yet supported with --" + g_strViaSSACFG + "."); if (m_options.optimiserSettings().runYulOptimiser) solUnimplemented( "Optimization (using --" + g_strOptimize + ") is not yet supported with ethdebug." @@ -1514,6 +1530,9 @@ void CommandLineParser::processArgs() m_args.count(g_strModelCheckerTargets) || m_args.count(g_strModelCheckerTimeout); m_options.output.viaIR = (m_args.count(g_strExperimentalViaIR) > 0 || m_args.count(g_strViaIR) > 0); + m_options.output.viaSSACFG = m_args.contains(g_strViaSSACFG); + if (m_options.output.viaSSACFG) + m_options.output.viaIR = true; solAssert( m_options.input.mode == InputMode::Compiler || @@ -1540,6 +1559,9 @@ void CommandLineParser::processArgs() enableEthdebugMessage + " output can only be selected, if --via-ir was specified." ); + if (m_options.output.viaSSACFG) + solUnimplemented("ethdebug is not yet supported with --" + g_strViaSSACFG + "."); + if (incompatibleEthdebugOutputs) solThrow( CommandLineValidationError, diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 9d9dc28b6ada..5f5527c619f2 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -200,6 +200,7 @@ struct CommandLineOptions bool overwriteFiles = false; langutil::EVMVersion evmVersion; bool viaIR = false; + bool viaSSACFG = false; RevertStrings revertStrings = RevertStrings::Default; std::optional debugInfoSelection; CompilerStack::State stopAfter = CompilerStack::State::CompilationSuccessful; diff --git a/test/cmdlineTests/metadata_via_ssa_cfg/args b/test/cmdlineTests/metadata_via_ssa_cfg/args new file mode 100644 index 000000000000..565e51922510 --- /dev/null +++ b/test/cmdlineTests/metadata_via_ssa_cfg/args @@ -0,0 +1 @@ +--metadata --experimental --via-ssa-cfg diff --git a/test/cmdlineTests/metadata_via_ssa_cfg/input.sol b/test/cmdlineTests/metadata_via_ssa_cfg/input.sol new file mode 100644 index 000000000000..a3a86cc8d317 --- /dev/null +++ b/test/cmdlineTests/metadata_via_ssa_cfg/input.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity *; + +contract C {} diff --git a/test/cmdlineTests/metadata_via_ssa_cfg/output b/test/cmdlineTests/metadata_via_ssa_cfg/output new file mode 100644 index 000000000000..5752237261f6 --- /dev/null +++ b/test/cmdlineTests/metadata_via_ssa_cfg/output @@ -0,0 +1,4 @@ + +======= input.sol:C ======= +Metadata: +{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"input.sol":"C"},"evmVersion":"osaka","experimental":true,"libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[],"viaIR":true,"viaSSACFG":true},"sources":{"input.sol":{"keccak256":"0x5cf617b1707a484e3c4bd59643013dec76ab7d75900b46855214729ae3e0ceb0","license":"GPL-3.0","urls":["bzz-raw://ac418a02dfadf87234150d3568f33269e3f49460345cb39300e017a6d755eff2","dweb:/ipfs/QmQq3owBu25x2WV46HB1WyKzJpxiAPecU7eMKqtXCF7eeS"]}},"version":1} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/input.json b/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/input.json new file mode 100644 index 000000000000..d5ad9153e9c9 --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/input.json @@ -0,0 +1,15 @@ +{ + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A {}" + } + }, + "settings": { + "experimental": true, + "viaSSACFG": true, + "outputSelection": { + "*": {"*": ["evm.bytecode.ethdebug"]} + } + } +} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/output.json b/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/output.json new file mode 100644 index 000000000000..7e4cdbc8dc93 --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_ethdebug/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "SSA CFG codegen does not yet support ethdebug.", + "message": "SSA CFG codegen does not yet support ethdebug.", + "severity": "error", + "type": "UnimplementedFeatureError" + } + ] +} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/input.json b/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/input.json new file mode 100644 index 000000000000..4515a2489cb6 --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/input.json @@ -0,0 +1,11 @@ +{ + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A { constructor() { uint x = 2; { uint y = 3; } } }" + } + }, + "settings": { + "viaSSACFG": true + } +} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/output.json b/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/output.json new file mode 100644 index 000000000000..16de05ce376c --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_no_experimental/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "'viaSSACFG' setting is experimental and can only be used with the 'settings.experimental' option enabled.", + "message": "'viaSSACFG' setting is experimental and can only be used with the 'settings.experimental' option enabled.", + "severity": "error", + "type": "FatalError" + } + ] +} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/input.json b/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/input.json new file mode 100644 index 000000000000..242883416455 --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/input.json @@ -0,0 +1,13 @@ +{ + "language": "Solidity", + "sources": { + "A.sol": { + "content": "contract A {}" + } + }, + "settings": { + "experimental": true, + "viaIR": false, + "viaSSACFG": true + } +} diff --git a/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/output.json b/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/output.json new file mode 100644 index 000000000000..938367890f08 --- /dev/null +++ b/test/cmdlineTests/standard_via_ssa_cfg_via_ir_false/output.json @@ -0,0 +1,11 @@ +{ + "errors": [ + { + "component": "general", + "formattedMessage": "\"settings.viaSSACFG\" requires compilation via IR.", + "message": "\"settings.viaSSACFG\" requires compilation via IR.", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/via_ssa_cfg_ethdebug/args b/test/cmdlineTests/via_ssa_cfg_ethdebug/args new file mode 100644 index 000000000000..fa8be093c03d --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_ethdebug/args @@ -0,0 +1 @@ +--experimental --via-ssa-cfg --ethdebug diff --git a/test/cmdlineTests/via_ssa_cfg_ethdebug/err b/test/cmdlineTests/via_ssa_cfg_ethdebug/err new file mode 100644 index 000000000000..170609e36f60 --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_ethdebug/err @@ -0,0 +1 @@ +Error: ethdebug is not yet supported with --via-ssa-cfg. diff --git a/test/cmdlineTests/via_ssa_cfg_ethdebug/exit b/test/cmdlineTests/via_ssa_cfg_ethdebug/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_ethdebug/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/via_ssa_cfg_ethdebug/input.sol b/test/cmdlineTests/via_ssa_cfg_ethdebug/input.sol new file mode 100644 index 000000000000..f123f6c8bee3 --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_ethdebug/input.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C {} diff --git a/test/cmdlineTests/via_ssa_cfg_without_experimental/args b/test/cmdlineTests/via_ssa_cfg_without_experimental/args new file mode 100644 index 000000000000..fe15a47cb3ce --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_without_experimental/args @@ -0,0 +1 @@ +--via-ssa-cfg diff --git a/test/cmdlineTests/via_ssa_cfg_without_experimental/err b/test/cmdlineTests/via_ssa_cfg_without_experimental/err new file mode 100644 index 000000000000..0d92cf2651c3 --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_without_experimental/err @@ -0,0 +1 @@ +Error: The following options are only available in experimental mode: --via-ssa-cfg. To enable experimental mode, use the --experimental flag. diff --git a/test/cmdlineTests/via_ssa_cfg_without_experimental/exit b/test/cmdlineTests/via_ssa_cfg_without_experimental/exit new file mode 100644 index 000000000000..d00491fd7e5b --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_without_experimental/exit @@ -0,0 +1 @@ +1 diff --git a/test/cmdlineTests/via_ssa_cfg_without_experimental/input.sol b/test/cmdlineTests/via_ssa_cfg_without_experimental/input.sol new file mode 100644 index 000000000000..136e28fdf74b --- /dev/null +++ b/test/cmdlineTests/via_ssa_cfg_without_experimental/input.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + function f() public {} +} diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 683522693c3c..476fcd3a00ab 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -423,6 +423,51 @@ BOOST_AUTO_TEST_CASE(metadata_viair) check(sourceCode, false); } +BOOST_AUTO_TEST_CASE(metadata_viassacfg) +{ + static auto constexpr sourceCode = R"( + pragma solidity >=0.0; + contract test { + } + )"; + + auto check = [](char const* _src, bool _viaSSACFG) + { + CompilerStack compilerStack; + compilerStack.setSources({{"", _src}}); + compilerStack.setEVMVersion(solidity::test::CommonOptions::get().evmVersion()); + compilerStack.setOptimiserSettings(solidity::test::CommonOptions::get().optimize); + compilerStack.setViaIR(true); + compilerStack.setExperimental(true); + compilerStack.setViaSSACFG(_viaSSACFG); + BOOST_REQUIRE_MESSAGE(compilerStack.compile(), "Compiling contract failed"); + + Json metadata; + BOOST_REQUIRE(util::jsonParseStrict(compilerStack.metadata("test"), metadata)); + BOOST_CHECK(solidity::test::isValidMetadata(metadata)); + BOOST_CHECK(metadata.contains("settings")); + BOOST_CHECK(metadata["settings"].contains("viaIR")); + BOOST_CHECK(metadata["settings"]["viaIR"].get()); + if (_viaSSACFG) + { + BOOST_CHECK(metadata["settings"].contains("viaSSACFG")); + BOOST_CHECK(metadata["settings"]["viaSSACFG"].get()); + } + else + BOOST_CHECK(!metadata["settings"].contains("viaSSACFG")); + + std::map const parsedCBORMetadata = requireParsedCBORMetadata( + compilerStack.runtimeObject("test").bytecode, + CompilerStack::MetadataFormat::WithReleaseVersionTag + ); + + BOOST_CHECK(parsedCBORMetadata.contains("experimental")); + }; + + check(sourceCode, true); + check(sourceCode, false); +} + BOOST_AUTO_TEST_CASE(metadata_revert_strings) { CompilerStack compilerStack; diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 43b68223bea7..18c366e3e8e5 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -2347,6 +2347,32 @@ BOOST_AUTO_TEST_CASE(experimental_non_boolean) BOOST_CHECK(containsError(result, "JSONError", "'settings.experimental' must be a Boolean.")); } +BOOST_AUTO_TEST_CASE(via_ssa_cfg_with_experimental) +{ + frontend::StandardCompiler compiler; + static auto constexpr input = R"( + { + "language": "Solidity", + "sources": { + "A.sol": { + "content": "// SPDX-License-Identifier: GPL-2.0\npragma solidity >=0.0;\ncontract A { function f() public pure returns (uint) { return 1; } }" + } + }, + "settings": { + "experimental": true, + "viaSSACFG": true, + "outputSelection": {"*": {"*": ["evm.bytecode"]}} + } + } + )"; + + Json parsedInput; + BOOST_REQUIRE(util::jsonParseStrict(input, parsedInput)); + Json result = compiler.compile(parsedInput); + // Should compile without fatal errors (warnings are acceptable) + BOOST_CHECK(!containsError(result, "FatalError", "")); +} + BOOST_AUTO_TEST_SUITE_END() } // end namespaces diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index 6d9153381633..1e5896604a41 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -155,7 +155,8 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) "--model-checker-show-unsupported", "--model-checker-solvers=z3,smtlib2", "--model-checker-targets=underflow,divByZero", - "--model-checker-timeout=5" + "--model-checker-timeout=5", + "--via-ssa-cfg" }; if (inputMode == InputMode::CompilerWithASTImport) @@ -183,6 +184,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) expectedOptions.output.overwriteFiles = true; expectedOptions.output.evmVersion = EVMVersion::spuriousDragon(); expectedOptions.output.viaIR = true; + expectedOptions.output.viaSSACFG = true; expectedOptions.output.revertStrings = RevertStrings::Strip; expectedOptions.output.debugInfoSelection = DebugInfoSelection::fromString("location"); expectedOptions.formatting.json = JsonFormat{JsonFormat::Pretty, 7}; @@ -318,9 +320,11 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) commandLine += assemblyOptions; if (expectedLanguage == YulStack::Language::StrictAssembly) commandLine += std::vector{ + "--experimental", "--optimize", "--optimize-runs=1000", "--yul-optimizations=agf", + "--via-ssa-cfg", }; CommandLineOptions expectedOptions; @@ -357,10 +361,12 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options) expectedOptions.compiler.outputs.astCompactJson = true; if (expectedLanguage == YulStack::Language::StrictAssembly) { + expectedOptions.experimental = true; expectedOptions.optimizer.optimizeEvmasm = true; expectedOptions.optimizer.optimizeYul = true; expectedOptions.optimizer.yulSteps = "agf"; expectedOptions.optimizer.expectedExecutionsPerDeployment = 1000; + expectedOptions.output.viaSSACFG = true; } CommandLineOptions parsedOptions = parseCommandLine(commandLine); @@ -436,7 +442,8 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) {"--model-checker-solvers=z3,smtlib2", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, {"--model-checker-timeout=5", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, {"--model-checker-contracts=contract1.yul:A,contract2.yul:B", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, - {"--model-checker-targets=underflow,divByZero", {"--assemble", "--strict-assembly", "--standard-json", "--link"}} + {"--model-checker-targets=underflow,divByZero", {"--assemble", "--strict-assembly", "--standard-json", "--link"}}, + {"--via-ssa-cfg", {"--standard-json", "--link", "--import-asm-json"}} }; for (auto const& [optionName, inputModes]: invalidOptionInputModeCombinations) @@ -448,9 +455,24 @@ BOOST_AUTO_TEST_CASE(invalid_options_input_modes_combinations) soltestAssert(!optionNameWithoutValue.empty()); std::vector commandLine = {"solc", optionName, "file", inputMode}; + bool experimentalMode = false; + if (optionNameWithoutValue == "--via-ssa-cfg") + { + commandLine.push_back("--experimental"); + experimentalMode = true; + } std::string expectedMessage = "The following options are not supported in the current input mode: " + optionNameWithoutValue; - auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedMessage; }; + // When --experimental is combined with --standard-json, a different error fires first + // (standard JSON mode is incompatible with --experimental flag). Accept that too. + auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) + { + std::string const what = _exception.what(); + return what == expectedMessage || ( + experimentalMode && + what.starts_with("Standard JSON input mode is incompatible with the --experimental flag.") + ); + }; BOOST_CHECK_EXCEPTION(parseCommandLine(commandLine), CommandLineValidationError, hasCorrectMessage); } @@ -679,7 +701,8 @@ BOOST_AUTO_TEST_CASE(experimental_features_without_experimental_flag) "--ir-optimized-ast-json", "--yul-cfg-json", "--ethdebug", - "--ethdebug-runtime" + "--ethdebug-runtime", + "--via-ssa-cfg" }; std::string expectedErrorMessage; @@ -703,6 +726,13 @@ BOOST_AUTO_TEST_CASE(experimental_features_without_experimental_flag) BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); } +BOOST_AUTO_TEST_CASE(via_ssa_cfg_smoke) +{ + auto const commandLineOptions = parseCommandLine({"solc", "--experimental", "--via-ssa-cfg", "contract.sol"}); + BOOST_CHECK_EQUAL(commandLineOptions.output.viaSSACFG, true); + BOOST_CHECK_EQUAL(commandLineOptions.output.viaIR, true); +} + BOOST_AUTO_TEST_CASE(debug_info_ethdebug_without_experimental_flag) { std::string const expectedErrorMessage =