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
1820namespace 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-
3623template <StackManipulationCallbackConcept Callback, size_t ReachableStackDepth=16 >
3724class 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
5745private:
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