Coverage Report

Created: 2025-07-01 06:18

/src/WasmEdge/include/runtime/stackmgr.h
Line
Count
Source (jump to first uncovered line)
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: 2019-2024 Second State INC
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) noexcept
42
0
        : Module(Mod), From(FromIt), Locals(L), Arity(A), VPos(V) {}
43
    const Instance::ModuleInstance *Module;
44
    AST::InstrView::iterator From;
45
    uint32_t Locals;
46
    uint32_t Arity;
47
    uint32_t VPos;
48
    std::vector<Handler> HandlerStack;
49
  };
50
51
  /// Stack manager provides the stack control for Wasm execution with VALIDATED
52
  /// modules. All operations of instructions passed validation, therefore no
53
  /// unexpect operations will occur.
54
0
  StackManager() noexcept {
55
0
    ValueStack.reserve(2048U);
56
0
    FrameStack.reserve(16U);
57
0
  }
58
0
  ~StackManager() = default;
59
60
  /// Getter of stack size.
61
0
  size_t size() const noexcept { return ValueStack.size(); }
62
63
  /// Unsafe getter of top entry of stack.
64
0
  Value &getTop() { return ValueStack.back(); }
65
66
  /// Unsafe getter of top N-th value entry of stack.
67
0
  Value &getTopN(uint32_t Offset) noexcept {
68
0
    assuming(0 < Offset && Offset <= ValueStack.size());
69
0
    return ValueStack[ValueStack.size() - Offset];
70
0
  }
71
72
  /// Unsafe getter of top N value entries of stack.
73
0
  Span<Value> getTopSpan(uint32_t N) {
74
0
    return Span<Value>(ValueStack.end() - N, N);
75
0
  }
76
77
  /// Push a new value entry to stack.
78
0
  template <typename T> void push(T &&Val) {
79
0
    ValueStack.push_back(std::forward<T>(Val));
80
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&)
Unexecuted instantiation: void WasmEdge::Runtime::StackManager::push<unsigned int>(unsigned int&&)
81
82
  /// Push a vector of value to stack
83
0
  void pushValVec(const std::vector<Value> &ValVec) {
84
0
    ValueStack.insert(ValueStack.end(), ValVec.begin(), ValVec.end());
85
0
  }
86
87
  /// Unsafe pop and return the top entry.
88
0
  Value pop() {
89
0
    Value V = std::move(ValueStack.back());
90
0
    ValueStack.pop_back();
91
0
    return V;
92
0
  }
93
94
  /// Unsafe pop and return the top N entries.
95
0
  std::vector<Value> pop(uint32_t N) {
96
0
    std::vector<Value> Vec;
97
0
    Vec.reserve(N);
98
0
    std::move(ValueStack.end() - N, ValueStack.end(), std::back_inserter(Vec));
99
0
    ValueStack.erase(ValueStack.end() - N, ValueStack.end());
100
0
    return Vec;
101
0
  }
102
103
  /// Push a new frame entry to stack.
104
  void pushFrame(const Instance::ModuleInstance *Module,
105
                 AST::InstrView::iterator From, uint32_t LocalNum = 0,
106
0
                 uint32_t Arity = 0, bool IsTailCall = false) noexcept {
107
0
    if (!IsTailCall) {
108
0
      FrameStack.emplace_back(Module, From, LocalNum, Arity,
109
0
                              static_cast<uint32_t>(ValueStack.size()));
110
0
    } else {
111
0
      assuming(!FrameStack.empty());
112
0
      assuming(FrameStack.back().VPos >= FrameStack.back().Locals);
113
0
      assuming(FrameStack.back().VPos - FrameStack.back().Locals <=
114
0
               ValueStack.size() - LocalNum);
115
0
      ValueStack.erase(ValueStack.begin() + FrameStack.back().VPos -
116
0
                           FrameStack.back().Locals,
117
0
                       ValueStack.end() - LocalNum);
118
0
      FrameStack.back().Module = Module;
119
0
      FrameStack.back().Locals = LocalNum;
120
0
      FrameStack.back().Arity = Arity;
121
0
      FrameStack.back().VPos = static_cast<uint32_t>(ValueStack.size());
122
0
      FrameStack.back().HandlerStack.clear();
123
0
    }
124
0
  }
125
126
  /// Unsafe pop top frame.
127
0
  AST::InstrView::iterator popFrame() noexcept {
128
0
    assuming(!FrameStack.empty());
129
0
    assuming(FrameStack.back().VPos >= FrameStack.back().Locals);
130
0
    assuming(FrameStack.back().VPos - FrameStack.back().Locals <=
131
0
             ValueStack.size() - FrameStack.back().Arity);
132
0
    ValueStack.erase(ValueStack.begin() + FrameStack.back().VPos -
133
0
                         FrameStack.back().Locals,
134
0
                     ValueStack.end() - FrameStack.back().Arity);
135
0
    auto From = FrameStack.back().From;
136
0
    FrameStack.pop_back();
137
0
    return From;
138
0
  }
139
140
  // Get all frames
141
0
  Span<const Frame> getFramesSpan() const { return FrameStack; }
142
143
  /// Push handler for try-catch block.
144
  void
145
  pushHandler(AST::InstrView::iterator TryIt, uint32_t BlockParamNum,
146
0
              Span<const AST::Instruction::CatchDescriptor> Catch) noexcept {
147
0
    assuming(!FrameStack.empty());
148
0
    FrameStack.back().HandlerStack.emplace_back(
149
0
        TryIt, static_cast<uint32_t>(ValueStack.size()) - BlockParamNum, Catch);
150
0
  }
151
152
  /// Pop the top handler on the stack.
153
0
  std::optional<Handler> popTopHandler(uint32_t AssocValSize) noexcept {
154
0
    while (!FrameStack.empty()) {
155
0
      auto &Frame = FrameStack.back();
156
0
      if (!Frame.HandlerStack.empty()) {
157
0
        auto TopHandler = std::move(Frame.HandlerStack.back());
158
0
        Frame.HandlerStack.pop_back();
159
0
        assuming(TopHandler.VPos <= ValueStack.size() - AssocValSize);
160
0
        ValueStack.erase(ValueStack.begin() + TopHandler.VPos,
161
0
                         ValueStack.end() - AssocValSize);
162
0
        return TopHandler;
163
0
      }
164
0
      FrameStack.pop_back();
165
0
    }
166
0
    return std::nullopt;
167
0
  }
168
169
  /// Unsafe remove inactive handler.
170
0
  void removeInactiveHandler(AST::InstrView::iterator PC) noexcept {
171
0
    assuming(!FrameStack.empty());
172
    // First pop the inactive handlers. Br instructions may cause the handlers
173
    // in current frame becomes inactive.
174
0
    auto &HandlerStack = FrameStack.back().HandlerStack;
175
0
    while (!HandlerStack.empty()) {
176
0
      auto &Handler = HandlerStack.back();
177
0
      if (PC < Handler.Try ||
178
0
          PC > Handler.Try + Handler.Try->getTryCatch().JumpEnd) {
179
0
        HandlerStack.pop_back();
180
0
      } else {
181
0
        break;
182
0
      }
183
0
    }
184
0
  }
185
186
  /// Unsafe erase value stack.
187
0
  void eraseValueStack(uint32_t EraseBegin, uint32_t EraseEnd) noexcept {
188
0
    assuming(EraseEnd <= EraseBegin && EraseBegin <= ValueStack.size());
189
0
    ValueStack.erase(ValueStack.end() - EraseBegin,
190
0
                     ValueStack.end() - EraseEnd);
191
0
  }
192
193
  // Get all Value
194
0
  Span<const Value> getValueSpan() const { return ValueStack; }
195
196
  /// Unsafe leave top label.
197
  AST::InstrView::iterator
198
0
  maybePopFrameOrHandler(AST::InstrView::iterator PC) noexcept {
199
0
    if (FrameStack.size() > 1 && PC->isExprLast()) {
200
      // Noted that there's always a base frame in stack.
201
0
      return popFrame();
202
0
    }
203
0
    if (PC->isTryBlockLast()) {
204
0
      FrameStack.back().HandlerStack.pop_back();
205
0
    }
206
0
    return PC;
207
0
  }
208
209
  /// Unsafe getter of module address.
210
0
  const Instance::ModuleInstance *getModule() const noexcept {
211
0
    if (unlikely(FrameStack.empty())) {
212
0
      return nullptr;
213
0
    }
214
0
    return FrameStack.back().Module;
215
0
  }
216
217
  /// Reset stack.
218
0
  void reset() noexcept {
219
0
    ValueStack.clear();
220
0
    FrameStack.clear();
221
0
  }
222
223
private:
224
  /// \name Data of stack manager.
225
  /// @{
226
  std::vector<Value> ValueStack;
227
  std::vector<Frame> FrameStack;
228
  /// @}
229
};
230
231
} // namespace Runtime
232
} // namespace WasmEdge