Skip to content

Commit 1ea88b8

Browse files
committed
SSACFG: Use tagged union for ValueId
1 parent 343373b commit 1ea88b8

21 files changed

+523
-463
lines changed

libyul/backends/evm/ssa/ControlFlow.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ using namespace solidity::yul::ssa;
2424

2525
ControlFlowLiveness::ControlFlowLiveness(ControlFlow const& _controlFlow):
2626
controlFlow(_controlFlow),
27-
mainLiveness(std::make_unique<LivenessAnalysis>(*_controlFlow.mainGraph)),
28-
functionLiveness(_controlFlow.functionGraphs | ranges::views::transform([](auto const& _cfg) { return std::make_unique<LivenessAnalysis>(*_cfg); }) | ranges::to<std::vector>)
27+
cfgLiveness(_controlFlow.functionGraphs | ranges::views::transform([](auto const& _cfg) { return std::make_unique<LivenessAnalysis>(*_cfg); }) | ranges::to<std::vector>)
2928
{ }
3029

3130
std::string ControlFlowLiveness::toDot() const

libyul/backends/evm/ssa/ControlFlow.h

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
#include <libyul/AST.h>
2525
#include <libyul/Scope.h>
2626

27+
#include <range/v3/algorithm/find_if.hpp>
28+
2729
namespace solidity::yul::ssa
2830
{
2931

@@ -33,44 +35,52 @@ struct ControlFlowLiveness{
3335
explicit ControlFlowLiveness(ControlFlow const& _controlFlow);
3436

3537
std::reference_wrapper<ControlFlow const> controlFlow;
36-
std::unique_ptr<LivenessAnalysis> mainLiveness;
37-
std::vector<std::unique_ptr<LivenessAnalysis>> functionLiveness;
38+
std::vector<std::unique_ptr<LivenessAnalysis>> cfgLiveness;
3839

3940
std::string toDot() const;
4041
};
4142

4243
struct ControlFlow
4344
{
44-
std::unique_ptr<SSACFG> mainGraph{std::make_unique<SSACFG>()};
45-
std::vector<std::unique_ptr<SSACFG>> functionGraphs{};
46-
std::vector<std::tuple<Scope::Function const*, SSACFG const*>> functionGraphMapping{};
45+
using FunctionGraphID = std::uint32_t;
4746

48-
SSACFG const* functionGraph(Scope::Function const* _function)
47+
static FunctionGraphID constexpr mainGraphID() noexcept { return 0; }
48+
49+
SSACFG const* mainGraph() const { return functionGraph(mainGraphID()); }
50+
51+
SSACFG const* functionGraph(Scope::Function const* _function) const
4952
{
50-
auto it = std::find_if(functionGraphMapping.begin(), functionGraphMapping.end(), [_function](auto const& tup) { return _function == std::get<0>(tup); });
53+
auto it = ranges::find_if(functionGraphMapping, [_function](auto const& tup) { return _function == std::get<0>(tup); });
5154
if (it != functionGraphMapping.end())
5255
return std::get<1>(*it);
5356
return nullptr;
5457
}
5558

59+
SSACFG const* functionGraph(FunctionGraphID const _id) const
60+
{
61+
return functionGraphs.at(_id).get();
62+
}
63+
5664
std::string toDot(ControlFlowLiveness const* _liveness=nullptr) const
5765
{
5866
if (_liveness)
5967
yulAssert(&_liveness->controlFlow.get() == this);
6068
std::ostringstream output;
6169
output << "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\"]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n";
62-
output << mainGraph->toDot(false, std::nullopt, _liveness ? _liveness->mainLiveness.get() : nullptr);
6370

6471
for (size_t index=0; index < functionGraphs.size(); ++index)
6572
output << functionGraphs[index]->toDot(
6673
false,
67-
index+1,
68-
_liveness ? _liveness->functionLiveness[index].get() : nullptr
74+
index,
75+
_liveness ? _liveness->cfgLiveness[index].get() : nullptr
6976
);
7077

7178
output << "}\n";
7279
return output.str();
7380
}
81+
82+
std::vector<std::unique_ptr<SSACFG>> functionGraphs{};
83+
std::vector<std::tuple<Scope::Function const*, SSACFG const*>> functionGraphMapping{};
7484
};
7585

7686
}

libyul/backends/evm/ssa/LivenessAnalysis.cpp

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,11 @@ using namespace solidity::yul::ssa;
3131

3232
namespace
3333
{
34-
constexpr auto excludingLiteralsFilter(SSACFG const& _cfg)
34+
constexpr auto excludingLiteralsFilter()
3535
{
36-
return [&_cfg](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool
36+
return [](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool
3737
{
38-
return !std::holds_alternative<SSACFG::LiteralValue>(_cfg.valueInfo(_valueId));
39-
};
40-
}
41-
constexpr auto unreachableFilter(SSACFG const& _cfg)
42-
{
43-
return [&_cfg](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool
44-
{
45-
return std::holds_alternative<SSACFG::UnreachableValue>(_cfg.valueInfo(_valueId));
38+
return !_valueId.isLiteral();
4639
};
4740
}
4841
}
@@ -149,18 +142,18 @@ LivenessAnalysis::LivenessData LivenessAnalysis::blockExitValues(SSACFG::BlockId
149142
[](SSACFG::BasicBlock::MainExit const&) {},
150143
[&](SSACFG::BasicBlock::FunctionReturn const& _functionReturn)
151144
{
152-
for (auto const& valueId: _functionReturn.returnValues | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
145+
for (auto const& valueId: _functionReturn.returnValues | ranges::views::filter(excludingLiteralsFilter()))
153146
result.insert(valueId);
154147
},
155148
[&](SSACFG::BasicBlock::JumpTable const& _jt)
156149
{
157-
if (excludingLiteralsFilter(m_cfg)(_jt.value))
150+
if (excludingLiteralsFilter()(_jt.value))
158151
result.insert(_jt.value);
159152
},
160153
[](SSACFG::BasicBlock::Jump const&) {},
161154
[&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump)
162155
{
163-
if (excludingLiteralsFilter(m_cfg)(_conditionalJump.condition))
156+
if (excludingLiteralsFilter()(_conditionalJump.condition))
164157
result.insert(_conditionalJump.condition);
165158
},
166159
[](SSACFG::BasicBlock::Terminated const&) {}};
@@ -218,12 +211,11 @@ void LivenessAnalysis::runDagDfs()
218211
{
219212
for (auto const& phi: m_cfg.block(_successor).phis)
220213
{
221-
auto const& info = m_cfg.valueInfo(phi);
222-
yulAssert(std::holds_alternative<SSACFG::PhiValue>(info), "value info of phi wasn't PhiValue");
223-
auto const argIndex = m_cfg.phiArgumentIndex(blockId, _successor);
224-
yulAssert(argIndex < std::get<SSACFG::PhiValue>(info).arguments.size());
225-
auto const arg = std::get<SSACFG::PhiValue>(info).arguments.at(argIndex);
226-
if (!std::holds_alternative<SSACFG::LiteralValue>(m_cfg.valueInfo(arg)))
214+
auto const& info = m_cfg.phiInfo(phi);
215+
auto const& argIndex = m_cfg.phiArgumentIndex(blockId, _successor);
216+
yulAssert(argIndex < info.arguments.size());
217+
auto const& arg = info.arguments.at(argIndex);
218+
if (!arg.isLiteral())
227219
live.insert(arg);
228220
}
229221
});
@@ -242,11 +234,11 @@ void LivenessAnalysis::runDagDfs()
242234
});
243235

244236
if (std::holds_alternative<SSACFG::BasicBlock::FunctionReturn>(block.exit))
245-
for (auto const& returnValue: std::get<SSACFG::BasicBlock::FunctionReturn>(block.exit).returnValues | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
237+
for (auto const& returnValue: std::get<SSACFG::BasicBlock::FunctionReturn>(block.exit).returnValues | ranges::views::filter(excludingLiteralsFilter()))
246238
live.insert(returnValue);
247239

248240
// clean out unreachables
249-
live.eraseIf([&](auto const& _entry) { return unreachableFilter(m_cfg)(_entry.first); });
241+
live.eraseIf([&](auto const& _entry) { return _entry.first.isUnreachable(); });
250242

251243
// LiveOut(B) <- live
252244
m_liveOuts[blockId.value] = live;
@@ -259,9 +251,9 @@ void LivenessAnalysis::runDagDfs()
259251
for (auto const& op: block.operations | ranges::views::reverse)
260252
{
261253
// remove variables defined at p from live
262-
live.eraseAll(op.outputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)) | ranges::to<std::vector>);
254+
live.eraseAll(op.outputs | ranges::views::filter(excludingLiteralsFilter()) | ranges::to<std::vector>);
263255
// add uses at p to live
264-
live.insertAll(op.inputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)) | ranges::to<std::vector>);
256+
live.insertAll(op.inputs | ranges::views::filter(excludingLiteralsFilter()) | ranges::to<std::vector>);
265257
}
266258
}
267259

@@ -272,7 +264,7 @@ void LivenessAnalysis::runDagDfs()
272264
}
273265
}
274266

275-
void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader)
267+
void LivenessAnalysis::runLoopTreeDfs(SSACFG::BlockId::ValueType const _loopHeader)
276268
{
277269
// SSA Book, Algorithm 9.3
278270
if (m_loopNestingForest.loopNodes().contains(_loopHeader))
@@ -286,7 +278,7 @@ void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader)
286278
// must be live out of header if live in of children
287279
m_liveOuts[_loopHeader].maxUnion(liveLoop);
288280
// for each blockId \in children(loopHeader)
289-
for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue)
281+
for (SSACFG::BlockId::ValueType blockIdValue = 0u; blockIdValue < m_cfg.numBlocks(); ++blockIdValue)
290282
if (m_loopNestingForest.loopParents()[blockIdValue] == _loopHeader)
291283
{
292284
// propagate loop liveness information down to the loop header's children
@@ -313,9 +305,9 @@ void LivenessAnalysis::fillOperationsLiveOut()
313305
for (auto const& op: operations | ranges::views::reverse)
314306
{
315307
*rit = live;
316-
for (auto const& output: op.outputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
308+
for (auto const& output: op.outputs | ranges::views::filter(excludingLiteralsFilter()))
317309
live.erase(output);
318-
for (auto const& input: op.inputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
310+
for (auto const& input: op.inputs | ranges::views::filter(excludingLiteralsFilter()))
319311
live.insert(input);
320312
++rit;
321313
}

libyul/backends/evm/ssa/LivenessAnalysis.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,7 @@ class LivenessAnalysis
4242

4343
LivenessData() = default;
4444
template<std::input_iterator Iter, std::sentinel_for<Iter> Sentinel>
45-
LivenessData(Iter begin, Sentinel end): m_liveCounts(begin, end) {
46-
}
45+
LivenessData(Iter begin, Sentinel end): m_liveCounts(begin, end) {}
4746
explicit LivenessData(LiveCounts&& _liveCounts): m_liveCounts(std::move(_liveCounts)) {}
4847

4948
bool contains(Value const& _valueId) const;
@@ -113,7 +112,7 @@ class LivenessAnalysis
113112

114113
private:
115114
void runDagDfs();
116-
void runLoopTreeDfs(std::size_t _loopHeader);
115+
void runLoopTreeDfs(SSACFG::BlockId::ValueType _loopHeader);
117116
void fillOperationsLiveOut();
118117
LivenessData blockExitValues(SSACFG::BlockId const& _blockId) const;
119118

libyul/backends/evm/ssa/SSACFG.cpp

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#include <fmt/ranges.h>
2929
#pragma GCC diagnostic pop
3030

31+
#include <libyul/backends/evm/ssa/JunkAdmittingBlocksFinder.h>
32+
3133
#include <range/v3/view/zip.hpp>
3234

3335
using namespace solidity;
@@ -43,41 +45,23 @@ class SSACFGPrinter
4345
SSACFGPrinter(SSACFG const& _cfg, SSACFG::BlockId _blockId, LivenessAnalysis const* _liveness):
4446
m_cfg(_cfg), m_functionIndex(0), m_liveness(_liveness)
4547
{
48+
if (_liveness)
49+
m_junkAdmittingBlocks = std::make_unique<JunkAdmittingBlocksFinder>(_cfg, _liveness->topologicalSort());
4650
printBlock(_blockId);
4751
}
4852
SSACFGPrinter(SSACFG const& _cfg, size_t _functionIndex, Scope::Function const& _function, LivenessAnalysis const* _liveness):
4953
m_cfg(_cfg), m_functionIndex(_functionIndex), m_liveness(_liveness)
5054
{
55+
if (_liveness)
56+
m_junkAdmittingBlocks = std::make_unique<JunkAdmittingBlocksFinder>(_cfg, _liveness->topologicalSort());
5157
printFunction(_function);
5258
}
5359
friend std::ostream& operator<<(std::ostream& stream, SSACFGPrinter const& printer) {
5460
stream << printer.m_result.str();
5561
return stream;
5662
}
57-
private:
58-
static std::string varToString(SSACFG const& _cfg, SSACFG::ValueId _var) {
59-
if (_var.value == std::numeric_limits<size_t>::max())
60-
return "INVALID";
61-
auto const& info = _cfg.valueInfo(_var);
62-
return std::visit(
63-
GenericVisitor{
64-
[&](SSACFG::UnreachableValue const&) -> std::string {
65-
return "[unreachable]";
66-
},
67-
[&](SSACFG::PhiValue const&) -> std::string {
68-
return fmt::format("v{}", _var.value);
69-
},
70-
[&](SSACFG::VariableValue const&) -> std::string {
71-
return fmt::format("v{}", _var.value);
72-
},
73-
[&](SSACFG::LiteralValue const& _literal) -> std::string {
74-
return formatNumberReadable(_literal.value);
75-
}
76-
},
77-
info
78-
);
79-
}
8063

64+
private:
8165
static std::string escape(std::string_view const str)
8266
{
8367
using namespace std::literals;
@@ -113,7 +97,7 @@ class SSACFGPrinter
11397

11498
static std::string formatPhi(SSACFG const& _cfg, SSACFG::PhiValue const& _phiValue)
11599
{
116-
auto const transform = [&](SSACFG::ValueId const& valueId) { return varToString(_cfg, valueId); };
100+
auto const transform = [&](SSACFG::ValueId const& valueId) { return valueId.str(_cfg); };
117101
std::vector<std::string> formattedArgs;
118102
formattedArgs.reserve(_phiValue.arguments.size());
119103
for (auto const& [arg, entry]: ranges::zip_view(_phiValue.arguments | ranges::views::transform(transform), _cfg.block(_phiValue.block).entries))
@@ -126,19 +110,23 @@ class SSACFGPrinter
126110

127111
void writeBlock(SSACFG::BlockId const& _id, SSACFG::BasicBlock const& _block)
128112
{
129-
auto const valueToString = [&](SSACFG::ValueId const& valueId) { return varToString(m_cfg, valueId); };
113+
auto const valueToString = [&](SSACFG::ValueId const& valueId) { return valueId.str(m_cfg); };
130114
bool entryBlock = _id.value == 0 && m_functionIndex == 0;
131115
if (entryBlock)
132116
{
133117
m_result << fmt::format("Entry{} [label=\"Entry\"];\n", m_functionIndex);
134118
m_result << fmt::format("Entry{} -> {};\n", m_functionIndex, formatBlockHandle(_id));
135119
}
136120
{
121+
std::string revertPathInfo;
122+
if (m_junkAdmittingBlocks)
123+
revertPathInfo = m_junkAdmittingBlocks->allowsAdditionOfJunk(_id) ? "fillcolor=\"#FF746C\", style=filled, " : "";
137124
if (m_liveness)
138125
{
139126
m_result << fmt::format(
140-
"{} [label=\"\\\nBlock {}; ({}, max {})\\n",
127+
"{} [{}label=\"\\\nBlock {}; ({}, max {})\\n",
141128
formatBlockHandle(_id),
129+
revertPathInfo,
142130
_id.value,
143131
m_liveness->topologicalSort().preOrderIndexOf(_id.value),
144132
m_liveness->topologicalSort().maxSubtreePreOrderIndexOf(_id.value)
@@ -151,14 +139,19 @@ class SSACFGPrinter
151139
"LiveOut: {}\\l\\n",
152140
fmt::join(m_liveness->liveOut(_id) | ranges::views::transform([&](auto const& liveOut) { return valueToString(SSACFG::ValueId{liveOut.first}) + fmt::format("[{}]", liveOut.second); }), ", ")
153141
);
142+
auto const usedVariables = m_liveness->used(_id);
143+
m_result << fmt::format(
144+
"Used: {}\\l\\n",
145+
fmt::join(usedVariables | ranges::views::transform([&](auto const& used) { return valueToString(SSACFG::ValueId{used.first}) + fmt::format("[{}]", used.second); }), ", ")
146+
);
154147
}
155148
else
156-
m_result << fmt::format("{} [label=\"\\\nBlock {}\\n", formatBlockHandle(_id), _id.value);
149+
m_result << fmt::format("{} [{}label=\"\\\nBlock {}\\n", formatBlockHandle(_id), revertPathInfo, _id.value);
150+
157151
for (auto const& phi: _block.phis)
158152
{
159-
auto const* phiValue = std::get_if<SSACFG::PhiValue>(&m_cfg.valueInfo(phi));
160-
solAssert(phiValue);
161-
m_result << fmt::format("v{} := {}\\l\\\n", phi.value, formatPhi(m_cfg, *phiValue));
153+
auto const& phiInfo = m_cfg.phiInfo(phi);
154+
m_result << fmt::format("phi{} := {}\\l\\\n", phi.value(), formatPhi(m_cfg, phiInfo));
162155
}
163156
for (auto const& operation: _block.operations)
164157
{
@@ -172,7 +165,7 @@ class SSACFGPrinter
172165
[&](SSACFG::LiteralAssignment const&)
173166
{
174167
yulAssert(operation.inputs.size() == 1);
175-
return varToString(m_cfg, operation.inputs.back());
168+
return operation.inputs.back().str(m_cfg);
176169
}
177170
}, operation.kind);
178171
if (!operation.outputs.empty())
@@ -210,7 +203,7 @@ class SSACFGPrinter
210203
m_result << fmt::format("{} -> {}Exit;\n", formatBlockHandle(_id), formatBlockHandle(_id));
211204
m_result << fmt::format(
212205
"{}Exit [label=\"{{ If {} | {{ <0> Zero | <1> NonZero }}}}\" shape=Mrecord];\n",
213-
formatBlockHandle(_id), varToString(m_cfg, _conditionalJump.condition)
206+
formatBlockHandle(_id), _conditionalJump.condition.str(m_cfg)
214207
);
215208
m_result << formatEdge(_id, _conditionalJump.zero, "0");
216209
m_result << formatEdge(_id, _conditionalJump.nonZero, "1");
@@ -279,7 +272,7 @@ class SSACFGPrinter
279272
void printFunction(Scope::Function const& _fun)
280273
{
281274
static auto constexpr returnsTransform = [](auto const& functionReturnValue) { return escape(functionReturnValue.get().name.str()); };
282-
static auto constexpr argsTransform = [](auto const& arg) { return fmt::format("v{}", std::get<1>(arg).value); };
275+
static auto constexpr argsTransform = [](auto const& arg) { return fmt::format("v{}", std::get<1>(arg).value()); };
283276
m_result << "FunctionEntry_" << escape(_fun.name.str()) << "_" << m_cfg.entry.value << " [label=\"";
284277
if (!m_cfg.returns.empty())
285278
m_result << fmt::format("function {0}:\n {1} := {0}({2})", escape(_fun.name.str()), fmt::join(m_cfg.returns | ranges::views::transform(returnsTransform), ", "), fmt::join(m_cfg.arguments | ranges::views::transform(argsTransform), ", "));
@@ -291,12 +284,28 @@ class SSACFGPrinter
291284
}
292285

293286
SSACFG const& m_cfg;
287+
std::unique_ptr<JunkAdmittingBlocksFinder> m_junkAdmittingBlocks;
294288
size_t m_functionIndex;
295289
LivenessAnalysis const* m_liveness;
296290
std::stringstream m_result{};
297291
};
298292
}
299293

294+
std::string SSACFG::ValueId::str(SSACFG const& _cfg) const
295+
{
296+
if (!hasValue())
297+
return "INVALID";
298+
switch (kind())
299+
{
300+
case Kind::Literal: return toCompactHexWithPrefix(_cfg.literalInfo(*this).value);
301+
case Kind::Variable: return fmt::format("v{}", value());
302+
case Kind::Phi: return fmt::format("phi{}", value());
303+
case Kind::Unreachable: return "[unreachable]";
304+
}
305+
unreachable();
306+
}
307+
308+
300309
std::string SSACFG::toDot(
301310
bool _includeDiGraphDefinition,
302311
std::optional<size_t> _functionIndex,
@@ -305,7 +314,7 @@ std::string SSACFG::toDot(
305314
{
306315
std::ostringstream output;
307316
if (_includeDiGraphDefinition)
308-
output << "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\"]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n";
317+
output << "digraph SSACFG {\nnodesep=0.7;\ngraph[fontname=\"DejaVu Sans\", rankdir=LR]\nnode[shape=box,fontname=\"DejaVu Sans\"];\n\n";
309318
if (function)
310319
output << SSACFGPrinter(*this, _functionIndex ? *_functionIndex : static_cast<size_t>(1), *function, _liveness);
311320
else

0 commit comments

Comments
 (0)