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