Coverage Report

Created: 2026-07-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/include/runtime/instance/memory.h
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
//===-- wasmedge/runtime/instance/memory.h - Memory Instance definition ---===//
5
//
6
// Part of the WasmEdge Project.
7
//
8
//===----------------------------------------------------------------------===//
9
///
10
/// \file
11
/// This file contains the memory instance definition in store manager.
12
///
13
//===----------------------------------------------------------------------===//
14
#pragma once
15
16
#include "ast/type.h"
17
#include "common/errcode.h"
18
#include "common/errinfo.h"
19
#include "common/int128.h"
20
#include "common/spdlog.h"
21
#include "common/types.h"
22
#include "system/allocator.h"
23
24
#include <algorithm>
25
#include <condition_variable>
26
#include <cstdint>
27
#include <cstring>
28
#include <fstream>
29
#include <memory>
30
#include <mutex>
31
#include <set>
32
#include <type_traits>
33
#include <unordered_map>
34
#include <utility>
35
36
namespace WasmEdge {
37
namespace Runtime {
38
namespace Instance {
39
40
class MemoryInstance {
41
42
public:
43
  static inline constexpr const uint64_t kPageSize = UINT64_C(65536);
44
  static inline constexpr const uint64_t kPageLimit32 = UINT64_C(0x10000);
45
  static inline constexpr const uint64_t kPageLimit64 =
46
      UINT64_C(0x1000000000000);
47
  MemoryInstance() = delete;
48
  MemoryInstance(MemoryInstance &&Inst) noexcept
49
      : MemType(Inst.MemType), DataPtr(Inst.DataPtr), PageLimit(Inst.PageLimit),
50
0
        LivePageCount(Inst.LivePageCount) {
51
0
    Inst.DataPtr = nullptr;
52
0
  }
53
  MemoryInstance(const AST::MemoryType &MType,
54
                 uint64_t PageLim = kPageLimit64) noexcept
55
0
      : MemType(MType), PageLimit(PageLim),
56
0
        LivePageCount(MType.getLimit().getMin()) {
57
0
    using namespace std::literals;
58
0
    if (MemType.getLimit().is32() && PageLimit > kPageLimit32) {
59
0
      if (PageLimit != kPageLimit64) {
60
        // Only log error when the page limit is not the default value of 64-bit
61
        // memory.
62
0
        spdlog::error("Memory Instance: Limited {} page larger than the 32-bit "
63
0
                      "page upper bound {}."sv,
64
0
                      PageLimit, kPageLimit32);
65
0
      }
66
0
      PageLimit = kPageLimit32;
67
0
    }
68
0
    if (MemType.getLimit().getMin() > PageLimit) {
69
0
      spdlog::error("Memory Instance: Limited {} page in configuration."sv,
70
0
                    PageLimit);
71
0
      setLivePageCount(PageLimit);
72
0
    }
73
0
    DataPtr = Allocator::allocate(MemType.getLimit().getMin());
74
0
    if (DataPtr == nullptr) {
75
0
      spdlog::error("Memory Instance: Unable to find usable memory address."sv);
76
0
      setLivePageCount(0U);
77
0
      return;
78
0
    }
79
0
  }
80
0
  ~MemoryInstance() noexcept {
81
0
    Allocator::release(DataPtr, MemType.getLimit().getMin());
82
0
  }
83
84
0
  bool isShared() const noexcept { return MemType.getLimit().isShared(); }
85
86
  /// Get page size of memory.data
87
0
  uint64_t getPageSize() const noexcept {
88
    // The memory page size is bound to the limit in the memory type.
89
0
    return MemType.getLimit().getMin();
90
0
  }
91
92
  /// Get a stable pointer to the live page-count field for compiled code.
93
0
  const uint64_t *getPageSizePtr() const noexcept { return &LivePageCount; }
94
95
  /// Get the stable reference to the live data buffer for compiled code.
96
0
  uint8_t *const &getDataPtr() const noexcept { return DataPtr; }
97
0
  uint8_t *&getDataPtr() noexcept { return DataPtr; }
98
99
  /// Get memory size of memory.data
100
0
  uint64_t getSize() const noexcept {
101
    // The memory page size is bound to the limit in the memory type.
102
0
    return MemType.getLimit().getMin() * kPageSize;
103
0
  }
104
105
  /// Getter for memory type.
106
0
  const AST::MemoryType &getMemoryType() const noexcept { return MemType; }
107
108
  /// Check access size is valid.
109
  bool checkAccessBound(const uint64_t Offset,
110
0
                        const uint64_t Length) const noexcept {
111
    // Due to applying the Memory64 proposal, we should avoid the overflow issue
112
    // of the following code:
113
    //   return Offset + Length <= Limit;
114
0
    const uint64_t Limit = MemType.getLimit().getMin() * kPageSize;
115
0
    return std::numeric_limits<uint64_t>::max() - Offset >= Length &&
116
0
           Offset + Length <= Limit;
117
0
  }
118
119
  /// Grow page
120
0
  bool growPage(const uint64_t Count) noexcept {
121
0
    using namespace std::literals;
122
0
    if (Count == 0) {
123
0
      return true;
124
0
    }
125
0
    uint64_t MaxPageCaped =
126
0
        MemType.getLimit().is32() ? kPageLimit32 : kPageLimit64;
127
0
    const uint64_t Min = MemType.getLimit().getMin();
128
0
    assuming(MaxPageCaped >= Min);
129
0
    if (MemType.getLimit().hasMax()) {
130
0
      const uint64_t Max = MemType.getLimit().getMax();
131
0
      MaxPageCaped = std::min(Max, MaxPageCaped);
132
0
    }
133
0
    if (Count > MaxPageCaped - Min) {
134
0
      return false;
135
0
    }
136
0
    assuming(PageLimit >= Min);
137
0
    if (Count > PageLimit - Min) {
138
0
      spdlog::error("Memory Instance: Memory grow page failed, exceeded "
139
0
                    "limited {} page size in configuration."sv,
140
0
                    PageLimit);
141
0
      return false;
142
0
    }
143
0
    if (auto NewPtr = Allocator::resize(DataPtr, Min, Min + Count);
144
0
        NewPtr == nullptr) {
145
0
      return false;
146
0
    } else {
147
0
      DataPtr = NewPtr;
148
0
    }
149
0
    setLivePageCount(Min + Count);
150
0
    return true;
151
0
  }
152
153
  /// Get slice of Data[Offset : Offset + Length - 1]
154
  Expect<Span<Byte>> getBytes(const uint64_t Offset,
155
0
                              const uint64_t Length) const noexcept {
156
    // Check the memory boundary.
157
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
158
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
159
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
160
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
161
0
    }
162
0
    return Span<Byte>(&DataPtr[Offset], Length);
163
0
  }
164
165
  /// Replace the bytes of Data[Offset :] by Slice[Start : Start + Length - 1]
166
  Expect<void> setBytes(Span<const Byte> Slice, const uint64_t Offset,
167
0
                        const uint64_t Start, const uint64_t Length) noexcept {
168
    // Check the memory boundary.
169
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
170
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
171
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
172
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
173
0
    }
174
175
    // Check the input data validation.
176
0
    if (unlikely(std::numeric_limits<uint64_t>::max() - Start < Length ||
177
0
                 Start + Length > static_cast<uint64_t>(Slice.size()))) {
178
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
179
0
      spdlog::error(ErrInfo::InfoBoundary(Start, Length,
180
0
                                          static_cast<uint64_t>(Slice.size())));
181
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
182
0
    }
183
184
    // Copy the data. The slice may be from the same memory instance, so use
185
    // memmove semantics for the possible overlapping case.
186
0
    if (likely(Length > 0)) {
187
0
      std::memmove(DataPtr + Offset, Slice.data() + Start, Length);
188
0
    }
189
0
    return {};
190
0
  }
191
192
  /// Fill the bytes of Data[Offset : Offset + Length - 1] by Val.
193
  Expect<void> fillBytes(const uint8_t Val, const uint64_t Offset,
194
0
                         const uint64_t Length) noexcept {
195
    // Check the memory boundary.
196
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
197
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
198
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
199
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
200
0
    }
201
202
    // Copy the data.
203
0
    if (likely(Length > 0)) {
204
0
      std::fill(DataPtr + Offset, DataPtr + Offset + Length, Val);
205
0
    }
206
0
    return {};
207
0
  }
208
209
  /// Get an uint8 array from Data[Offset : Offset + Length - 1]
210
  Expect<void> getArray(uint8_t *Arr, const uint64_t Offset,
211
                        const uint64_t Length,
212
0
                        const bool IsReverse = false) const noexcept {
213
0
    // Check the memory boundary.
214
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
215
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
216
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
217
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
218
0
    }
219
0
    if (likely(Length > 0)) {
220
0
      // Copy the data.
221
0
      if (IsReverse) {
222
0
        std::reverse_copy(DataPtr + Offset, DataPtr + Offset + Length, Arr);
223
0
      } else {
224
0
        std::copy(DataPtr + Offset, DataPtr + Offset + Length, Arr);
225
0
      }
226
0
    }
227
0
    return {};
228
0
  }
229
230
  /// Replace Data[Offset : Offset + Length - 1] to an uint8 array
231
  Expect<void> setArray(const uint8_t *Arr, const uint64_t Offset,
232
                        const uint64_t Length,
233
0
                        const bool IsReverse = false) noexcept {
234
0
    // Check the memory boundary.
235
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
236
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
237
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
238
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
239
0
    }
240
0
    if (likely(Length > 0)) {
241
0
      // Copy the data.
242
0
      if (IsReverse) {
243
0
        std::reverse_copy(Arr, Arr + Length, DataPtr + Offset);
244
0
      } else {
245
0
        std::copy(Arr, Arr + Length, DataPtr + Offset);
246
0
      }
247
0
    }
248
0
    return {};
249
0
  }
250
251
  /// Get pointer to specific offset of memory or null.
252
  template <typename T>
253
  typename std::enable_if_t<std::is_pointer_v<T>, T>
254
  getPointerOrNull(const uint64_t Offset) const noexcept {
255
    using Type = std::remove_pointer_t<T>;
256
    if (Offset == 0 || unlikely(!checkAccessBound(Offset, sizeof(Type)))) {
257
      return nullptr;
258
    }
259
    return reinterpret_cast<T>(&DataPtr[Offset]);
260
  }
261
262
  /// Get pointer to specific offset of memory.
263
  template <typename T>
264
  typename std::enable_if_t<std::is_pointer_v<T>, T>
265
0
  getPointer(const uint64_t Offset) const noexcept {
266
0
    using Type = std::remove_pointer_t<T>;
267
0
    if (unlikely(!checkAccessBound(Offset, sizeof(Type)))) {
268
0
      return nullptr;
269
0
    }
270
0
    return reinterpret_cast<T>(&DataPtr[Offset]);
271
0
  }
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicImEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicIjEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicIiEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicIlEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicIhEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPNSt3__16atomicItEEEENS4_9enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP17__wasi_addrinfo_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP17__wasi_sockaddr_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPjEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES7_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPmEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES7_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP15__wasi_fdstat_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP17__wasi_filestat_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP16__wasi_prestat_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPiEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES7_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP16__wasi_address_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIP16__wasi_roflags_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPK17__wasi_addrinfo_tEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES9_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPKjEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES8_E4typeEm
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPtEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES7_E4typeEm
272
273
  /// Get array of object with count at specific offset of memory.
274
  template <typename T>
275
0
  Span<T> getSpan(const uint64_t Offset, const uint64_t Count) const noexcept {
276
0
    uint64_t Size;
277
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
278
    uint128_t Num =
279
        static_cast<uint128_t>(sizeof(T)) * static_cast<uint128_t>(Count);
280
    if ((Num >> 64).high() != 0) {
281
      return Span<T>();
282
    }
283
    Size = Num.low();
284
#else
285
0
    if (unlikely(__builtin_mul_overflow(static_cast<uint64_t>(sizeof(T)), Count,
286
0
                                        &Size))) {
287
0
      return Span<T>();
288
0
    }
289
0
#endif
290
0
    if (unlikely(!checkAccessBound(Offset, Size))) {
291
0
      return Span<T>();
292
0
    }
293
0
    return Span<T>(reinterpret_cast<T *>(&DataPtr[Offset]), Count);
294
0
  }
Unexecuted instantiation: cxx20::span<unsigned char, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<unsigned char>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<unsigned char const, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<unsigned char const>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<unsigned int, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<unsigned int>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<__wasi_iovec_t, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<__wasi_iovec_t>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<__wasi_ciovec_t, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<__wasi_ciovec_t>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<char, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<char>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<__wasi_subscription_t const, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<__wasi_subscription_t const>(unsigned long, unsigned long) const
Unexecuted instantiation: cxx20::span<__wasi_event_t, 18446744073709551615ul> WasmEdge::Runtime::Instance::MemoryInstance::getSpan<__wasi_event_t>(unsigned long, unsigned long) const
295
296
  /// Get array of object at specific offset of memory.
297
  std::string_view getStringView(const uint64_t Offset,
298
0
                                 const uint64_t Size) const noexcept {
299
0
    if (unlikely(!checkAccessBound(Offset, Size))) {
300
0
      return {};
301
0
    }
302
0
    return {reinterpret_cast<const char *>(&DataPtr[Offset]), Size};
303
0
  }
304
305
  /// Template for loading bytes and converting them to a value.
306
  ///
307
  /// Load bytes of the specified length and construct a value.
308
  /// Only output values of int32, uint32, int64, uint64, float, and double are
309
  /// allowed.
310
  ///
311
  /// \param Value the constructed output value.
312
  /// \param Offset the start offset in data array.
313
  ///
314
  /// \returns void on success, ErrCode on failure.
315
  template <typename T, uint32_t Length = sizeof(T)>
316
  typename std::enable_if_t<IsWasmNumV<T>, Expect<void>>
317
0
  loadValue(T &Value, const uint64_t Offset) const noexcept {
318
    // Check the data boundary.
319
0
    static_assert(Length <= sizeof(T));
320
    // Check the memory boundary.
321
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
322
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
323
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
324
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
325
0
    }
326
    // Load the data to the value.
327
0
    if (likely(Length > 0)) {
328
0
      if constexpr (std::is_floating_point_v<T>) {
329
        // Floating case. Do the memory copy.
330
0
        EndianValue<T> LoadValue;
331
0
        std::memcpy(&LoadValue.raw(), &DataPtr[Offset], Length);
332
0
        Value = LoadValue.le();
333
0
      } else {
334
0
        if constexpr (sizeof(T) > 8) {
335
0
          assuming(sizeof(T) == 16);
336
0
          EndianValue<T> LoadValue = 0U;
337
0
          std::memcpy(&LoadValue.raw(), &DataPtr[Offset], Length);
338
0
          Value = LoadValue.le();
339
0
        } else {
340
          // Integer case. Extend to the result type.
341
0
          EndianValue<uint64_t> LoadVal = 0;
342
0
          std::memcpy(&LoadVal.raw(), &DataPtr[Offset], Length);
343
0
          uint64_t Val = LoadVal.le();
344
0
          if (std::is_signed_v<T> && (Val >> (Length * 8 - 1))) {
345
            // Signed extension.
346
0
            for (unsigned int I = Length; I < 8; I++) {
347
0
              Val |= 0xFFULL << (I * 8);
348
0
            }
349
0
          }
350
0
          Value = static_cast<T>(Val);
351
0
        }
352
0
      }
353
0
    }
354
0
    return {};
355
0
  }
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned int, 4u>(unsigned int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned long, 8u>(unsigned long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<float>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<float, 4u>(float&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<double>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<double, 8u>(double&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<int, 1u>(int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned int, 1u>(unsigned int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<int, 2u>(int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned int, 2u>(unsigned int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<long, 1u>(long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned long, 1u>(unsigned long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<long, 2u>(long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned long, 2u>(unsigned long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<long, 4u>(long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned long, 4u>(unsigned long&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned __int128>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned __int128, 16u>(unsigned __int128&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned __int128>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned __int128, 4u>(unsigned __int128&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<unsigned __int128>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<unsigned __int128, 8u>(unsigned __int128&, unsigned long) const
356
357
  /// Template for storing a value as bytes.
358
  ///
359
  /// Store the value using the specified byte length.
360
  /// Only input values of uint32, uint64, float, and double are allowed.
361
  ///
362
  /// \param Value the value to store into the data array.
363
  /// \param Offset the start offset in data array.
364
  ///
365
  /// \returns void on success, ErrCode on failure.
366
  template <typename T, uint32_t Length = sizeof(T)>
367
  typename std::enable_if_t<IsWasmNativeNumV<T>, Expect<void>>
368
0
  storeValue(const T &Value, const uint64_t Offset) noexcept {
369
    // Check the data boundary.
370
0
    static_assert(Length <= sizeof(T));
371
    // Check the memory boundary.
372
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
373
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
374
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
375
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
376
0
    }
377
    // Copy the value to memory.
378
0
    if (likely(Length > 0)) {
379
0
      T StoreValue = EndianValue<T>(Value).le();
380
0
      std::memcpy(&DataPtr[Offset], &StoreValue, Length);
381
0
    }
382
0
    return {};
383
0
  }
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int, 4u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned long, 8u>(unsigned long const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<float>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<float, 4u>(float const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<double>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<double, 8u>(double const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int, 1u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int, 2u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned long, 1u>(unsigned long const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned long, 2u>(unsigned long const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned long, 4u>(unsigned long const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned __int128>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned __int128, 16u>(unsigned __int128 const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int const>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int const, 1u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int const>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int const, 2u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int const>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int const, 4u>(unsigned int const&, unsigned long)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned long const>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned long const, 8u>(unsigned long const&, unsigned long)
384
385
  /// Waiter support for atomic wait/notify across threads.
386
  struct Waiter {
387
    std::mutex Mutex;
388
    std::condition_variable Cond;
389
    bool Notified = false;
390
0
    Waiter() noexcept = default;
391
  };
392
393
0
  std::mutex &getWaiterMapMutex() noexcept { return WaiterMapMutex; }
394
395
0
  std::unordered_multimap<uint64_t, Waiter> &getWaiterMap() noexcept {
396
0
    return WaiterMap;
397
0
  }
398
399
  /// Wake all waiters on this memory instance (used by Executor::stop()).
400
0
  void notifyAllWaiters() noexcept {
401
0
    std::unique_lock<std::mutex> Locker(WaiterMapMutex);
402
0
    for (auto &[Addr, W] : WaiterMap) {
403
      // Lock the Waiter mutex and notify while holding it. This pairs with
404
      // the wait loop which checks StopToken under this same mutex before
405
      // entering Cond.wait(). The lock ensures we either:
406
      // (a) block until the waiter enters Cond.wait() — then wake it, or
407
      // (b) run before the waiter checks StopToken — it will see it set.
408
0
      std::unique_lock<std::mutex> WaiterLocker(W.Mutex);
409
0
      W.Cond.notify_all();
410
0
    }
411
0
  }
412
413
private:
414
  /// Update the page count in the limit and its live mirror synchronously.
415
0
  void setLivePageCount(const uint64_t Count) noexcept {
416
0
    MemType.getLimit().setMin(Count);
417
0
    LivePageCount = Count;
418
0
  }
419
420
  /// \name Data of memory instance.
421
  /// @{
422
  AST::MemoryType MemType;
423
  uint8_t *DataPtr = nullptr;
424
  uint64_t PageLimit;
425
  uint64_t LivePageCount;
426
  std::mutex WaiterMapMutex;
427
  std::unordered_multimap<uint64_t, Waiter> WaiterMap;
428
  /// @}
429
};
430
431
} // namespace Instance
432
} // namespace Runtime
433
} // namespace WasmEdge