Skip to content

Commit d5d29bd

Browse files
committed
SSACFG: exporting to json is stateless
1 parent 1ea88b8 commit d5d29bd

File tree

6 files changed

+590
-628
lines changed

6 files changed

+590
-628
lines changed

libyul/YulStack.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,7 @@ Json YulStack::cfgJson() const
405405
keepLiteralAssignments
406406
);
407407
std::unique_ptr<ssa::ControlFlowLiveness> liveness = std::make_unique<ssa::ControlFlowLiveness>(*controlFlow);
408-
ssa::SSACFGJsonExporter exporter(*controlFlow, liveness.get());
409-
return exporter.run();
408+
return ssa::json::exportControlFlow(*controlFlow, liveness.get());
410409
};
411410

412411
std::function<Json(std::vector<std::shared_ptr<ObjectNode>>)> exportCFGFromSubObjects;

libyul/backends/evm/ssa/SSACFGJsonExporter.cpp

Lines changed: 114 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -17,73 +17,119 @@
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

2931
using namespace solidity;
32+
using namespace solidity::yul;
3033
using 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
}

libyul/backends/evm/ssa/SSACFGJsonExporter.h

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,10 @@
2020

2121
#include <libyul/backends/evm/ssa/ControlFlow.h>
2222
#include <libsolutil/JSON.h>
23-
#include <libsolutil/Visitor.h>
2423

25-
namespace solidity::yul::ssa
24+
namespace solidity::yul::ssa::json
2625
{
2726

28-
class SSACFGJsonExporter
29-
{
30-
public:
31-
SSACFGJsonExporter(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness=nullptr);
32-
Json run();
33-
Json exportBlock(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness);
34-
Json exportFunction(SSACFG const& _cfg, LivenessAnalysis const* _liveness);
35-
std::string varToString(SSACFG const& _cfg, SSACFG::ValueId _var);
36-
37-
private:
38-
ControlFlow const& m_controlFlow;
39-
ControlFlowLiveness const* m_liveness;
40-
Json toJson(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness);
41-
Json toJson(Json& _ret, SSACFG const& _cfg, SSACFG::Operation const& _operation);
42-
Json toJson(SSACFG const& _cfg, std::vector<SSACFG::ValueId> const& _values);
43-
};
27+
Json exportControlFlow(ControlFlow const& _controlFlow, ControlFlowLiveness const* _liveness);
4428

4529
}

0 commit comments

Comments
 (0)