| 130 | } |
| 131 | |
| 132 | frontend::test::TestCase::TestResult StackLayoutGeneratorTest::run(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted) |
| 133 | { |
| 134 | static std::string_view constexpr SUBOBJECT_GRAPH_SEPARATOR = "\n>>>>> GRAPH SEPARATOR\n"; |
| 135 | YulStack const yulStack = yul::test::parseYul(m_source); |
| 136 | if (yulStack.hasErrors()) |
| 137 | { |
| 138 | yul::test::printYulErrors(yulStack, _stream, _linePrefix, _formatted); |
| 139 | return TestResult::FatalError; |
| 140 | } |
| 141 | |
| 142 | std::set<Object const*> visited; |
| 143 | visited.insert(yulStack.parserResult().get()); |
| 144 | |
| 145 | std::vector<Object const*> toVisit{yulStack.parserResult().get()}; |
| 146 | while (!toVisit.empty()) |
| 147 | { |
| 148 | auto const& object = *toVisit.back(); |
| 149 | toVisit.pop_back(); |
| 150 | |
| 151 | auto const* evmDialect = dynamic_cast<EVMDialect const*>(object.dialect()); |
| 152 | yulAssert(evmDialect); |
| 153 | |
| 154 | std::unique_ptr<ControlFlowGraphs> const controlFlowGraphs = SSACFGBuilder::build( |
| 155 | *object.analysisInfo, |
| 156 | *evmDialect, |
| 157 | object.code()->root(), |
| 158 | false |
| 159 | ); |
| 160 | // insert separator |
| 161 | if (!m_obtainedResult.empty()) |
| 162 | m_obtainedResult += SUBOBJECT_GRAPH_SEPARATOR; |
| 163 | |
| 164 | m_obtainedResult += "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\", rankdir=LR]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n"; |
| 165 | |
| 166 | for (std::size_t index = 0; index < controlFlowGraphs->functionGraphs.size(); ++index) |
| 167 | { |
| 168 | auto const& cfg = *controlFlowGraphs->functionGraphs[index]; |
| 169 | SSACFGStackLayout const layout = StackLayoutGenerator::generate( |
| 170 | LivenessAnalysis(cfg), |
| 171 | gatherCallSites(cfg), |
| 172 | static_cast<ControlFlowGraphs::FunctionGraphID>(index) |
| 173 | ); |
| 174 | StackLayoutDotExporter exporter(cfg, index, layout, *controlFlowGraphs); |
| 175 | if (!cfg.isMainGraph()) |
| 176 | m_obtainedResult += exporter.exportFunction(cfg, false); |
| 177 | else |
| 178 | m_obtainedResult += exporter.exportBlocks(cfg.entry, false); |
| 179 | } |
| 180 | |
| 181 | m_obtainedResult += "}\n"; |
| 182 | |
| 183 | for (auto const& subNode: object.subObjects) |
| 184 | if (auto subObject = std::dynamic_pointer_cast<Object>(subNode)) |
| 185 | if (!visited.contains(subObject.get())) |
| 186 | { |
| 187 | visited.insert(subObject.get()); |
| 188 | toVisit.push_back(subObject.get()); |
| 189 | } |
nothing calls this directly
no test coverage detected