Skip to content

Commit 2ca5fb3

Browse files
authored
Merge pull request #16216 from argotorg/livenessUsageCounts
Modify SSA CFG liveness to contain (max) variable usage counts
2 parents a238a5d + 2f5dc0d commit 2ca5fb3

File tree

14 files changed

+399
-192
lines changed

14 files changed

+399
-192
lines changed

libyul/backends/evm/ssa/LivenessAnalysis.cpp

Lines changed: 174 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -20,46 +20,165 @@
2020

2121
#include <libsolutil/Visitor.h>
2222

23+
#include <range/v3/algorithm/find.hpp>
24+
#include <range/v3/algorithm/find_if.hpp>
2325
#include <range/v3/range/conversion.hpp>
26+
2427
#include <range/v3/view/filter.hpp>
2528
#include <range/v3/view/reverse.hpp>
2629

2730
using namespace solidity::yul::ssa;
2831

2932
namespace
3033
{
31-
constexpr auto literalsFilter(SSACFG const& _cfg)
34+
constexpr auto excludingLiteralsFilter(SSACFG const& _cfg)
35+
{
36+
return [&_cfg](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool
37+
{
38+
return !std::holds_alternative<SSACFG::LiteralValue>(_cfg.valueInfo(_valueId));
39+
};
40+
}
41+
constexpr auto unreachableFilter(SSACFG const& _cfg)
3242
{
33-
return [&_cfg](SSACFG::ValueId const& _valueId) -> bool
43+
return [&_cfg](LivenessAnalysis::LivenessData::Value const& _valueId) -> bool
3444
{
35-
return !std::holds_alternative<SSACFG::LiteralValue>(_cfg.valueInfo(_valueId));;
45+
return std::holds_alternative<SSACFG::UnreachableValue>(_cfg.valueInfo(_valueId));
3646
};
3747
}
3848
}
3949

40-
std::set<SSACFG::ValueId> LivenessAnalysis::blockExitValues(SSACFG::BlockId const& _blockId) const
50+
bool LivenessAnalysis::LivenessData::contains(Value const& _valueId) const
51+
{
52+
return findEntry(_valueId) != m_liveCounts.end();
53+
}
54+
55+
LivenessAnalysis::LivenessData::Count LivenessAnalysis::LivenessData::count(Value const& _valueId) const
56+
{
57+
if (
58+
auto const it = findEntry(_valueId);
59+
it != m_liveCounts.end()
60+
)
61+
return it->second;
62+
return 0;
63+
}
64+
65+
LivenessAnalysis::LivenessData::LiveCounts::const_iterator LivenessAnalysis::LivenessData::begin() const
66+
{
67+
return m_liveCounts.begin();
68+
}
69+
70+
LivenessAnalysis::LivenessData::LiveCounts::const_iterator LivenessAnalysis::LivenessData::end() const
71+
{
72+
return m_liveCounts.end();
73+
}
74+
75+
LivenessAnalysis::LivenessData::LiveCounts::size_type LivenessAnalysis::LivenessData::size() const
76+
{
77+
return m_liveCounts.size();
78+
}
79+
80+
bool LivenessAnalysis::LivenessData::empty() const { return m_liveCounts.empty(); }
81+
82+
void LivenessAnalysis::LivenessData::insert(Value const& _value, Count _count)
83+
{
84+
if (_count == 0)
85+
return;
86+
87+
auto it = findEntry(_value);
88+
if (it != m_liveCounts.end())
89+
it->second += _count;
90+
else
91+
m_liveCounts.emplace_back(_value, _count);
92+
}
93+
94+
LivenessAnalysis::LivenessData& LivenessAnalysis::LivenessData::maxUnion(LivenessData const& _other)
95+
{
96+
for (auto const& [value, count]: _other.m_liveCounts)
97+
{
98+
auto it = findEntry(value);
99+
if (it != m_liveCounts.end())
100+
it->second = std::max(it->second, count);
101+
else
102+
m_liveCounts.emplace_back(value, count);
103+
}
104+
return *this;
105+
}
106+
107+
LivenessAnalysis::LivenessData& LivenessAnalysis::LivenessData::operator+=(LivenessData const& _other)
41108
{
42-
std::set<SSACFG::ValueId> result;
43-
util::GenericVisitor exitVisitor {
109+
for (auto const& [valueId, count]: _other.m_liveCounts)
110+
insert(valueId, count);
111+
return *this;
112+
}
113+
114+
LivenessAnalysis::LivenessData& LivenessAnalysis::LivenessData::operator-=(LivenessData const& _other)
115+
{
116+
std::erase_if(m_liveCounts, [&](auto const& entry) { return _other.contains(entry.first); });
117+
return *this;
118+
}
119+
120+
void LivenessAnalysis::LivenessData::erase(Value const& _value)
121+
{
122+
if (
123+
auto const it = findEntry(_value);
124+
it != m_liveCounts.end()
125+
)
126+
m_liveCounts.erase(it);
127+
}
128+
129+
void LivenessAnalysis::LivenessData::remove(Value const& _value, Count _count)
130+
{
131+
if (_count == 0)
132+
return;
133+
134+
auto it = findEntry(_value);
135+
if (it != m_liveCounts.end())
136+
{
137+
if (it->second <= _count)
138+
m_liveCounts.erase(it);
139+
else
140+
it->second -= _count;
141+
}
142+
}
143+
144+
145+
LivenessAnalysis::LivenessData LivenessAnalysis::blockExitValues(SSACFG::BlockId const& _blockId) const
146+
{
147+
LivenessData result;
148+
util::GenericVisitor exitVisitor{
44149
[](SSACFG::BasicBlock::MainExit const&) {},
45-
[&](SSACFG::BasicBlock::FunctionReturn const& _functionReturn) {
46-
result += _functionReturn.returnValues | ranges::views::filter(literalsFilter(m_cfg));
150+
[&](SSACFG::BasicBlock::FunctionReturn const& _functionReturn)
151+
{
152+
for (auto const& valueId: _functionReturn.returnValues | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
153+
result.insert(valueId);
47154
},
48-
[&](SSACFG::BasicBlock::JumpTable const& _jt) {
49-
if (literalsFilter(m_cfg)(_jt.value))
50-
result.emplace(_jt.value);
155+
[&](SSACFG::BasicBlock::JumpTable const& _jt)
156+
{
157+
if (excludingLiteralsFilter(m_cfg)(_jt.value))
158+
result.insert(_jt.value);
51159
},
52160
[](SSACFG::BasicBlock::Jump const&) {},
53-
[&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump) {
54-
if (literalsFilter(m_cfg)(_conditionalJump.condition))
55-
result.emplace(_conditionalJump.condition);
161+
[&](SSACFG::BasicBlock::ConditionalJump const& _conditionalJump)
162+
{
163+
if (excludingLiteralsFilter(m_cfg)(_conditionalJump.condition))
164+
result.insert(_conditionalJump.condition);
56165
},
57-
[](SSACFG::BasicBlock::Terminated const&) {}
58-
};
166+
[](SSACFG::BasicBlock::Terminated const&) {}};
59167
std::visit(exitVisitor, m_cfg.block(_blockId).exit);
60168
return result;
61169
}
62170

171+
172+
LivenessAnalysis::LivenessData::LiveCounts::iterator LivenessAnalysis::LivenessData::findEntry(Value const& _value)
173+
{
174+
return ranges::find_if(m_liveCounts, [&](auto const& _entry) { return _entry.first == _value; });
175+
}
176+
177+
LivenessAnalysis::LivenessData::LiveCounts::const_iterator LivenessAnalysis::LivenessData::findEntry(Value const& _value) const
178+
{
179+
return ranges::find_if(m_liveCounts, [&](auto const& _entry) { return _entry.first == _value; });
180+
}
181+
63182
LivenessAnalysis::LivenessAnalysis(SSACFG const& _cfg):
64183
m_cfg(_cfg),
65184
m_topologicalSort(_cfg),
@@ -75,6 +194,14 @@ LivenessAnalysis::LivenessAnalysis(SSACFG const& _cfg):
75194
fillOperationsLiveOut();
76195
}
77196

197+
LivenessAnalysis::LivenessData LivenessAnalysis::used(SSACFG::BlockId const _blockId) const
198+
{
199+
auto used = liveIn(_blockId);
200+
for (auto const& [valueId, count]: liveOut(_blockId))
201+
used.remove(valueId, count);
202+
return used;
203+
}
204+
78205
void LivenessAnalysis::runDagDfs()
79206
{
80207
// SSA Book, Algorithm 9.2
@@ -85,7 +212,7 @@ void LivenessAnalysis::runDagDfs()
85212
auto const& block = m_cfg.block(blockId);
86213

87214
// live <- PhiUses(B)
88-
std::set<SSACFG::ValueId> live{};
215+
LivenessData live{};
89216
block.forEachExit(
90217
[&](SSACFG::BlockId const& _successor)
91218
{
@@ -105,15 +232,21 @@ void LivenessAnalysis::runDagDfs()
105232
block.forEachExit(
106233
[&](SSACFG::BlockId const& _successor) {
107234
if (!m_topologicalSort.backEdge(blockId, _successor))
108-
live += m_liveIns[_successor.value] - m_cfg.block(_successor).phis;
235+
{
236+
// LiveIn(S) - PhiDefs(S)
237+
auto liveInWithoutPhiDefs = m_liveIns[_successor.value];
238+
for (auto const& phiId: m_cfg.block(_successor).phis)
239+
liveInWithoutPhiDefs.erase(phiId);
240+
live.maxUnion(liveInWithoutPhiDefs);
241+
}
109242
});
110243

111244
if (std::holds_alternative<SSACFG::BasicBlock::FunctionReturn>(block.exit))
112-
live += std::get<SSACFG::BasicBlock::FunctionReturn>(block.exit).returnValues
113-
| ranges::views::filter(literalsFilter(m_cfg));
245+
for (auto const& returnValue: std::get<SSACFG::BasicBlock::FunctionReturn>(block.exit).returnValues | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
246+
live.insert(returnValue);
114247

115248
// clean out unreachables
116-
live = live | ranges::views::filter([&](auto const& valueId) { return !std::holds_alternative<SSACFG::UnreachableValue>(m_cfg.valueInfo(valueId)); }) | ranges::to<std::set>;
249+
live.eraseIf([&](auto const& _entry) { return unreachableFilter(m_cfg)(_entry.first); });
117250

118251
// LiveOut(B) <- live
119252
m_liveOuts[blockId.value] = live;
@@ -126,35 +259,39 @@ void LivenessAnalysis::runDagDfs()
126259
for (auto const& op: block.operations | ranges::views::reverse)
127260
{
128261
// remove variables defined at p from live
129-
live -= op.outputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to<std::vector>;
262+
live.eraseAll(op.outputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)) | ranges::to<std::vector>);
130263
// add uses at p to live
131-
live += op.inputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to<std::vector>;
264+
live.insertAll(op.inputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)) | ranges::to<std::vector>);
132265
}
133266
}
134267

135268
// livein(b) <- live \cup PhiDefs(B)
136-
m_liveIns[blockId.value] = live + block.phis;
269+
for (auto const& phi: block.phis)
270+
live.insert(phi);
271+
m_liveIns[blockId.value] = live;
137272
}
138273
}
139274

140275
void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader)
141276
{
142277
// SSA Book, Algorithm 9.3
143-
if (m_loopNestingForest.loopNodes().count(_loopHeader) > 0)
278+
if (m_loopNestingForest.loopNodes().contains(_loopHeader))
144279
{
145280
// the loop header block id
146281
auto const& block = m_cfg.block(SSACFG::BlockId{_loopHeader});
147282
// LiveLoop <- LiveIn(B_N) - PhiDefs(B_N)
148-
auto liveLoop = m_liveIns[_loopHeader] - block.phis;
283+
auto liveLoop = m_liveIns[_loopHeader];
284+
for (auto const& phi: block.phis)
285+
liveLoop.erase(phi);
149286
// must be live out of header if live in of children
150-
m_liveOuts[_loopHeader] += liveLoop;
287+
m_liveOuts[_loopHeader].maxUnion(liveLoop);
151288
// for each blockId \in children(loopHeader)
152289
for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue)
153290
if (m_loopNestingForest.loopParents()[blockIdValue] == _loopHeader)
154291
{
155292
// propagate loop liveness information down to the loop header's children
156-
m_liveIns[blockIdValue] += liveLoop;
157-
m_liveOuts[blockIdValue] += liveLoop;
293+
m_liveIns[blockIdValue].maxUnion(liveLoop);
294+
m_liveOuts[blockIdValue].maxUnion(liveLoop);
158295

159296
runLoopTreeDfs(blockIdValue);
160297
}
@@ -163,21 +300,23 @@ void LivenessAnalysis::runLoopTreeDfs(size_t const _loopHeader)
163300

164301
void LivenessAnalysis::fillOperationsLiveOut()
165302
{
166-
for (size_t blockIdValue = 0; blockIdValue < m_cfg.numBlocks(); ++blockIdValue)
303+
for (SSACFG::BlockId blockId{0}; blockId.value < m_cfg.numBlocks(); ++blockId.value)
167304
{
168-
SSACFG::BlockId const blockId{blockIdValue};
169305
auto const& operations = m_cfg.block(blockId).operations;
170-
auto& liveOuts = m_operationLiveOuts[blockIdValue];
306+
auto& liveOuts = m_operationLiveOuts[blockId.value];
171307
liveOuts.resize(operations.size());
172308
if (!operations.empty())
173309
{
174-
auto live = m_liveOuts[blockIdValue] + blockExitValues(blockId);
310+
auto live = m_liveOuts[blockId.value];
311+
live += blockExitValues(blockId);
175312
auto rit = liveOuts.rbegin();
176313
for (auto const& op: operations | ranges::views::reverse)
177314
{
178315
*rit = live;
179-
live -= op.outputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to<std::vector>;
180-
live += op.inputs | ranges::views::filter(literalsFilter(m_cfg)) | ranges::to<std::vector>;
316+
for (auto const& output: op.outputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
317+
live.erase(output);
318+
for (auto const& input: op.inputs | ranges::views::filter(excludingLiteralsFilter(m_cfg)))
319+
live.insert(input);
181320
++rit;
182321
}
183322
}

0 commit comments

Comments
 (0)