Skip to content

Commit 2bc75a2

Browse files
committed
optimize operation forward shuffler
1 parent deced6c commit 2bc75a2

File tree

1 file changed

+39
-70
lines changed

1 file changed

+39
-70
lines changed

libyul/backends/evm/ssa/OperationForwardShuffler.h

Lines changed: 39 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -11,28 +11,15 @@
1111
#include <range/v3/view/take.hpp>
1212
#include <range/v3/range_concepts.hpp>
1313

14+
#include <boost/container/flat_map.hpp>
15+
1416
#include <cstdint>
1517
#include <map>
1618
#include <variant>
1719

1820
namespace solidity::yul::ssa
1921
{
2022

21-
namespace detail
22-
{
23-
template<ranges::range Slots, typename Slot = ranges::range_value_t<Slots>>
24-
std::map<Slot, size_t> histogram(Slots const& _slots)
25-
{
26-
std::map<Slot, size_t> result;
27-
for (auto const& slot: _slots)
28-
{
29-
auto const [it, _] = result.try_emplace(slot);
30-
++it->second;
31-
}
32-
return result;
33-
}
34-
}
35-
3623
template<StackManipulationCallbackConcept Callback, size_t ReachableStackDepth=16>
3724
class OperationForwardShuffler
3825
{
@@ -48,54 +35,28 @@ class OperationForwardShuffler
4835
{
4936
yulAssert(ranges::none_of(_args, [](auto const& _slot) { return _slot.isJunk(); }));
5037

38+
TargetStats const targetStats(_args, _liveOut);
5139
constexpr std::size_t maxIterations = 1000;
5240
std::size_t i = 0;
53-
for (; i < maxIterations && shuffleStep(_stack, _args, _liveOut, _generateJunk); ++i) {}
41+
for (; i < maxIterations && shuffleStep(_stack, _args, _liveOut, targetStats, _generateJunk); ++i) {}
5442
yulAssert(i < maxIterations, fmt::format("Maximum iterations reached on {}", stackToString(_stack.data())));
5543
}
5644

5745
private:
58-
static std::ptrdiff_t loss(Stack<Callback>::Data const& _stackData, std::vector<Slot> const& _args, LivenessAnalysis::LivenessData const& _liveOut)
59-
{
60-
std::ptrdiff_t result = 0;
61-
62-
// every correct slot in the args gets a plus, every incorrect/missing one a minus
63-
for (size_t i = 0; i < _args.size(); ++i)
64-
if (_stackData.size() > i && _stackData[_stackData.size() - i - 1] == _args[_args.size() - i - 1])
65-
++result;
66-
else
67-
--result;
68-
for (auto const& [liveOutValue, _]: _liveOut)
69-
{
70-
if (_stackData.size() < _args.size())
71-
--result;
72-
73-
auto it = ranges::find(_stackData | ranges::views::reverse | ranges::views::drop(_args.size()), liveOutValue);
74-
if (it == ranges::end(_stackData))
75-
--result;
76-
else
77-
++result;
78-
}
79-
80-
return result;
81-
}
82-
8346
struct StackStats
8447
{
8548
StackStats(Stack<Callback> const& _stack, size_t _argsRegionSize)
8649
{
87-
histogramTail = detail::histogram(_stack.data() | ranges::views::reverse | ranges::views::drop(_argsRegionSize));
88-
histogram = histogramTail;
89-
for (Slot const& argsSlot: _stack.data() | ranges::views::reverse | ranges::views::take(_argsRegionSize))
50+
histogram.reserve(_stack.size());
51+
histogramTail.reserve(_stack.size());
52+
histogramArgs.reserve(_argsRegionSize);
53+
for (auto const& [i, slot]: _stack | ranges::views::enumerate)
9054
{
91-
{
92-
auto const [it, _] = histogram.try_emplace(argsSlot);
93-
++it->second;
94-
}
95-
{
96-
auto const [it, _] = histogramArgs.try_emplace(argsSlot);
97-
++it->second;
98-
}
55+
++histogram[slot];
56+
if (_stack.size() >= _argsRegionSize && i < _stack.size() - _argsRegionSize)
57+
++histogramTail[slot];
58+
else
59+
++histogramArgs[slot];
9960
}
10061
}
10162

@@ -115,26 +76,33 @@ class OperationForwardShuffler
11576
}
11677

11778
private:
118-
std::map<Slot, size_t> histogramTail;
119-
std::map<Slot, size_t> histogramArgs;
120-
std::map<Slot, size_t> histogram;
79+
boost::container::flat_map<Slot, size_t> histogramTail;
80+
boost::container::flat_map<Slot, size_t> histogramArgs;
81+
boost::container::flat_map<Slot, size_t> histogram;
12182
};
12283

84+
struct TargetStats
85+
{
86+
TargetStats(std::vector<Slot> const& _args, LivenessAnalysis::LivenessData const& _liveOut)
87+
{
88+
targetMinCounts.reserve(_args.size() + _liveOut.size());
89+
for (auto const& arg: _args)
90+
++targetMinCounts[arg];
91+
for (auto const& _liveValueId: _liveOut | ranges::views::keys)
92+
++targetMinCounts[Slot::makeValueID(_liveValueId)];
93+
}
94+
95+
boost::container::flat_map<Slot, size_t> targetMinCounts;
96+
};
12397
struct Ops
12498
{
125-
Ops(Stack<Callback> const& _stack, std::vector<Slot> const& _args, LivenessAnalysis::LivenessData const& _liveOut):
99+
Ops(Stack<Callback>& _stack, std::vector<Slot> const& _args, LivenessAnalysis::LivenessData const& _liveOut, TargetStats const& _targetStats):
126100
stackStats(_stack, _args.size()),
127-
targetMinCounts(detail::histogram(_args)),
128101
stack(_stack),
129102
args(_args),
130-
liveOut(_liveOut)
131-
{
132-
for (auto const& _liveValueId: _liveOut | ranges::views::keys)
133-
{
134-
auto const [it, _] = targetMinCounts.try_emplace(Slot::makeValueID(_liveValueId));
135-
++it->second;
136-
}
137-
}
103+
liveOut(_liveOut),
104+
targetStats(_targetStats)
105+
{}
138106

139107
bool argsRegionIsCorrect() const
140108
{
@@ -156,15 +124,15 @@ class OperationForwardShuffler
156124

157125
bool distributionIsCorrect() const
158126
{
159-
for (auto const& [targetSlot, targetMinCount]: targetMinCounts)
127+
for (auto const& [targetSlot, targetMinCount]: targetStats.targetMinCounts)
160128
if (stackStats.totalCount(targetSlot) < targetMinCount)
161129
return false;
162130
return true;
163131
}
164132

165133
size_t targetMinCount(Slot const& _slot) const
166134
{
167-
return util::valueOrDefault(targetMinCounts, _slot, size_t{0});
135+
return util::valueOrDefault(targetStats.targetMinCounts, _slot, size_t{0});
168136
}
169137

170138
size_t targetArgsCount(Slot const& _slot) const
@@ -201,10 +169,10 @@ class OperationForwardShuffler
201169
}
202170

203171
StackStats stackStats;
204-
std::map<Slot, size_t> targetMinCounts;
205172
Stack<Callback> const& stack;
206173
std::vector<Slot> const& args;
207174
LivenessAnalysis::LivenessData const& liveOut;
175+
TargetStats const& targetStats;
208176
};
209177

210178
// If dupping an ideal slot causes a slot that will still be required to become unreachable, then dup
@@ -304,10 +272,11 @@ class OperationForwardShuffler
304272
Stack<Callback>& _stack,
305273
std::vector<Slot> const& _args,
306274
LivenessAnalysis::LivenessData const& _liveOut,
275+
TargetStats const& _targetStats,
307276
bool const _generateJunk
308277
)
309278
{
310-
Ops const ops(_stack, _args, _liveOut);
279+
Ops const ops(_stack, _args, _liveOut, _targetStats);
311280

312281
// Check if we have the required top already
313282
if (ops.argsRegionIsCorrect())
@@ -551,7 +520,7 @@ class OperationForwardShuffler
551520
}
552521

553522
// now all required slots are present in required quantity
554-
for (auto const& [targetSlot, targetSlotMinCount]: ops.targetMinCounts)
523+
for (auto const& [targetSlot, targetSlotMinCount]: ops.targetStats.targetMinCounts)
555524
yulAssert(ops.stackStats.totalCount(targetSlot) >= targetSlotMinCount);
556525

557526
auto swappableDepthRange = ranges::views::iota(0u, std::min(ReachableStackDepth + 1u, _stack.size())) | ranges::views::reverse;

0 commit comments

Comments
 (0)