From 4c283f00c1f2d3ae91aa216522a834a7e5566400 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 18 Nov 2020 18:24:33 +0100 Subject: [PATCH] Add IRGenerator::verifyCallGraphs() --- libsolidity/codegen/ir/IRGenerator.cpp | 82 ++++++++++++++++++++++++-- libsolidity/codegen/ir/IRGenerator.h | 12 +++- 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index bec31200c..92406aa7b 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -36,18 +36,61 @@ #include #include #include +#include #include +#include + #include #include +#include using namespace std; +using namespace ranges; using namespace solidity; using namespace solidity::util; using namespace solidity::frontend; +namespace +{ + +void verifyCallGraph( + set const& _expectedCallables, + set _generatedFunctions +) +{ + for (auto const& expectedCallable: _expectedCallables) + if (auto const* expectedFunction = dynamic_cast(expectedCallable)) + { + solAssert( + _generatedFunctions.count(expectedFunction) == 1 || expectedFunction->isConstructor(), + "No code generated for function " + expectedFunction->name() + "even though it is not a constructor." + ); + _generatedFunctions.erase(expectedFunction); + } + + solAssert( + _generatedFunctions.size() == 0, + "Of the generated functions " + toString(_generatedFunctions.size()) + " are not in the call graph." + ); +} + +set collectReachableCallables( + FunctionCallGraphBuilder::ContractCallGraph const& _graph +) +{ + set reachableCallables; + for (FunctionCallGraphBuilder::Node const& reachableNode: _graph.edges | views::keys) + if (holds_alternative(reachableNode)) + reachableCallables.emplace(get(reachableNode)); + + return reachableCallables; +} + +} + pair IRGenerator::run( ContractDefinition const& _contract, map const& _otherYulSources @@ -76,11 +119,31 @@ pair IRGenerator::run( return {warning + ir, warning + asmStack.print()}; } +void IRGenerator::verifyCallGraphs( + FunctionCallGraphBuilder::ContractCallGraph const& _creationGraph, + FunctionCallGraphBuilder::ContractCallGraph const& _deployedGraph +) +{ + // m_creationFunctionList and m_deployedFunctionList are not used for any other purpose so + // we can just destroy them without bothering to make a copy. + + verifyCallGraph(collectReachableCallables(_creationGraph), move(m_creationFunctionList)); + m_creationFunctionList = {}; + + verifyCallGraph(collectReachableCallables(_deployedGraph), move(m_deployedFunctionList)); + m_deployedFunctionList = {}; +} + string IRGenerator::generate( ContractDefinition const& _contract, map const& _otherYulSources ) { + // Remember to call verifyCallGraphs() (which clears the list of generated functions) if you + // want to reuse the generator. + solAssert(m_creationFunctionList.empty(), ""); + solAssert(m_deployedFunctionList.empty(), ""); + auto subObjectSources = [&_otherYulSources](std::set const& subObjects) -> string { std::string subObjectsSources; @@ -142,8 +205,9 @@ string IRGenerator::generate( t("deploy", deployCode(_contract)); generateImplicitConstructors(_contract); - generateQueuedFunctions(); + m_creationFunctionList = generateQueuedFunctions(); InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions(); + t("functions", m_context.functionCollector().requestedFunctions()); t("subObjects", subObjectSources(m_context.subObjectsCreated())); @@ -162,7 +226,7 @@ string IRGenerator::generate( t("DeployedObject", IRNames::deployedObject(_contract)); t("library_address", IRNames::libraryAddressImmutable()); t("dispatch", dispatchRoutine(_contract)); - generateQueuedFunctions(); + m_deployedFunctionList = generateQueuedFunctions(); generateInternalDispatchFunctions(); t("deployedFunctions", m_context.functionCollector().requestedFunctions()); t("deployedSubObjects", subObjectSources(m_context.subObjectsCreated())); @@ -170,6 +234,7 @@ string IRGenerator::generate( // This has to be called only after all other code generation for the deployed object is complete. bool deployedInvolvesAssembly = m_context.inlineAssemblySeen(); t("memoryInitDeployed", memoryInit(!deployedInvolvesAssembly)); + return t.render(); } @@ -180,11 +245,20 @@ string IRGenerator::generate(Block const& _block) return generator.code(); } -void IRGenerator::generateQueuedFunctions() +set IRGenerator::generateQueuedFunctions() { + set functions; + while (!m_context.functionGenerationQueueEmpty()) + { + FunctionDefinition const& functionDefinition = *m_context.dequeueFunctionForCodeGeneration(); + + functions.emplace(&functionDefinition); // NOTE: generateFunction() may modify function generation queue - generateFunction(*m_context.dequeueFunctionForCodeGeneration()); + generateFunction(functionDefinition); + } + + return functions; } InternalDispatchMap IRGenerator::generateInternalDispatchFunctions() diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h index 7b7c58bd4..f467b5d3f 100644 --- a/libsolidity/codegen/ir/IRGenerator.h +++ b/libsolidity/codegen/ir/IRGenerator.h @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -56,6 +57,11 @@ public: std::map const& _otherYulSources ); + void verifyCallGraphs( + FunctionCallGraphBuilder::ContractCallGraph const& _creationGraph, + FunctionCallGraphBuilder::ContractCallGraph const& _deployedGraph + ); + private: std::string generate( ContractDefinition const& _contract, @@ -65,7 +71,8 @@ private: /// Generates code for all the functions from the function generation queue. /// The resulting code is stored in the function collector in IRGenerationContext. - void generateQueuedFunctions(); + /// @returns A set of ast nodes of the generated functions. + std::set generateQueuedFunctions(); /// Generates all the internal dispatch functions necessary to handle any function that could /// possibly be called via a pointer. /// @return The content of the dispatch for reuse in runtime code. Reuse is necessary because @@ -115,6 +122,9 @@ private: langutil::EVMVersion const m_evmVersion; OptimiserSettings const m_optimiserSettings; + std::set m_creationFunctionList; + std::set m_deployedFunctionList; + IRGenerationContext m_context; YulUtilFunctions m_utils; };