From 1b5c409a8adbe24cf84efed48ed3e7635aeca502 Mon Sep 17 00:00:00 2001 From: r0qs Date: Fri, 31 May 2024 13:50:04 +0200 Subject: [PATCH] Convert Yul CFG to Json --- libsolidity/interface/CompilerStack.cpp | 11 ++ libsolidity/interface/CompilerStack.h | 3 + libsolidity/interface/StandardCompiler.cpp | 13 +- libyul/CMakeLists.txt | 2 + libyul/YulControlFlowGraphExporter.cpp | 212 +++++++++++++++++++++ libyul/YulControlFlowGraphExporter.h | 42 ++++ libyul/YulStack.cpp | 41 ++++ libyul/YulStack.h | 3 + solc/CommandLineInterface.cpp | 32 +++- solc/CommandLineInterface.h | 1 + solc/CommandLineParser.cpp | 2 + solc/CommandLineParser.h | 2 + test/solc/CommandLineParser.cpp | 3 +- 13 files changed, 362 insertions(+), 5 deletions(-) create mode 100644 libyul/YulControlFlowGraphExporter.cpp create mode 100644 libyul/YulControlFlowGraphExporter.h diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 1f432db40fca..ce9525c2b6e6 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -924,6 +924,16 @@ Json const& CompilerStack::yulIRAst(std::string const& _contractName) const return contract(_contractName).yulIRAst; } +Json const& CompilerStack::yulCFGJson(std::string const& _contractName) const +{ + if (m_stackState != CompilationSuccessful) + solThrow(CompilerError, "Compilation was not successful."); + + solUnimplementedAssert(!isExperimentalSolidity()); + + return contract(_contractName).yulCFGJson; +} + std::string const& CompilerStack::yulIROptimized(std::string const& _contractName) const { solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful."); @@ -1506,6 +1516,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract, bool _unopti ); compiledContract.yulIRAst = stack.astJson(); + compiledContract.yulCFGJson = stack.cfgJson(); if (!_unoptimizedOnly) { stack.optimize(); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 25863b6d0690..0a62cbf7efaf 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -298,6 +298,8 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac /// @returns the optimized IR representation of a contract AST in JSON format. Json const& yulIROptimizedAst(std::string const& _contractName) const; + Json const& yulCFGJson(std::string const& _contractName) const; + /// @returns the assembled object for a contract. virtual evmasm::LinkerObject const& object(std::string const& _contractName) const override; @@ -411,6 +413,7 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac std::string yulIROptimized; ///< Reparsed and possibly optimized Yul IR code. Json yulIRAst; ///< JSON AST of Yul IR code. Json yulIROptimizedAst; ///< JSON AST of optimized Yul IR code. + Json yulCFGJson; ///< JSON CFG of Yul IR code. util::LazyInit metadata; ///< The metadata json that will be hashed into the chain. util::LazyInit abi; util::LazyInit storageLayout; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 0c1ccb6b7653..385e48c2236e 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -180,7 +180,7 @@ bool hashMatchesContent(std::string const& _hash, std::string const& _content) bool isArtifactRequested(Json const& _outputSelection, std::string const& _artifact, bool _wildcardMatchesExperimental) { - static std::set experimental{"ir", "irAst", "irOptimized", "irOptimizedAst"}; + static std::set experimental{"ir", "irAst", "irOptimized", "irOptimizedAst", "yulCFGJson"}; for (auto const& selectedArtifactJson: _outputSelection) { std::string const& selectedArtifact = selectedArtifactJson.get(); @@ -191,6 +191,9 @@ bool isArtifactRequested(Json const& _outputSelection, std::string const& _artif return true; else if (selectedArtifact == "*") { + // TODO: yulCFGJson is only experimental now, so it should not be matched by "*". + if (_artifact == "yulCFGJson") + return false; // "ir", "irOptimized" can only be matched by "*" if activated. if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental) return true; @@ -264,7 +267,7 @@ bool isBinaryRequested(Json const& _outputSelection) // This does not include "evm.methodIdentifiers" on purpose! static std::vector const outputsThatRequireBinaries = std::vector{ "*", - "ir", "irAst", "irOptimized", "irOptimizedAst", + "ir", "irAst", "irOptimized", "irOptimizedAst", "yulCFGJson", "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly" } + evmObjectComponents("bytecode") + evmObjectComponents("deployedBytecode"); @@ -307,7 +310,7 @@ CompilerStack::IROutputSelection irOutputSelection(Json const& _outputSelection) for (auto const& requests: fileRequests) for (auto const& request: requests) { - if (request == "irOptimized" || request == "irOptimizedAst") + if (request == "irOptimized" || request == "irOptimizedAst" || request == "yulCFGJson") return CompilerStack::IROutputSelection::UnoptimizedAndOptimized; if (request == "ir" || request == "irAst") @@ -1485,6 +1488,8 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu contractData["irOptimized"] = compilerStack.yulIROptimized(contractName); if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimizedAst", wildcardMatchesExperimental)) contractData["irOptimizedAst"] = compilerStack.yulIROptimizedAst(contractName); + if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "yulCFGJson", wildcardMatchesExperimental)) + contractData["yulCFGJson"] = compilerStack.yulCFGJson(contractName); // EVM Json evmData; @@ -1698,6 +1703,8 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) output["contracts"][sourceName][contractName]["irOptimized"] = stack.print(); if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "evm.assembly", wildcardMatchesExperimental)) output["contracts"][sourceName][contractName]["evm"]["assembly"] = object.assembly->assemblyString(stack.debugInfoSelection()); + if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "yulCFGJson", wildcardMatchesExperimental)) + output["contracts"][sourceName][contractName]["yulCFGJson"] = stack.cfgJson(); return output; } diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index a35665cc75d5..3e2d5ce46dd4 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -39,6 +39,8 @@ add_library(yul ScopeFiller.h Utilities.cpp Utilities.h + YulControlFlowGraphExporter.h + YulControlFlowGraphExporter.cpp YulName.h YulString.h backends/evm/AbstractAssembly.h diff --git a/libyul/YulControlFlowGraphExporter.cpp b/libyul/YulControlFlowGraphExporter.cpp new file mode 100644 index 000000000000..3f347fcf4f91 --- /dev/null +++ b/libyul/YulControlFlowGraphExporter.cpp @@ -0,0 +1,212 @@ +/* + 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 + +#include +#include + +#include +#include + +#include +#include +#include + +using namespace solidity; +using namespace solidity::langutil; +using namespace solidity::util; +using namespace solidity::yul; + +YulControlFlowGraphExporter::YulControlFlowGraphExporter(ControlFlow const& _controlFlow): m_controlFlow(_controlFlow) +{ +} + +std::string YulControlFlowGraphExporter::varToString(SSACFG const& _cfg, SSACFG::ValueId _var) +{ + if (_var.value == std::numeric_limits::max()) + return std::string("INVALID"); + auto const& info = _cfg.valueInfo(_var); + return std::visit( + util::GenericVisitor{ + [&](SSACFG::UnreachableValue const&) -> std::string { + return "[unreachable]"; + }, + [&](SSACFG::LiteralValue const& _literal) { + return toCompactHexWithPrefix(_literal.value); + }, + [&](auto const&) { + return "v" + std::to_string(_var.value); + } + }, + info + ); +} + +Json YulControlFlowGraphExporter::run() +{ + Json yulObjectJson = Json::object(); + yulObjectJson["blocks"] = exportBlock(*m_controlFlow.mainGraph, SSACFG::BlockId{0}); + + Json functionsJson = Json::object(); + for (auto const& [function, functionGraph]: m_controlFlow.functionGraphMapping) + functionsJson[function->name.str()] = exportFunction(*functionGraph); + yulObjectJson["functions"] = functionsJson; + + return yulObjectJson; +} + +Json YulControlFlowGraphExporter::exportFunction(SSACFG const& _cfg) +{ + Json functionJson = Json::object(); + functionJson["type"] = "Function"; + functionJson["entry"] = "Block" + std::to_string(_cfg.entry.value); + functionJson["arguments"] = Json::array(); + for (auto const& [arg, valueId]: _cfg.arguments) + functionJson["arguments"].emplace_back(arg.get().name.str()); + functionJson["returns"] = Json::array(); + for (auto const& ret: _cfg.returns) + functionJson["returns"].emplace_back(ret.get().name.str()); + functionJson["blocks"] = exportBlock(_cfg, _cfg.entry); + return functionJson; +} + +Json YulControlFlowGraphExporter::exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryId) +{ + Json blocksJson = Json::array(); + util::BreadthFirstSearch bfs{{{_entryId}}}; + bfs.run([&](SSACFG::BlockId _blockId, auto _addChild) { + auto const& block = _cfg.block(_blockId); + // Convert current block to JSON + Json blockJson = toJson(_cfg, _blockId); + + Json exitBlockJson = Json::object(); + std::visit(util::GenericVisitor{ + [&](SSACFG::BasicBlock::MainExit const&) { + exitBlockJson["targets"] = { "Block" + std::to_string(_blockId.value) }; + exitBlockJson["type"] = "MainExit"; + }, + [&](SSACFG::BasicBlock::Jump const& _jump) + { + exitBlockJson["targets"] = { "Block" + std::to_string(_jump.target.value) }; + exitBlockJson["type"] = "Jump"; + _addChild(_jump.target); + }, + [&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) + { + exitBlockJson["targets"] = { "Block" + std::to_string(_conditionalJump.zero.value), "Block" + std::to_string(_conditionalJump.nonZero.value) }; + exitBlockJson["cond"] = varToString(_cfg, _conditionalJump.condition); + exitBlockJson["type"] = "ConditionalJump"; + + _addChild(_conditionalJump.zero); + _addChild(_conditionalJump.nonZero); + }, + [&](SSACFG::BasicBlock::FunctionReturn const& _return) { + exitBlockJson["instructions"] = toJson(_cfg, _return.returnValues); + exitBlockJson["targets"] = { "Block" + std::to_string(_blockId.value) }; + exitBlockJson["type"] = "FunctionReturn"; + }, + [&](SSACFG::BasicBlock::Terminated const&) { + exitBlockJson["targets"] = { "Block" + std::to_string(_blockId.value) }; + exitBlockJson["type"] = "Terminated"; + }, + [&](SSACFG::BasicBlock::JumpTable const&) { + yulAssert(false); + } + }, block.exit); + blockJson["exit"] = exitBlockJson; + blocksJson.emplace_back(blockJson); + }); + + return blocksJson; +} + +Json YulControlFlowGraphExporter::toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId) +{ + Json blockJson = Json::object(); + auto const& block = _cfg.block(_blockId); + + blockJson["id"] = "Block" + std::to_string(_blockId.value); + blockJson["instructions"] = Json::array(); + if (!block.phis.empty()) + { + blockJson["entries"] = block.entries + | ranges::views::transform([](auto const& entry) { return "Block" + std::to_string(entry.value); }) + | ranges::to(); + for (auto const& phi: block.phis) + { + auto* phiInfo = std::get_if(&_cfg.valueInfo(phi)); + yulAssert(phiInfo); + Json phiJson = Json::object(); + phiJson["op"] = "PhiFunction"; + phiJson["in"] = toJson(_cfg, phiInfo->arguments); + phiJson["out"] = toJson(_cfg, std::vector{phi}); + blockJson["instructions"].push_back(phiJson); + } + } + for (auto const& operation: block.operations) + blockJson["instructions"].push_back(toJson(blockJson, _cfg, operation)); + + return blockJson; +} + +Json YulControlFlowGraphExporter::toJson(Json& _ret, SSACFG const& _cfg, SSACFG::Operation const& _operation) +{ + Json opJson = Json::object(); + std::visit(util::GenericVisitor{ + [&](SSACFG::Call const& _call) { + _ret["type"] = "FunctionCall"; + opJson["op"] = _call.function.get().name.str(); + }, + [&](SSACFG::BuiltinCall const& _call) { + _ret["type"] = "BuiltinCall"; + Json builtinArgsJson = Json::array(); + auto const& builtin = _call.builtin.get(); + if (!builtin.literalArguments.empty()) + { + auto const& functionCallArgs = _call.call.get().arguments; + for (size_t i = 0; i < builtin.literalArguments.size(); ++i) + { + std::optional const& argument = builtin.literalArguments[i]; + if (argument.has_value() && i < functionCallArgs.size()) + { + // The function call argument at index i must be a literal if builtin.literalArguments[i] is not nullopt + yulAssert(std::holds_alternative(functionCallArgs[i])); + builtinArgsJson.push_back(formatLiteral(std::get(functionCallArgs[i]))); + } + } + } + + if (!builtinArgsJson.empty()) + opJson["builtinArgs"] = builtinArgsJson; + + opJson["op"] = _call.builtin.get().name.str(); + }, + }, _operation.kind); + + opJson["in"] = toJson(_cfg, _operation.inputs); + opJson["out"] = toJson(_cfg, _operation.outputs); + + return opJson; +} + +Json YulControlFlowGraphExporter::toJson(SSACFG const& _cfg, std::vector const& _values) +{ + Json ret = Json::array(); + for (auto const& value: _values) + ret.push_back(varToString(_cfg, value)); + return ret; +} diff --git a/libyul/YulControlFlowGraphExporter.h b/libyul/YulControlFlowGraphExporter.h new file mode 100644 index 000000000000..37520e194b21 --- /dev/null +++ b/libyul/YulControlFlowGraphExporter.h @@ -0,0 +1,42 @@ +/* + 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 + +#pragma once + +#include +#include +#include + +using namespace solidity; +using namespace yul; + +class YulControlFlowGraphExporter +{ +public: + YulControlFlowGraphExporter(ControlFlow const& _controlFlow); + Json run(); + Json exportBlock(SSACFG const& _cfg, SSACFG::BlockId _blockId); + Json exportFunction(SSACFG const& _cfg); + std::string varToString(SSACFG const& _cfg, SSACFG::ValueId _var); + +private: + ControlFlow const& m_controlFlow; + Json toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId); + Json toJson(Json& _ret, SSACFG const& _cfg, SSACFG::Operation const& _operation); + Json toJson(SSACFG const& _cfg, std::vector const& _values); +}; diff --git a/libyul/YulStack.cpp b/libyul/YulStack.cpp index eac84ae3a13e..eb15f9495cf8 100644 --- a/libyul/YulStack.cpp +++ b/libyul/YulStack.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -366,6 +368,45 @@ Json YulStack::astJson() const return m_parserResult->toJson(); } +Json YulStack::cfgJson() const +{ + yulAssert(m_parserResult, ""); + yulAssert(m_parserResult->hasCode(), ""); + yulAssert(m_parserResult->analysisInfo, ""); + // FIXME: we should not regenerate the cfg, but for now this is sufficient for testing purposes + auto exportCFGFromObject = [&](Object const& _object) -> Json { + // NOTE: The block Ids are reset for each object + std::unique_ptr controlFlow = SSAControlFlowGraphBuilder::build( + *_object.analysisInfo.get(), + languageToDialect(m_language, m_evmVersion), + _object.code()->root() + ); + YulControlFlowGraphExporter exporter(*controlFlow); + return exporter.run(); + }; + + std::function>)> exportCFGFromSubObjects; + exportCFGFromSubObjects = [&](std::vector> _subObjects) -> Json { + Json subObjectsJson = Json::object(); + for (std::shared_ptr const& subObjectNode: _subObjects) + if (Object const* subObject = dynamic_cast(subObjectNode.get())) + { + subObjectsJson[subObject->name] = exportCFGFromObject(*subObject); + subObjectsJson["type"] = "subObject"; + if (!subObject->subObjects.empty()) + subObjectsJson["subObjects"] = exportCFGFromSubObjects(subObject->subObjects); + } + return subObjectsJson; + }; + + Object const& object = *m_parserResult.get(); + Json jsonObject = Json::object(); + jsonObject[object.name] = exportCFGFromObject(object); + jsonObject["type"] = "Object"; + jsonObject["subObjects"] = exportCFGFromSubObjects(object.subObjects); + return jsonObject; +} + std::shared_ptr YulStack::parserResult() const { yulAssert(m_stackState >= AnalysisSuccessful, "Analysis was not successful."); diff --git a/libyul/YulStack.h b/libyul/YulStack.h index 69c0f01656f9..746e0c49fa72 100644 --- a/libyul/YulStack.h +++ b/libyul/YulStack.h @@ -141,6 +141,9 @@ class YulStack: public langutil::CharStreamProvider ) const; Json astJson() const; + // return the JSON representation of the YuL CFG (experimental) + Json cfgJson() const; + /// Return the parsed and analyzed object. std::shared_ptr parserResult() const; diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 02b6dcade295..f78fdfeb81c9 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -291,6 +291,30 @@ void CommandLineInterface::handleIRAst(std::string const& _contractName) } } +void CommandLineInterface::handleYulCFGExport(std::string const& _contractName) +{ + solAssert(CompilerInputModes.count(m_options.input.mode) == 1); + + if (!m_options.compiler.outputs.yulCFGJson) + return; + + if (!m_options.output.dir.empty()) + createFile( + m_compiler->filesystemFriendlyName(_contractName) + "_yul_cfg.json", + util::jsonPrint( + m_compiler->yulCFGJson(_contractName), + m_options.formatting.json + ) + ); + else + { + sout() << util::jsonPrint( + m_compiler->yulCFGJson(_contractName), + m_options.formatting.json + ) << std::endl; + } +} + void CommandLineInterface::handleIROptimized(std::string const& _contractName) { solAssert(CompilerInputModes.count(m_options.input.mode) == 1); @@ -866,7 +890,7 @@ void CommandLineInterface::compile() m_compiler->selectDebugInfo(m_options.output.debugInfoSelection.value()); CompilerStack::IROutputSelection irOutputSelection = CompilerStack::IROutputSelection::None; - if (m_options.compiler.outputs.irOptimized || m_options.compiler.outputs.irOptimizedAstJson) + if (m_options.compiler.outputs.irOptimized || m_options.compiler.outputs.irOptimizedAstJson || m_options.compiler.outputs.yulCFGJson) irOutputSelection = CompilerStack::IROutputSelection::UnoptimizedAndOptimized; else if (m_options.compiler.outputs.ir || m_options.compiler.outputs.irAstJson) irOutputSelection = CompilerStack::IROutputSelection::UnoptimizedOnly; @@ -1280,6 +1304,11 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y sout() << "AST:" << std::endl << std::endl; sout() << util::jsonPrint(stack.astJson(), m_options.formatting.json) << std::endl; } + if (m_options.compiler.outputs.yulCFGJson) + { + sout() << "Yul Control Flow Graph:" << std::endl << std::endl; + sout() << util::jsonPrint(stack.cfgJson(), m_options.formatting.json) << std::endl; + } solAssert(_targetMachine == yul::YulStack::Machine::EVM, ""); if (m_options.compiler.outputs.asm_) { @@ -1335,6 +1364,7 @@ void CommandLineInterface::outputCompilationResults() handleIRAst(contract); handleIROptimized(contract); handleIROptimizedAst(contract); + handleYulCFGExport(contract); handleSignatureHashes(contract); handleMetadata(contract); handleABI(contract); diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index c23fd9e17b1f..ce30a4d71db7 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -108,6 +108,7 @@ class CommandLineInterface void handleIRAst(std::string const& _contract); void handleIROptimized(std::string const& _contract); void handleIROptimizedAst(std::string const& _contract); + void handleYulCFGExport(std::string const& _contract); void handleBytecode(std::string const& _contract); void handleSignatureHashes(std::string const& _contract); void handleMetadata(std::string const& _contract); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 19089d885ae8..dca68bb1c50b 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -473,6 +473,7 @@ void CommandLineParser::parseOutputSelection() CompilerOutputs::componentName(&CompilerOutputs::irOptimized), CompilerOutputs::componentName(&CompilerOutputs::astCompactJson), CompilerOutputs::componentName(&CompilerOutputs::asmJson), + CompilerOutputs::componentName(&CompilerOutputs::yulCFGJson), }; static std::set const evmAssemblyJsonImportModeOutputs = { CompilerOutputs::componentName(&CompilerOutputs::asm_), @@ -755,6 +756,7 @@ General Information)").c_str(), (CompilerOutputs::componentName(&CompilerOutputs::irAstJson).c_str(), "AST of Intermediate Representation (IR) of all contracts in a compact JSON format.") (CompilerOutputs::componentName(&CompilerOutputs::irOptimized).c_str(), "Optimized Intermediate Representation (IR) of all contracts.") (CompilerOutputs::componentName(&CompilerOutputs::irOptimizedAstJson).c_str(), "AST of optimized Intermediate Representation (IR) of all contracts in a compact JSON format.") + (CompilerOutputs::componentName(&CompilerOutputs::yulCFGJson).c_str(), "Control Flow Graph (CFG) of Yul code in JSON format.") (CompilerOutputs::componentName(&CompilerOutputs::signatureHashes).c_str(), "Function signature hashes of the contracts.") (CompilerOutputs::componentName(&CompilerOutputs::natspecUser).c_str(), "Natspec user documentation of all contracts.") (CompilerOutputs::componentName(&CompilerOutputs::natspecDev).c_str(), "Natspec developer documentation of all contracts.") diff --git a/solc/CommandLineParser.h b/solc/CommandLineParser.h index 5d941f662c99..3d187694e2e0 100644 --- a/solc/CommandLineParser.h +++ b/solc/CommandLineParser.h @@ -87,6 +87,7 @@ struct CompilerOutputs {"metadata", &CompilerOutputs::metadata}, {"storage-layout", &CompilerOutputs::storageLayout}, {"transient-storage-layout", &CompilerOutputs::transientStorageLayout}, + {"yul-cfg-json", &CompilerOutputs::yulCFGJson}, }; return components; } @@ -100,6 +101,7 @@ struct CompilerOutputs bool abi = false; bool ir = false; bool irAstJson = false; + bool yulCFGJson = false; bool irOptimized = false; bool irOptimizedAstJson = false; bool signatureHashes = false; diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index e0347a18c4b5..227b1b9213d8 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -131,6 +131,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) "dir2/file2.sol:L=0x1111122222333334444455555666667777788888", "--ast-compact-json", "--asm", "--asm-json", "--opcodes", "--bin", "--bin-runtime", "--abi", "--ir", "--ir-ast-json", "--ir-optimized", "--ir-optimized-ast-json", "--hashes", "--userdoc", "--devdoc", "--metadata", + "--yul-cfg-json", "--storage-layout", "--transient-storage-layout", "--gas", "--combined-json=" @@ -193,7 +194,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options) true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, - true, true, + true, true, true, }; expectedOptions.compiler.estimateGas = true; expectedOptions.compiler.combinedJsonRequests = {