1717// SPDX-License-Identifier: GPL-3.0
1818
1919#include < libyul/backends/evm/ssa/SSACFGJsonExporter.h>
20+
2021#include < libyul/Utilities.h>
2122
2223#include < libsolutil/Algorithms.h>
2324#include < libsolutil/Numeric.h>
25+ #include < libsolutil/Visitor.h>
2426
27+ #include < range/v3/view/drop.hpp>
2528#include < range/v3/view/enumerate.hpp>
26- #include < range/v3/view/map.hpp>
2729#include < range/v3/view/transform.hpp>
2830
2931using namespace solidity ;
32+ using namespace solidity ::yul;
3033using namespace solidity ::yul::ssa;
34+ using namespace solidity ::yul::ssa::json;
3135
32- SSACFGJsonExporter::SSACFGJsonExporter (ControlFlow const & _controlFlow, ControlFlowLiveness const * _liveness): m_controlFlow(_controlFlow), m_liveness(_liveness)
36+ namespace
37+ {
38+ Json toJson (SSACFG const & _cfg, std::vector<SSACFG::ValueId> const & _values)
3339{
40+ Json ret = Json::array ();
41+ for (auto const & value: _values)
42+ ret.push_back (value.str (_cfg));
43+ return ret;
3444}
3545
36- std::string SSACFGJsonExporter::varToString ( SSACFG const & _cfg, SSACFG::ValueId _var )
46+ Json toJson (Json& _ret, SSACFG const & _cfg, SSACFG::Operation const & _operation )
3747{
38- if (_var.value == std::numeric_limits<size_t >::max ())
39- return std::string (" INVALID" );
40- auto const & info = _cfg.valueInfo (_var);
41- return std::visit (
42- util::GenericVisitor{
43- [&](SSACFG::UnreachableValue const &) -> std::string {
44- return " [unreachable]" ;
45- },
46- [&](SSACFG::LiteralValue const & _literal) {
47- return toCompactHexWithPrefix (_literal.value );
48- },
49- [&](auto const &) {
50- return " v" + std::to_string (_var.value );
51- }
48+ Json opJson = Json::object ();
49+ std::visit (util::GenericVisitor{
50+ [&](SSACFG::Call const & _call) {
51+ _ret[" type" ] = " FunctionCall" ;
52+ opJson[" op" ] = _call.function .get ().name .str ();
5253 },
53- info
54- );
55- }
54+ [&](SSACFG::LiteralAssignment const &) {
55+ yulAssert (_operation.inputs .size () == 1 );
56+ yulAssert (_operation.inputs .back ().isLiteral ());
57+ opJson[" op" ] = " LiteralAssignment" ;
58+ },
59+ [&](SSACFG::BuiltinCall const & _call) {
60+ _ret[" type" ] = " BuiltinCall" ;
61+ Json builtinArgsJson = Json::array ();
62+ auto const & builtin = _call.builtin .get ();
63+ if (!builtin.literalArguments .empty ())
64+ {
65+ auto const & functionCallArgs = _call.call .get ().arguments ;
66+ for (size_t i = 0 ; i < builtin.literalArguments .size (); ++i)
67+ {
68+ std::optional<LiteralKind> const & argument = builtin.literalArguments [i];
69+ if (argument.has_value () && i < functionCallArgs.size ())
70+ {
71+ // The function call argument at index i must be a literal if builtin.literalArguments[i] is not nullopt
72+ yulAssert (std::holds_alternative<Literal>(functionCallArgs[i]));
73+ builtinArgsJson.push_back (formatLiteral (std::get<Literal>(functionCallArgs[i])));
74+ }
75+ }
76+ }
5677
57- Json SSACFGJsonExporter::run ()
58- {
59- if (m_liveness)
60- yulAssert (&m_liveness->controlFlow .get () == &m_controlFlow);
78+ if (!builtinArgsJson.empty ())
79+ opJson[" literalArgs" ] = builtinArgsJson;
6180
62- Json yulObjectJson = Json::object ();
63- yulObjectJson[" blocks" ] = exportBlock (*m_controlFlow.mainGraph , SSACFG::BlockId{0 }, m_liveness ? m_liveness->mainLiveness .get () : nullptr );
81+ opJson[" op" ] = _call.builtin .get ().name ;
82+ },
83+ }, _operation.kind );
6484
65- Json functionsJson = Json::object ();
66- size_t index = 0 ;
67- for (auto const & [function, functionGraph]: m_controlFlow.functionGraphMapping )
68- functionsJson[function->name .str ()] = exportFunction (*functionGraph, m_liveness ? m_liveness->functionLiveness [index++].get () : nullptr );
69- yulObjectJson[" functions" ] = functionsJson;
85+ opJson[" in" ] = toJson (_cfg, _operation.inputs );
86+ opJson[" out" ] = toJson (_cfg, _operation.outputs );
7087
71- return yulObjectJson ;
88+ return opJson ;
7289}
7390
74- Json SSACFGJsonExporter::exportFunction (SSACFG const & _cfg, LivenessAnalysis const * _liveness)
91+ Json toJson (SSACFG const & _cfg, SSACFG::BlockId _blockId , LivenessAnalysis const * _liveness)
7592{
76- Json functionJson = Json::object ();
77- functionJson[" type" ] = " Function" ;
78- functionJson[" entry" ] = " Block" + std::to_string (_cfg.entry .value );
79- static auto constexpr argsTransform = [](auto const & _arg) { return fmt::format (" v{}" , std::get<1 >(_arg).value ); };
80- functionJson[" arguments" ] = _cfg.arguments | ranges::views::transform (argsTransform) | ranges::to<std::vector>;
81- functionJson[" numReturns" ] = _cfg.returns .size ();
82- functionJson[" blocks" ] = exportBlock (_cfg, _cfg.entry , _liveness);
83- return functionJson;
93+ auto const valueToString = [&](LivenessAnalysis::LivenessData::LiveCounts::value_type const & _live) { return _live.first .str (_cfg); };
94+
95+ Json blockJson = Json::object ();
96+ auto const & block = _cfg.block (_blockId);
97+
98+ blockJson[" id" ] = " Block" + std::to_string (_blockId.value );
99+ if (_liveness)
100+ {
101+ Json livenessJson = Json::object ();
102+ livenessJson[" in" ] = _liveness->liveIn (_blockId)
103+ | ranges::views::transform (valueToString)
104+ | ranges::to<Json::array_t >();
105+ livenessJson[" out" ] = _liveness->liveOut (_blockId)
106+ | ranges::views::transform (valueToString)
107+ | ranges::to<Json::array_t >();
108+ blockJson[" liveness" ] = livenessJson;
109+ }
110+ blockJson[" instructions" ] = Json::array ();
111+ if (!block.phis .empty ())
112+ {
113+ blockJson[" entries" ] = block.entries
114+ | ranges::views::transform ([](auto const & entry) { return " Block" + std::to_string (entry.value ); })
115+ | ranges::to<Json::array_t >();
116+ for (auto const & phi: block.phis )
117+ {
118+ auto const & phiInfo = _cfg.phiInfo (phi);
119+ Json phiJson = Json::object ();
120+ phiJson[" op" ] = " PhiFunction" ;
121+ phiJson[" in" ] = toJson (_cfg, phiInfo.arguments );
122+ phiJson[" out" ] = toJson (_cfg, std::vector{phi});
123+ blockJson[" instructions" ].push_back (phiJson);
124+ }
125+ }
126+ for (auto const & operation: block.operations )
127+ blockJson[" instructions" ].push_back (toJson (blockJson, _cfg, operation));
128+
129+ return blockJson;
84130}
85131
86- Json SSACFGJsonExporter:: exportBlock (SSACFG const & _cfg, SSACFG::BlockId _entryId, LivenessAnalysis const * _liveness)
132+ Json exportBlock (SSACFG const & _cfg, SSACFG::BlockId _entryId, LivenessAnalysis const * _liveness)
87133{
88134 Json blocksJson = Json::array ();
89135 util::BreadthFirstSearch<SSACFG::BlockId> bfs{{{_entryId}}};
@@ -97,16 +143,14 @@ Json SSACFGJsonExporter::exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryI
97143 [&](SSACFG::BasicBlock::MainExit const &) {
98144 exitBlockJson[" type" ] = " MainExit" ;
99145 },
100- [&](SSACFG::BasicBlock::Jump const & _jump)
101- {
146+ [&](SSACFG::BasicBlock::Jump const & _jump){
102147 exitBlockJson[" targets" ] = { " Block" + std::to_string (_jump.target .value ) };
103148 exitBlockJson[" type" ] = " Jump" ;
104149 _addChild (_jump.target );
105150 },
106- [&](SSACFG::BasicBlock::ConditionalJump const & _conditionalJump)
107- {
151+ [&](SSACFG::BasicBlock::ConditionalJump const & _conditionalJump) {
108152 exitBlockJson[" targets" ] = { " Block" + std::to_string (_conditionalJump.zero .value ), " Block" + std::to_string (_conditionalJump.nonZero .value ) };
109- exitBlockJson[" cond" ] = varToString (_cfg, _conditionalJump.condition );
153+ exitBlockJson[" cond" ] = _conditionalJump.condition . str (_cfg );
110154 exitBlockJson[" type" ] = " ConditionalJump" ;
111155
112156 _addChild (_conditionalJump.zero );
@@ -130,100 +174,35 @@ Json SSACFGJsonExporter::exportBlock(SSACFG const& _cfg, SSACFG::BlockId _entryI
130174 return blocksJson;
131175}
132176
133- Json SSACFGJsonExporter::toJson (SSACFG const & _cfg, SSACFG::BlockId _blockId , LivenessAnalysis const * _liveness)
177+ Json exportFunction (SSACFG const & _cfg, LivenessAnalysis const * _liveness)
134178{
135- auto const valueToString = [&](LivenessAnalysis::LivenessData::LiveCounts::value_type const & _live) { return varToString (_cfg, _live.first ); };
136-
137- Json blockJson = Json::object ();
138- auto const & block = _cfg.block (_blockId);
139-
140- blockJson[" id" ] = " Block" + std::to_string (_blockId.value );
141- if (_liveness)
142- {
143- Json livenessJson = Json::object ();
144- livenessJson[" in" ] = _liveness->liveIn (_blockId)
145- | ranges::views::transform (valueToString)
146- | ranges::to<Json::array_t >();
147- livenessJson[" out" ] = _liveness->liveOut (_blockId)
148- | ranges::views::transform (valueToString)
149- | ranges::to<Json::array_t >();
150- blockJson[" liveness" ] = livenessJson;
151- }
152- blockJson[" instructions" ] = Json::array ();
153- if (!block.phis .empty ())
154- {
155- blockJson[" entries" ] = block.entries
156- | ranges::views::transform ([](auto const & entry) { return " Block" + std::to_string (entry.value ); })
157- | ranges::to<Json::array_t >();
158- for (auto const & phi: block.phis )
159- {
160- auto * phiInfo = std::get_if<SSACFG::PhiValue>(&_cfg.valueInfo (phi));
161- yulAssert (phiInfo);
162- Json phiJson = Json::object ();
163- phiJson[" op" ] = " PhiFunction" ;
164- phiJson[" in" ] = toJson (_cfg, phiInfo->arguments );
165- phiJson[" out" ] = toJson (_cfg, std::vector<SSACFG::ValueId>{phi});
166- blockJson[" instructions" ].push_back (phiJson);
167- }
168- }
169- for (auto const & operation: block.operations )
170- blockJson[" instructions" ].push_back (toJson (blockJson, _cfg, operation));
179+ Json functionJson = Json::object ();
180+ functionJson[" type" ] = " Function" ;
181+ functionJson[" entry" ] = " Block" + std::to_string (_cfg.entry .value );
182+ static auto constexpr argsTransform = [](auto const & _arg) { return fmt::format (" v{}" , std::get<1 >(_arg).value ()); };
183+ functionJson[" arguments" ] = _cfg.arguments | ranges::views::transform (argsTransform) | ranges::to<std::vector>;
184+ functionJson[" numReturns" ] = _cfg.returns .size ();
185+ functionJson[" blocks" ] = exportBlock (_cfg, _cfg.entry , _liveness);
186+ return functionJson;
187+ }
171188
172- return blockJson;
173189}
174190
175- Json SSACFGJsonExporter::toJson (Json& _ret, SSACFG const & _cfg, SSACFG::Operation const & _operation )
191+ Json json::exportControlFlow (ControlFlow const & _controlFlow, ControlFlowLiveness const * _liveness )
176192{
177- Json opJson = Json::object ();
178- std::visit (util::GenericVisitor{
179- [&](SSACFG::Call const & _call)
180- {
181- _ret[" type" ] = " FunctionCall" ;
182- opJson[" op" ] = _call.function .get ().name .str ();
183- },
184- [&](SSACFG::LiteralAssignment const &)
185- {
186- yulAssert (_operation.inputs .size () == 1 );
187- yulAssert (_cfg.isLiteralValue (_operation.inputs .back ()));
188- opJson[" op" ] = " LiteralAssignment" ;
189- },
190- [&](SSACFG::BuiltinCall const & _call)
191- {
192- _ret[" type" ] = " BuiltinCall" ;
193- Json builtinArgsJson = Json::array ();
194- auto const & builtin = _call.builtin .get ();
195- if (!builtin.literalArguments .empty ())
196- {
197- auto const & functionCallArgs = _call.call .get ().arguments ;
198- for (size_t i = 0 ; i < builtin.literalArguments .size (); ++i)
199- {
200- std::optional<LiteralKind> const & argument = builtin.literalArguments [i];
201- if (argument.has_value () && i < functionCallArgs.size ())
202- {
203- // The function call argument at index i must be a literal if builtin.literalArguments[i] is not nullopt
204- yulAssert (std::holds_alternative<Literal>(functionCallArgs[i]));
205- builtinArgsJson.push_back (formatLiteral (std::get<Literal>(functionCallArgs[i])));
206- }
207- }
208- }
209-
210- if (!builtinArgsJson.empty ())
211- opJson[" literalArgs" ] = builtinArgsJson;
212-
213- opJson[" op" ] = _call.builtin .get ().name ;
214- },
215- }, _operation.kind );
193+ if (_liveness)
194+ yulAssert (&_liveness->controlFlow .get () == &_controlFlow);
216195
217- opJson[ " in " ] = toJson (_cfg, _operation. inputs );
218- opJson[ " out " ] = toJson (_cfg, _operation. outputs );
196+ Json yulObjectJson = Json::object ( );
197+ yulObjectJson[ " blocks " ] = exportBlock (*_controlFlow. mainGraph (), SSACFG::BlockId{ 0 }, _liveness ? _liveness-> cfgLiveness . front (). get () : nullptr );
219198
220- return opJson;
221- }
199+ Json functionsJson = Json::object ();
200+ size_t index = 1 ;
201+ for (auto const & [function, functionGraph]: _controlFlow.functionGraphMapping | ranges::views::drop (1 ))
202+ {
203+ functionsJson[function->name .str ()] = exportFunction (*functionGraph, _liveness ? _liveness->cfgLiveness [index++].get () : nullptr );
204+ }
205+ yulObjectJson[" functions" ] = functionsJson;
222206
223- Json SSACFGJsonExporter::toJson (SSACFG const & _cfg, std::vector<SSACFG::ValueId> const & _values)
224- {
225- Json ret = Json::array ();
226- for (auto const & value: _values)
227- ret.push_back (varToString (_cfg, value));
228- return ret;
207+ return yulObjectJson;
229208}
0 commit comments