/src/solidity/libyul/backends/evm/ssa/Stack.h
Line | Count | Source |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | |
19 | | #pragma once |
20 | | |
21 | | #include <libyul/backends/evm/ssa/ShuffleTrace.h> |
22 | | #include <libyul/backends/evm/ssa/StackSlot.h> |
23 | | |
24 | | #include <range/v3/algorithm/find.hpp> |
25 | | #include <range/v3/view/reverse.hpp> |
26 | | |
27 | | #include <cstddef> |
28 | | |
29 | | namespace solidity::yul::ssa |
30 | | { |
31 | | |
32 | | /// A view over a `StackData` mimicking EVM stack semantics. When constructed with a trace, every stack |
33 | | /// manipulation is recorded as a `ShuffleOp`; an untraced view mutates the data silently. |
34 | | class Stack |
35 | | { |
36 | | public: |
37 | | using Slot = StackSlot; |
38 | | using Data = StackData; |
39 | | using Depth = StackDepth; |
40 | | using Offset = StackOffset; |
41 | | |
42 | | explicit Stack(Data& _data, ShuffleTrace* _trace = nullptr): |
43 | 0 | m_data(&_data), |
44 | 0 | m_trace(_trace) |
45 | 0 | {} |
46 | | |
47 | | Slot const& top() const |
48 | 0 | { |
49 | 0 | yulAssert(!m_data->empty()); |
50 | 0 | return m_data->back(); |
51 | 0 | } |
52 | | |
53 | 0 | void swap(Depth const& _depth) { swap(depthToOffset(_depth)); } |
54 | | void swap(Offset const& _offset) |
55 | 0 | { |
56 | 0 | yulAssert(isValidSwapTarget(_offset), "Stack too deep"); |
57 | 0 | std::swap((*m_data)[_offset.value], m_data->back()); |
58 | 0 | if (m_trace) |
59 | 0 | m_trace->push_back(ShuffleOp::swap(offsetToDepth(_offset))); |
60 | 0 | } |
61 | | |
62 | | void pop() |
63 | 0 | { |
64 | 0 | yulAssert(!m_data->empty()); |
65 | 0 | m_data->pop_back(); |
66 | 0 | if (m_trace) |
67 | 0 | m_trace->push_back(ShuffleOp::pop()); |
68 | 0 | } |
69 | | |
70 | | void push(Slot const& _slot) |
71 | 0 | { |
72 | 0 | yulAssert(!_slot.isFunctionReturnLabel(), "Cannot push function return label"); |
73 | 0 | m_data->emplace_back(_slot); |
74 | 0 | if (m_trace) |
75 | 0 | m_trace->push_back( |
76 | | // a pushed non-literal value can only be a spill reload |
77 | 0 | _slot.isValue() && !_slot.isLiteralValue() ? ShuffleOp::load(_slot) : ShuffleOp::push(_slot) |
78 | 0 | ); |
79 | 0 | } |
80 | | |
81 | 0 | void dup(Depth const& _depth) { dup(depthToOffset(_depth)); } |
82 | | void dup(Offset const& _offset) |
83 | 0 | { |
84 | 0 | auto const depth = offsetToDepth(_offset); |
85 | 0 | yulAssert(dupReachable(depth), "Stack too deep"); |
86 | 0 | auto const slot = (*m_data)[_offset.value]; |
87 | 0 | yulAssert(!slot.isFunctionReturnLabel(), "Cannot dup function return label"); |
88 | 0 | m_data->push_back(slot); |
89 | 0 | if (m_trace) |
90 | 0 | m_trace->push_back(ShuffleOp::dup(Depth{depth.value + 1})); |
91 | 0 | } |
92 | | |
93 | 0 | bool dupReachable(Offset const& _offset) const noexcept { return dupReachable(offsetToDepth(_offset)); } |
94 | 0 | bool dupReachable(Depth const& _depth) const noexcept { return _depth < size() && _depth.value + 1 <= reachableStackDepth; } |
95 | 0 | bool isValidSwapTarget(Offset const& _offset) const noexcept { return isValidSwapTarget(offsetToDepth(_offset)); } |
96 | 0 | bool isValidSwapTarget(Depth const& _depth) const noexcept { return _depth < size() && 1 <= _depth.value && _depth.value <= reachableStackDepth; } |
97 | 0 | bool isBeyondSwapRange(Offset const& _offset) const noexcept { return isBeyondSwapRange(offsetToDepth(_offset)); } |
98 | 0 | bool isBeyondSwapRange(Depth const& _depth) const noexcept { return _depth > reachableStackDepth; } |
99 | | |
100 | 0 | void declareJunk(Offset const& _offset) { (*m_data)[_offset.value] = Slot::makeJunk(); } |
101 | 0 | void declareJunk(Depth const& _depth) { declareJunk(depthToOffset(_depth)); } |
102 | | |
103 | 0 | Slot const& slot(Depth const& _depth) const { return (*m_data)[depthToOffset(_depth).value]; } |
104 | 0 | Slot const& slot(Offset const& _offset) const { return slot(offsetToDepth(_offset)); } |
105 | 0 | bool empty() const noexcept { return size() == 0; } |
106 | 0 | size_t size() const noexcept { return m_data->size(); } |
107 | | |
108 | | std::optional<Depth> findSlotDepth(Slot const& _value) const |
109 | 0 | { |
110 | 0 | auto rview = *this | ranges::views::reverse; |
111 | 0 | auto it = ranges::find(rview, _value); |
112 | |
|
113 | 0 | if (it == ranges::end(rview)) |
114 | 0 | return std::nullopt; |
115 | | |
116 | 0 | return Depth{static_cast<size_t>(std::distance(ranges::begin(rview), it))}; |
117 | 0 | } |
118 | | |
119 | 0 | Slot const& operator[](Offset const& _index) const noexcept { return (*m_data)[_index.value]; } |
120 | 0 | Data::const_iterator begin() const { return m_data->begin(); } |
121 | 0 | Data::const_iterator end() const { return m_data->end(); } |
122 | | |
123 | | Data const& data() const |
124 | 0 | { |
125 | 0 | return *m_data; |
126 | 0 | } |
127 | | |
128 | | /// index scheme conversion offset -> depth |
129 | | Depth offsetToDepth(Offset const& _offset) const |
130 | 0 | { |
131 | 0 | yulAssert(_offset < size(), "Offset out of range"); |
132 | 0 | return Depth{size() - _offset.value - 1}; |
133 | 0 | } |
134 | | /// index scheme conversion depth -> offset |
135 | | Offset depthToOffset(Depth const& _depth) const |
136 | 0 | { |
137 | | yulAssert(_depth < size(), "Depth out of range"); |
138 | 0 | return Offset{size() - _depth.value - 1}; |
139 | 0 | } |
140 | | |
141 | | private: |
142 | | Data* m_data; |
143 | | ShuffleTrace* m_trace; |
144 | | }; |
145 | | |
146 | | } |