Skip to content

Commit

Permalink
CompilerStack: Changes ir and irOptimized to be optional json / strin…
Browse files Browse the repository at this point in the history
…g objects.
  • Loading branch information
clonker committed Oct 14, 2024
1 parent e0cf8db commit b7f7244
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 65 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Compiler Features:


Bugfixes:
* CompilerStack: Fix crash that could occur when using the standard JSON interface for nondeployable contracts with ``outputSelection`` set to ``ir`` or ``irOptimized``.


### 0.8.28 (2024-10-09)
Expand Down
44 changes: 26 additions & 18 deletions libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -969,52 +969,61 @@ std::string const CompilerStack::filesystemFriendlyName(std::string const& _cont
return matchContract.contract->name();
}

std::string const& CompilerStack::yulIR(std::string const& _contractName) const
std::optional<std::string> const& CompilerStack::yulIR(std::string const& _contractName) const
{
solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful.");
return contract(_contractName).yulIR;
}

Json CompilerStack::yulIRAst(std::string const& _contractName) const
std::optional<Json> CompilerStack::yulIRAst(std::string const& _contractName) const
{
solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful.");
solUnimplementedAssert(!isExperimentalSolidity());

// NOTE: Intentionally not using LazyInit. The artifact can get very large and we don't want to
// keep it around when compiling a large project containing many contracts.
auto const& currentContract = contract(_contractName);
Contract const& currentContract = contract(_contractName);
yulAssert(currentContract.contract);
return currentContract.contract->canBeDeployed() ? loadGeneratedIR(currentContract.yulIR).astJson() : Json{};
yulAssert(currentContract.yulIR.has_value() == currentContract.contract->canBeDeployed());
if (!currentContract.yulIR)
return std::nullopt;
return loadGeneratedIR(*currentContract.yulIR).astJson();
}

Json CompilerStack::yulCFGJson(std::string const& _contractName) const
std::optional<Json> CompilerStack::yulCFGJson(std::string const& _contractName) const
{
solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful.");
solUnimplementedAssert(!isExperimentalSolidity());

// NOTE: Intentionally not using LazyInit. The artifact can get very large and we don't want to
// keep it around when compiling a large project containing many contracts.
auto const& currentContract = contract(_contractName);
Contract const& currentContract = contract(_contractName);
yulAssert(currentContract.contract);
return currentContract.contract->canBeDeployed() ? loadGeneratedIR(currentContract.yulIR).cfgJson() : Json{};
yulAssert(currentContract.yulIR.has_value() == currentContract.contract->canBeDeployed());
if (!currentContract.yulIR)
return std::nullopt;
return loadGeneratedIR(*currentContract.yulIR).cfgJson();
}

std::string const& CompilerStack::yulIROptimized(std::string const& _contractName) const
std::optional<std::string> const& CompilerStack::yulIROptimized(std::string const& _contractName) const
{
solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful.");
return contract(_contractName).yulIROptimized;
}

Json CompilerStack::yulIROptimizedAst(std::string const& _contractName) const
std::optional<Json> CompilerStack::yulIROptimizedAst(std::string const& _contractName) const
{
solAssert(m_stackState == CompilationSuccessful, "Compilation was not successful.");
solUnimplementedAssert(!isExperimentalSolidity());

// NOTE: Intentionally not using LazyInit. The artifact can get very large and we don't want to
// keep it around when compiling a large project containing many contracts.
auto const& currentContract = contract(_contractName);
Contract const& currentContract = contract(_contractName);
yulAssert(currentContract.contract);
return currentContract.contract->canBeDeployed() ? loadGeneratedIR(currentContract.yulIROptimized).astJson() : Json{};
yulAssert(currentContract.yulIROptimized.has_value() == currentContract.contract->canBeDeployed());
if (!currentContract.yulIROptimized)
return std::nullopt;
return loadGeneratedIR(*currentContract.yulIROptimized).astJson();
}

evmasm::LinkerObject const& CompilerStack::object(std::string const& _contractName) const
Expand Down Expand Up @@ -1513,7 +1522,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract, bool _unopti
solAssert(m_stackState >= AnalysisSuccessful, "");

Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
if (!compiledContract.yulIR.empty())
if (compiledContract.yulIR && !compiledContract.yulIR->empty())
return;

if (!*_contract.sourceUnit().annotation().useABICoderV2)
Expand All @@ -1533,7 +1542,7 @@ void CompilerStack::generateIR(ContractDefinition const& _contract, bool _unopti

std::map<ContractDefinition const*, std::string_view const> otherYulSources;
for (auto const& pair: m_contracts)
otherYulSources.emplace(pair.second.contract, pair.second.yulIR);
otherYulSources.emplace(pair.second.contract, pair.second.yulIR ? *pair.second.yulIR : std::string_view{});

if (m_experimentalAnalysis)
{
Expand Down Expand Up @@ -1570,7 +1579,8 @@ void CompilerStack::generateIR(ContractDefinition const& _contract, bool _unopti
);
}

YulStack stack = loadGeneratedIR(compiledContract.yulIR);
yulAssert(compiledContract.yulIR);
YulStack stack = loadGeneratedIR(*compiledContract.yulIR);
if (!_unoptimizedOnly)
{
stack.optimize();
Expand All @@ -1586,14 +1596,12 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
return;

Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
solAssert(!compiledContract.yulIROptimized.empty(), "");
solAssert(compiledContract.yulIROptimized && !compiledContract.yulIROptimized->empty());
if (!compiledContract.object.bytecode.empty())
return;

// Re-parse the Yul IR in EVM dialect
YulStack stack = loadGeneratedIR(compiledContract.yulIROptimized);

//cout << yul::AsmPrinter{}(*stack.parserResult()->code) << endl;
YulStack stack = loadGeneratedIR(*compiledContract.yulIROptimized);

std::string deployedName = IRNames::deployedObject(_contract);
solAssert(!deployedName.empty(), "");
Expand Down
14 changes: 7 additions & 7 deletions libsolidity/interface/CompilerStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,18 +321,18 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
virtual std::string const filesystemFriendlyName(std::string const& _contractName) const override;

/// @returns the IR representation of a contract.
std::string const& yulIR(std::string const& _contractName) const;
std::optional<std::string> const& yulIR(std::string const& _contractName) const;

/// @returns the IR representation of a contract AST in format.
Json yulIRAst(std::string const& _contractName) const;
std::optional<Json> yulIRAst(std::string const& _contractName) const;

/// @returns the optimized IR representation of a contract.
std::string const& yulIROptimized(std::string const& _contractName) const;
std::optional<std::string> const& yulIROptimized(std::string const& _contractName) const;

/// @returns the optimized IR representation of a contract AST in JSON format.
Json yulIROptimizedAst(std::string const& _contractName) const;
std::optional<Json> yulIROptimizedAst(std::string const& _contractName) const;

Json yulCFGJson(std::string const& _contractName) const;
std::optional<Json> yulCFGJson(std::string const& _contractName) const;

/// @returns the assembled object for a contract.
virtual evmasm::LinkerObject const& object(std::string const& _contractName) const override;
Expand Down Expand Up @@ -445,8 +445,8 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
std::optional<std::string> runtimeGeneratedYulUtilityCode; ///< Extra Yul utility code that was used when compiling the deployed assembly
evmasm::LinkerObject object; ///< Deployment object (includes the runtime sub-object).
evmasm::LinkerObject runtimeObject; ///< Runtime object.
std::string yulIR; ///< Yul IR code straight from the code generator.
std::string yulIROptimized; ///< Reparsed and possibly optimized Yul IR code.
std::optional<std::string> yulIR; ///< Yul IR code straight from the code generator.
std::optional<std::string> yulIROptimized; ///< Reparsed and possibly optimized Yul IR code.
util::LazyInit<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
util::LazyInit<Json const> abi;
util::LazyInit<Json const> storageLayout;
Expand Down
13 changes: 8 additions & 5 deletions libsolidity/interface/StandardCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1482,15 +1482,18 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu

// IR
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "ir", wildcardMatchesExperimental))
contractData["ir"] = compilerStack.yulIR(contractName);
contractData["ir"] = compilerStack.yulIR(contractName).value_or(std::string{});
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irAst", wildcardMatchesExperimental))
contractData["irAst"] = compilerStack.yulIRAst(contractName);
if (std::optional<Json> const irAst = compilerStack.yulIRAst(contractName))
contractData["irAst"] = *irAst;
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimized", wildcardMatchesExperimental))
contractData["irOptimized"] = compilerStack.yulIROptimized(contractName);
contractData["irOptimized"] = compilerStack.yulIROptimized(contractName).value_or(std::string{});
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "irOptimizedAst", wildcardMatchesExperimental))
contractData["irOptimizedAst"] = compilerStack.yulIROptimizedAst(contractName);
if (std::optional<Json> const irOptimizedAst = compilerStack.yulIROptimizedAst(contractName))
contractData["irOptimizedAst"] = *irOptimizedAst;
if (compilationSuccess && isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "yulCFGJson", wildcardMatchesExperimental))
contractData["yulCFGJson"] = compilerStack.yulCFGJson(contractName);
if (std::optional<Json> const yulCFGJson = compilerStack.yulCFGJson(contractName))
contractData["yulCFGJson"] = *yulCFGJson;

// EVM
Json evmData;
Expand Down
47 changes: 33 additions & 14 deletions solc/CommandLineInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,15 @@ void CommandLineInterface::handleIR(std::string const& _contractName)
if (!m_options.compiler.outputs.ir)
return;

std::optional<std::string> const& ir = m_compiler->yulIR(_contractName);
if (!ir)
return;
if (!m_options.output.dir.empty())
createFile(m_compiler->filesystemFriendlyName(_contractName) + ".yul", m_compiler->yulIR(_contractName));
createFile(m_compiler->filesystemFriendlyName(_contractName) + ".yul", ir ? *ir : std::string{});
else
{
sout() << "IR:" << std::endl;
sout() << m_compiler->yulIR(_contractName) << std::endl;
sout() << "IR:\n";
sout() << *ir << std::endl;
}
}

Expand All @@ -273,19 +276,23 @@ void CommandLineInterface::handleIRAst(std::string const& _contractName)
if (!m_options.compiler.outputs.irAstJson)
return;

std::optional<Json> const yulIRAst = m_compiler->yulIRAst(_contractName);
if (!yulIRAst)
return;

if (!m_options.output.dir.empty())
createFile(
m_compiler->filesystemFriendlyName(_contractName) + "_yul_ast.json",
util::jsonPrint(
m_compiler->yulIRAst(_contractName),
*yulIRAst,
m_options.formatting.json
)
);
else
{
sout() << "IR AST:" << std::endl;
sout() << "IR AST:\n";
sout() << util::jsonPrint(
m_compiler->yulIRAst(_contractName),
*yulIRAst,
m_options.formatting.json
) << std::endl;
}
Expand All @@ -298,18 +305,22 @@ void CommandLineInterface::handleYulCFGExport(std::string const& _contractName)
if (!m_options.compiler.outputs.yulCFGJson)
return;

std::optional<Json> const yulCFGJson = m_compiler->yulCFGJson(_contractName);
if (!yulCFGJson)
return;

if (!m_options.output.dir.empty())
createFile(
m_compiler->filesystemFriendlyName(_contractName) + "_yul_cfg.json",
util::jsonPrint(
m_compiler->yulCFGJson(_contractName),
*yulCFGJson,
m_options.formatting.json
)
);
else
{
sout() << util::jsonPrint(
m_compiler->yulCFGJson(_contractName),
*yulCFGJson,
m_options.formatting.json
) << std::endl;
}
Expand All @@ -322,15 +333,19 @@ void CommandLineInterface::handleIROptimized(std::string const& _contractName)
if (!m_options.compiler.outputs.irOptimized)
return;

std::optional<std::string> const& irOptimized = m_compiler->yulIROptimized(_contractName);
if (!irOptimized)
return;

if (!m_options.output.dir.empty())
createFile(
m_compiler->filesystemFriendlyName(_contractName) + "_opt.yul",
m_compiler->yulIROptimized(_contractName)
irOptimized.value_or(std::string{})
);
else
{
sout() << "Optimized IR:" << std::endl;
sout() << m_compiler->yulIROptimized(_contractName) << std::endl;
sout() << "Optimized IR:\n";
sout() << *irOptimized << std::endl;
}
}

Expand All @@ -341,19 +356,23 @@ void CommandLineInterface::handleIROptimizedAst(std::string const& _contractName
if (!m_options.compiler.outputs.irOptimizedAstJson)
return;

std::optional<Json> const yulIROptimizedAst = m_compiler->yulIROptimizedAst(_contractName);
if (!yulIROptimizedAst)
return;

if (!m_options.output.dir.empty())
createFile(
m_compiler->filesystemFriendlyName(_contractName) + "_opt_yul_ast.json",
util::jsonPrint(
m_compiler->yulIROptimizedAst(_contractName),
*yulIROptimizedAst,
m_options.formatting.json
)
);
else
{
sout() << "Optimized IR AST:" << std::endl;
sout() << "Optimized IR AST:\n";
sout() << util::jsonPrint(
m_compiler->yulIROptimizedAst(_contractName),
*yulIROptimizedAst,
m_options.formatting.json
) << std::endl;
}
Expand Down
8 changes: 0 additions & 8 deletions test/cmdlineTests/ast_ir_undeployable_contract/output
Original file line number Diff line number Diff line change
@@ -1,8 +0,0 @@
IR AST:
null
Optimized IR AST:
null
IR AST:
null
Optimized IR AST:
null
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
{
"contracts": {
"C": {
"C": {
"irAst": null,
"irOptimizedAst": null
},
"I": {
"irAst": null,
"irOptimizedAst": null
}
}
},
"sources": {
"C": {
"id": 0
Expand Down
4 changes: 3 additions & 1 deletion test/libsolidity/MemoryGuardTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,10 @@ TestCase::TestResult MemoryGuardTest::run(std::ostream& _stream, std::string con
for (std::string contractName: compiler().contractNames())
{
ErrorList errors;
std::optional<std::string> const& ir = compiler().yulIR(contractName);
solAssert(ir);
auto [object, analysisInfo] = yul::test::parse(
compiler().yulIR(contractName),
*ir,
EVMDialect::strictAssemblyForEVMObjects(CommonOptions::get().evmVersion(), CommonOptions::get().eofVersion()),
errors
);
Expand Down

0 comments on commit b7f7244

Please sign in to comment.