Coverage Report

Created: 2026-11-12 06:35

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 the page limit from the configuration.
93
0
  uint64_t getPageLimit() const noexcept { return PageLimit; }
94
95
  /// Get a stable pointer to the live page-count field for compiled code.
96
0
  const uint64_t *getPageSizePtr() const noexcept { return &LivePageCount; }
97
98
  /// Get the stable reference to the live data buffer for compiled code.
99
0
  uint8_t *const &getDataPtr() const noexcept { return DataPtr; }
100
0
  uint8_t *&getDataPtr() noexcept { return DataPtr; }
101
102
  /// Get memory size of memory.data
103
0
  uint64_t getSize() const noexcept {
104
    // The memory page size is bound to the limit in the memory type.
105
0
    return MemType.getLimit().getMin() * kPageSize;
106
0
  }
107
108
  /// Getter for memory type.
109
0
  const AST::MemoryType &getMemoryType() const noexcept { return MemType; }
110
111
  /// Check access size is valid.
112
  bool checkAccessBound(const uint64_t Offset,
113
0
                        const uint64_t Length) const noexcept {
114
    // Due to applying the Memory64 proposal, we should avoid the overflow issue
115
    // of the following code:
116
    //   return Offset + Length <= Limit;
117
0
    const uint64_t Limit = MemType.getLimit().getMin() * kPageSize;
118
0
    return std::numeric_limits<uint64_t>::max() - Offset >= Length &&
119
0
           Offset + Length <= Limit;
120
0
  }
121
122
  /// Grow page
123
0
  bool growPage(const uint64_t Count) noexcept {
124
0
    using namespace std::literals;
125
0
    if (Count == 0) {
126
0
      return true;
127
0
    }
128
0
    uint64_t MaxPageCaped =
129
0
        MemType.getLimit().is32() ? kPageLimit32 : kPageLimit64;
130
0
    const uint64_t Min = MemType.getLimit().getMin();
131
0
    assuming(MaxPageCaped >= Min);
132
0
    if (MemType.getLimit().hasMax()) {
133
0
      const uint64_t Max = MemType.getLimit().getMax();
134
0
      MaxPageCaped = std::min(Max, MaxPageCaped);
135
0
    }
136
0
    if (Count > MaxPageCaped - Min) {
137
0
      return false;
138
0
    }
139
0
    assuming(PageLimit >= Min);
140
0
    if (Count > PageLimit - Min) {
141
0
      spdlog::error("Memory Instance: Memory grow page failed, exceeded "
142
0
                    "limited {} page size in configuration."sv,
143
0
                    PageLimit);
144
0
      return false;
145
0
    }
146
0
    if (auto NewPtr = Allocator::resize(DataPtr, Min, Min + Count);
147
0
        NewPtr == nullptr) {
148
0
      return false;
149
0
    } else {
150
0
      DataPtr = NewPtr;
151
0
    }
152
0
    setLivePageCount(Min + Count);
153
0
    return true;
154
0
  }
155
156
  /// Shrink memory pages. Shared memories cannot shrink.
157
0
  bool shrinkPage(const uint64_t Count) noexcept {
158
0
    if (Count == 0) {
159
0
      return true;
160
0
    }
161
0
    const auto Old = getPageSize();
162
0
    if (isShared() || Count > Old) {
163
0
      return false;
164
0
    }
165
0
    auto *NewPtr = Allocator::shrink(DataPtr, Old, Old - Count);
166
0
    if (NewPtr == nullptr) {
167
0
      return false;
168
0
    }
169
0
    DataPtr = NewPtr;
170
0
    setLivePageCount(Old - Count);
171
0
    return true;
172
0
  }
173
174
  /// Get slice of Data[Offset : Offset + Length - 1]
175
  Expect<Span<Byte>> getBytes(const uint64_t Offset,
176
0
                              const uint64_t Length) const noexcept {
177
    // Check the memory boundary.
178
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
179
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
180
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
181
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
182
0
    }
183
0
    return Span<Byte>(&DataPtr[Offset], Length);
184
0
  }
185
186
  /// Replace the bytes of Data[Offset :] by Slice[Start : Start + Length - 1]
187
  Expect<void> setBytes(Span<const Byte> Slice, const uint64_t Offset,
188
0
                        const uint64_t Start, const uint64_t Length) noexcept {
189
    // Check the memory boundary.
190
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
191
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
192
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
193
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
194
0
    }
195
196
    // Check the input data validation.
197
0
    if (unlikely(std::numeric_limits<uint64_t>::max() - Start < Length ||
198
0
                 Start + Length > static_cast<uint64_t>(Slice.size()))) {
199
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
200
0
      spdlog::error(ErrInfo::InfoBoundary(Start, Length,
201
0
                                          static_cast<uint64_t>(Slice.size())));
202
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
203
0
    }
204
205
    // Copy the data. The slice may be from the same memory instance, so use
206
    // memmove semantics for the possible overlapping case.
207
0
    if (likely(Length > 0)) {
208
0
      std::memmove(DataPtr + Offset, Slice.data() + Start, Length);
209
0
    }
210
0
    return {};
211
0
  }
212
213
  /// Fill the bytes of Data[Offset : Offset + Length - 1] by Val.
214
  Expect<void> fillBytes(const uint8_t Val, const uint64_t Offset,
215
0
                         const uint64_t Length) noexcept {
216
    // Check the memory boundary.
217
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
218
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
219
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
220
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
221
0
    }
222
223
    // Copy the data.
224
0
    if (likely(Length > 0)) {
225
0
      std::fill(DataPtr + Offset, DataPtr + Offset + Length, Val);
226
0
    }
227
0
    return {};
228
0
  }
229
230
  /// Get an uint8 array from Data[Offset : Offset + Length - 1]
231
  Expect<void> getArray(uint8_t *Arr, const uint64_t Offset,
232
                        const uint64_t Length,
233
0
                        const bool IsReverse = false) const 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(DataPtr + Offset, DataPtr + Offset + Length, Arr);
244
0
      } else {
245
0
        std::copy(DataPtr + Offset, DataPtr + Offset + Length, Arr);
246
0
      }
247
0
    }
248
0
    return {};
249
0
  }
250
251
  /// Replace Data[Offset : Offset + Length - 1] to an uint8 array
252
  Expect<void> setArray(const uint8_t *Arr, const uint64_t Offset,
253
                        const uint64_t Length,
254
0
                        const bool IsReverse = false) noexcept {
255
0
    // Check the memory boundary.
256
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
257
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
258
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
259
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
260
0
    }
261
0
    if (likely(Length > 0)) {
262
0
      // Copy the data.
263
0
      if (IsReverse) {
264
0
        std::reverse_copy(Arr, Arr + Length, DataPtr + Offset);
265
0
      } else {
266
0
        std::copy(Arr, Arr + Length, DataPtr + Offset);
267
0
      }
268
0
    }
269
0
    return {};
270
0
  }
271
272
  /// Get pointer to specific offset of memory or null.
273
  template <typename T>
274
  typename std::enable_if_t<std::is_pointer_v<T>, T>
275
  getPointerOrNull(const uint64_t Offset) const noexcept {
276
    using Type = std::remove_pointer_t<T>;
277
    if (Offset == 0 || unlikely(!checkAccessBound(Offset, sizeof(Type)))) {
278
      return nullptr;
279
    }
280
    return reinterpret_cast<T>(&DataPtr[Offset]);
281
  }
282
283
  /// Get pointer to specific offset of memory.
284
  template <typename T>
285
  typename std::enable_if_t<std::is_pointer_v<T>, T>
286
0
  getPointer(const uint64_t Offset) const noexcept {
287
0
    using Type = std::remove_pointer_t<T>;
288
0
    if (unlikely(!checkAccessBound(Offset, sizeof(Type)))) {
289
0
      return nullptr;
290
0
    }
291
0
    return reinterpret_cast<T>(&DataPtr[Offset]);
292
0
  }
Unexecuted instantiation: _ZNK8WasmEdge7Runtime8Instance14MemoryInstance10getPointerIPcEENSt3__19enable_ifIXsr3stdE12is_pointer_vIT_EES7_E4typeEm
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
293
294
  /// Get array of object with count at specific offset of memory.
295
  template <typename T>
296
0
  Span<T> getSpan(const uint64_t Offset, const uint64_t Count) const noexcept {
297
0
    uint64_t Size;
298
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
299
    uint128_t Num =
300
        static_cast<uint128_t>(sizeof(T)) * static_cast<uint128_t>(Count);
301
    if ((Num >> 64).high() != 0) {
302
      return Span<T>();
303
    }
304
    Size = Num.low();
305
#else
306
0
    if (unlikely(__builtin_mul_overflow(static_cast<uint64_t>(sizeof(T)), Count,
307
0
                                        &Size))) {
308
0
      return Span<T>();
309
0
    }
310
0
#endif
311
0
    if (unlikely(!checkAccessBound(Offset, Size))) {
312
0
      return Span<T>();
313
0
    }
314
0
    return Span<T>(reinterpret_cast<T *>(&DataPtr[Offset]), Count);
315
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
316
317
  /// Get array of object at specific offset of memory.
318
  std::string_view getStringView(const uint64_t Offset,
319
0
                                 const uint64_t Size) const noexcept {
320
0
    if (unlikely(!checkAccessBound(Offset, Size))) {
321
0
      return {};
322
0
    }
323
0
    return {reinterpret_cast<const char *>(&DataPtr[Offset]), Size};
324
0
  }
325
326
  /// Template for loading bytes and converting them to a value.
327
  ///
328
  /// Load bytes of the specified length and construct a value.
329
  /// Only output values of int32, uint32, int64, uint64, float, and double are
330
  /// allowed.
331
  ///
332
  /// \param Value the constructed output value.
333
  /// \param Offset the start offset in data array.
334
  ///
335
  /// \returns void on success, ErrCode on failure.
336
  template <typename T, uint32_t Length = sizeof(T)>
337
  typename std::enable_if_t<IsWasmNumV<T>, Expect<void>>
338
0
  loadValue(T &Value, const uint64_t Offset) const noexcept {
339
    // Check the data boundary.
340
0
    static_assert(Length <= sizeof(T));
341
    // Check the memory boundary.
342
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
343
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
344
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
345
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
346
0
    }
347
    // Load the data to the value.
348
0
    if (likely(Length > 0)) {
349
0
      if constexpr (std::is_floating_point_v<T>) {
350
        // Floating case. Do the memory copy.
351
0
        EndianValue<T> LoadValue;
352
0
        std::memcpy(&LoadValue.raw(), &DataPtr[Offset], Length);
353
0
        Value = LoadValue.le();
354
0
      } else {
355
0
        if constexpr (sizeof(T) > 8) {
356
0
          assuming(sizeof(T) == 16);
357
0
          EndianValue<T> LoadValue = 0U;
358
0
          std::memcpy(&LoadValue.raw(), &DataPtr[Offset], Length);
359
0
          Value = LoadValue.le();
360
0
        } else {
361
          // Integer case. Extend to the result type.
362
0
          EndianValue<uint64_t> LoadVal = 0;
363
0
          std::memcpy(&LoadVal.raw(), &DataPtr[Offset], Length);
364
0
          uint64_t Val = LoadVal.le();
365
0
          if (std::is_signed_v<T> && (Val >> (Length * 8 - 1))) {
366
            // Signed extension.
367
0
            for (unsigned int I = Length; I < 8; I++) {
368
0
              Val |= 0xFFULL << (I * 8);
369
0
            }
370
0
          }
371
0
          Value = static_cast<T>(Val);
372
0
        }
373
0
      }
374
0
    }
375
0
    return {};
376
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
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<int, 3u>(int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<int, 4u>(int&, unsigned long) const
Unexecuted instantiation: std::__1::enable_if<IsWasmNumV<long>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::loadValue<long, 8u>(long&, 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, 3u>(unsigned int&, unsigned long) const
377
378
  /// Template for storing a value as bytes.
379
  ///
380
  /// Store the value using the specified byte length.
381
  /// Only input values of uint32, uint64, float, and double are allowed.
382
  ///
383
  /// \param Value the value to store into the data array.
384
  /// \param Offset the start offset in data array.
385
  ///
386
  /// \returns void on success, ErrCode on failure.
387
  template <typename T, uint32_t Length = sizeof(T)>
388
  typename std::enable_if_t<IsWasmNativeNumV<T>, Expect<void>>
389
0
  storeValue(const T &Value, const uint64_t Offset) noexcept {
390
    // Check the data boundary.
391
0
    static_assert(Length <= sizeof(T));
392
    // Check the memory boundary.
393
0
    if (unlikely(!checkAccessBound(Offset, Length))) {
394
0
      spdlog::error(ErrCode::Value::MemoryOutOfBounds);
395
0
      spdlog::error(ErrInfo::InfoBoundary(Offset, Length, getSize()));
396
0
      return Unexpect(ErrCode::Value::MemoryOutOfBounds);
397
0
    }
398
    // Copy the value to memory.
399
0
    if (likely(Length > 0)) {
400
0
      T StoreValue = EndianValue<T>(Value).le();
401
0
      std::memcpy(&DataPtr[Offset], &StoreValue, Length);
402
0
    }
403
0
    return {};
404
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)
Unexecuted instantiation: std::__1::enable_if<IsWasmNativeNumV<unsigned int>, cxx20::expected<void, WasmEdge::ErrCode> >::type WasmEdge::Runtime::Instance::MemoryInstance::storeValue<unsigned int, 3u>(unsigned int const&, unsigned long)
405
406
  /// Waiter support for atomic wait/notify across threads.
407
  struct Waiter {
408
    std::mutex Mutex;
409
    std::condition_variable Cond;
410
    bool Notified = false;
411
0
    Waiter() noexcept = default;
412
  };
413
414
0
  std::mutex &getWaiterMapMutex() noexcept { return WaiterMapMutex; }
415
416
0
  std::unordered_multimap<uint64_t, Waiter> &getWaiterMap() noexcept {
417
0
    return WaiterMap;
418
0
  }
419
420
  /// Wake all waiters on this memory instance (used by Executor::stop()).
421
0
  void notifyAllWaiters() noexcept {
422
0
    std::unique_lock<std::mutex> Locker(WaiterMapMutex);
423
0
    for (auto &[Addr, W] : WaiterMap) {
424
      // Lock the Waiter mutex and notify while holding it. This pairs with
425
      // the wait loop which checks StopToken under this same mutex before
426
      // entering Cond.wait(). The lock ensures we either:
427
      // (a) block until the waiter enters Cond.wait() — then wake it, or
428
      // (b) run before the waiter checks StopToken — it will see it set.
429
0
      std::unique_lock<std::mutex> WaiterLocker(W.Mutex);
430
0
      W.Cond.notify_all();
431
0
    }
432
0
  }
433
434
private:
435
  /// Update the page count in the limit and its live mirror synchronously.
436
0
  void setLivePageCount(const uint64_t Count) noexcept {
437
0
    MemType.getLimit().setMin(Count);
438
0
    LivePageCount = Count;
439
0
  }
440
441
  /// \name Data of memory instance.
442
  /// @{
443
  AST::MemoryType MemType;
444
  uint8_t *DataPtr = nullptr;
445
  uint64_t PageLimit;
446
  uint64_t LivePageCount;
447
  std::mutex WaiterMapMutex;
448
  std::unordered_multimap<uint64_t, Waiter> WaiterMap;
449
  /// @}
450
};
451
452
} // namespace Instance
453
} // namespace Runtime
454
} // namespace WasmEdge