/src/WasmEdge/include/runtime/stackmgr.h
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | //===-- wasmedge/runtime/stackmgr.h - Stack Manager definition ------------===// |
5 | | // |
6 | | // Part of the WasmEdge Project. |
7 | | // |
8 | | //===----------------------------------------------------------------------===// |
9 | | /// |
10 | | /// \file |
11 | | /// This file contains the definition of Stack Manager. |
12 | | /// |
13 | | //===----------------------------------------------------------------------===// |
14 | | #pragma once |
15 | | |
16 | | #include "ast/instruction.h" |
17 | | #include "runtime/instance/module.h" |
18 | | |
19 | | #include <optional> |
20 | | #include <vector> |
21 | | |
22 | | namespace WasmEdge { |
23 | | namespace Runtime { |
24 | | |
25 | | class StackManager { |
26 | | public: |
27 | | using Value = ValVariant; |
28 | | |
29 | | struct Handler { |
30 | | Handler(AST::InstrView::iterator TryIt, uint32_t V, |
31 | | Span<const AST::Instruction::CatchDescriptor> C) |
32 | 0 | : Try(TryIt), VPos(V), CatchClause(C) {} |
33 | | AST::InstrView::iterator Try; |
34 | | uint32_t VPos; |
35 | | Span<const AST::Instruction::CatchDescriptor> CatchClause; |
36 | | }; |
37 | | |
38 | | struct Frame { |
39 | | Frame() = delete; |
40 | | Frame(const Instance::ModuleInstance *Mod, AST::InstrView::iterator FromIt, |
41 | | uint32_t L, uint32_t A, uint32_t V, bool E) noexcept |
42 | 0 | : Module(Mod), From(FromIt), Locals(L), Arity(A), VPos(V), |
43 | 0 | NativeEntry(E) {} |
44 | | const Instance::ModuleInstance *Module; |
45 | | AST::InstrView::iterator From; |
46 | | uint32_t Locals; |
47 | | uint32_t Arity; |
48 | | uint32_t VPos; |
49 | | bool NativeEntry; |
50 | | std::vector<Handler> HandlerStack; |
51 | | }; |
52 | | |
53 | | /// Stack manager provides the stack control for Wasm execution with VALIDATED |
54 | | /// modules. All operations of instructions passed validation, therefore no |
55 | | /// unexpect operations will occur. |
56 | 0 | StackManager() noexcept { |
57 | 0 | ValueStack.reserve(2048U); |
58 | 0 | FrameStack.reserve(16U); |
59 | 0 | } |
60 | 0 | ~StackManager() = default; |
61 | | |
62 | | /// Getter for stack size. |
63 | 0 | size_t size() const noexcept { return ValueStack.size(); } |
64 | | |
65 | | /// Unsafe getter for the top entry of the stack. |
66 | 0 | Value &getTop() { return ValueStack.back(); } |
67 | | |
68 | | /// Unsafe getter for the N-th value entry from the top of the stack. |
69 | 0 | Value &getTopN(uint32_t Offset) noexcept { |
70 | 0 | assuming(0 < Offset && Offset <= ValueStack.size()); |
71 | 0 | return ValueStack[ValueStack.size() - Offset]; |
72 | 0 | } |
73 | | |
74 | | /// Unsafe getter for the top N value entries of the stack. |
75 | 0 | Span<Value> getTopSpan(uint32_t N) { |
76 | 0 | return Span<Value>(ValueStack.end() - N, N); |
77 | 0 | } |
78 | | |
79 | | /// Push a new value entry to the stack. |
80 | 0 | template <typename T> void push(T &&Val) { |
81 | 0 | ValueStack.push_back(std::forward<T>(Val)); |
82 | 0 | } Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant> const&>(WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant> const&) Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<WasmEdge::RefVariant>(WasmEdge::RefVariant&&) Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant> >(WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant>&&) Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant>&>(WasmEdge::Variant<unsigned int, int, unsigned long, long, float, double, unsigned __int128, __int128, unsigned long __vector(2), long __vector(2), unsigned int __vector(4), int __vector(4), unsigned short __vector(8), short __vector(8), unsigned char __vector(16), signed char __vector(16), float __vector(4), double __vector(2), WasmEdge::RefVariant>&) Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<WasmEdge::RefVariant&>(WasmEdge::RefVariant&) |
83 | | |
84 | | /// Push a vector of values to the stack. |
85 | 0 | void pushValVec(const std::vector<Value> &ValVec) { |
86 | 0 | ValueStack.insert(ValueStack.end(), ValVec.begin(), ValVec.end()); |
87 | 0 | } |
88 | | |
89 | | /// Unsafe pop and return the top entry. |
90 | 0 | Value pop() { |
91 | 0 | Value V = std::move(ValueStack.back()); |
92 | 0 | ValueStack.pop_back(); |
93 | 0 | return V; |
94 | 0 | } |
95 | | |
96 | | /// Unsafe pop and return the top N entries. |
97 | 0 | std::vector<Value> pop(uint32_t N) { |
98 | 0 | std::vector<Value> Vec; |
99 | 0 | Vec.reserve(N); |
100 | 0 | std::move(ValueStack.end() - N, ValueStack.end(), std::back_inserter(Vec)); |
101 | 0 | ValueStack.erase(ValueStack.end() - N, ValueStack.end()); |
102 | 0 | return Vec; |
103 | 0 | } |
104 | | |
105 | | /// Push a new frame entry to the stack. Set `IsNativeEntry` for the frames |
106 | | /// entered from the native code, which the exception handler walk never |
107 | | /// crosses; tail calls inherit the flag from the replaced frame. |
108 | | void pushFrame(const Instance::ModuleInstance *Module, |
109 | | AST::InstrView::iterator From, uint32_t LocalNum = 0, |
110 | | uint32_t Arity = 0, bool IsTailCall = false, |
111 | 0 | bool IsNativeEntry = false) noexcept { |
112 | 0 | if (!IsTailCall) { |
113 | 0 | FrameStack.emplace_back(Module, From, LocalNum, Arity, |
114 | 0 | static_cast<uint32_t>(ValueStack.size()), |
115 | 0 | IsNativeEntry); |
116 | 0 | } else { |
117 | 0 | assuming(!FrameStack.empty()); |
118 | 0 | assuming(FrameStack.back().VPos >= FrameStack.back().Locals); |
119 | 0 | assuming(FrameStack.back().VPos - FrameStack.back().Locals <= |
120 | 0 | ValueStack.size() - LocalNum); |
121 | 0 | ValueStack.erase(ValueStack.begin() + FrameStack.back().VPos - |
122 | 0 | FrameStack.back().Locals, |
123 | 0 | ValueStack.end() - LocalNum); |
124 | 0 | FrameStack.back().Module = Module; |
125 | 0 | FrameStack.back().Locals = LocalNum; |
126 | 0 | FrameStack.back().Arity = Arity; |
127 | 0 | FrameStack.back().VPos = static_cast<uint32_t>(ValueStack.size()); |
128 | 0 | FrameStack.back().HandlerStack.clear(); |
129 | 0 | } |
130 | 0 | } |
131 | | |
132 | | /// Unsafe pop top frame. |
133 | 0 | AST::InstrView::iterator popFrame() noexcept { |
134 | 0 | assuming(!FrameStack.empty()); |
135 | 0 | assuming(FrameStack.back().VPos >= FrameStack.back().Locals); |
136 | 0 | assuming(FrameStack.back().VPos - FrameStack.back().Locals <= |
137 | 0 | ValueStack.size() - FrameStack.back().Arity); |
138 | 0 | ValueStack.erase(ValueStack.begin() + FrameStack.back().VPos - |
139 | 0 | FrameStack.back().Locals, |
140 | 0 | ValueStack.end() - FrameStack.back().Arity); |
141 | 0 | auto From = FrameStack.back().From; |
142 | 0 | FrameStack.pop_back(); |
143 | 0 | return From; |
144 | 0 | } |
145 | | |
146 | | // Get all frames |
147 | 0 | Span<const Frame> getFramesSpan() const { return FrameStack; } |
148 | | |
149 | | /// Getter of the native entry flag of the top frame. |
150 | 0 | bool isTopFrameNativeEntry() const noexcept { |
151 | 0 | return !FrameStack.empty() && FrameStack.back().NativeEntry; |
152 | 0 | } |
153 | | |
154 | | /// Push handler for try-catch block. |
155 | | void |
156 | | pushHandler(AST::InstrView::iterator TryIt, uint32_t BlockParamNum, |
157 | 0 | Span<const AST::Instruction::CatchDescriptor> Catch) noexcept { |
158 | 0 | assuming(!FrameStack.empty()); |
159 | 0 | FrameStack.back().HandlerStack.emplace_back( |
160 | 0 | TryIt, static_cast<uint32_t>(ValueStack.size()) - BlockParamNum, Catch); |
161 | 0 | } |
162 | | |
163 | | /// Pop the top handler on the stack. The walk stops without popping when |
164 | | /// reaching a frame entered from the native code. |
165 | 0 | std::optional<Handler> popTopHandler(uint32_t AssocValSize) noexcept { |
166 | 0 | while (!FrameStack.empty()) { |
167 | 0 | auto &Frame = FrameStack.back(); |
168 | 0 | if (!Frame.HandlerStack.empty()) { |
169 | 0 | auto TopHandler = std::move(Frame.HandlerStack.back()); |
170 | 0 | Frame.HandlerStack.pop_back(); |
171 | 0 | assuming(TopHandler.VPos <= ValueStack.size() - AssocValSize); |
172 | 0 | ValueStack.erase(ValueStack.begin() + TopHandler.VPos, |
173 | 0 | ValueStack.end() - AssocValSize); |
174 | 0 | return TopHandler; |
175 | 0 | } |
176 | 0 | if (Frame.NativeEntry) { |
177 | 0 | break; |
178 | 0 | } |
179 | 0 | FrameStack.pop_back(); |
180 | 0 | } |
181 | 0 | return std::nullopt; |
182 | 0 | } |
183 | | |
184 | | /// Unsafe remove inactive handler. |
185 | 0 | void removeInactiveHandler(AST::InstrView::iterator PC) noexcept { |
186 | 0 | assuming(!FrameStack.empty()); |
187 | | // Pop the handlers the branch left behind. Callers must run this while the |
188 | | // inactive handlers are still on top: a handler buried under a later |
189 | | // try_table is indistinguishable from an active one here, so branchToLabel |
190 | | // cleans up before any new handler can be pushed over the stale ones. |
191 | 0 | auto &HandlerStack = FrameStack.back().HandlerStack; |
192 | 0 | while (!HandlerStack.empty()) { |
193 | 0 | auto &Handler = HandlerStack.back(); |
194 | 0 | if (PC < Handler.Try || |
195 | 0 | PC > Handler.Try + Handler.Try->getTryCatch().JumpEnd) { |
196 | 0 | HandlerStack.pop_back(); |
197 | 0 | } else { |
198 | 0 | break; |
199 | 0 | } |
200 | 0 | } |
201 | 0 | } |
202 | | |
203 | | /// Unsafe erase value stack. |
204 | 0 | void eraseValueStack(uint32_t EraseBegin, uint32_t EraseEnd) noexcept { |
205 | 0 | assuming(EraseEnd <= EraseBegin && EraseBegin <= ValueStack.size()); |
206 | 0 | ValueStack.erase(ValueStack.end() - EraseBegin, |
207 | 0 | ValueStack.end() - EraseEnd); |
208 | 0 | } |
209 | | |
210 | | // Get all Value |
211 | 0 | Span<const Value> getValueSpan() const { return ValueStack; } |
212 | | |
213 | | /// Unsafe leave top label. |
214 | | AST::InstrView::iterator |
215 | 0 | maybePopFrameOrHandler(AST::InstrView::iterator PC) noexcept { |
216 | 0 | if (FrameStack.size() > 1 && PC->isExprLast()) { |
217 | | // Noted that there's always a base frame in stack. |
218 | 0 | return popFrame(); |
219 | 0 | } |
220 | 0 | if (PC->isTryBlockLast()) { |
221 | 0 | FrameStack.back().HandlerStack.pop_back(); |
222 | 0 | } |
223 | 0 | return PC; |
224 | 0 | } |
225 | | |
226 | | /// Unsafe getter of module address. |
227 | 0 | const Instance::ModuleInstance *getModule() const noexcept { |
228 | 0 | if (unlikely(FrameStack.empty())) { |
229 | 0 | return nullptr; |
230 | 0 | } |
231 | 0 | return FrameStack.back().Module; |
232 | 0 | } |
233 | | |
234 | | /// Reset stack. |
235 | 0 | void reset() noexcept { |
236 | 0 | ValueStack.clear(); |
237 | 0 | FrameStack.clear(); |
238 | 0 | } |
239 | | |
240 | | private: |
241 | | /// \name Data of the stack manager. |
242 | | /// @{ |
243 | | std::vector<Value> ValueStack; |
244 | | std::vector<Frame> FrameStack; |
245 | | /// @} |
246 | | }; |
247 | | |
248 | | } // namespace Runtime |
249 | | } // namespace WasmEdge |