Coverage Report

Created: 2026-09-28 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/executor/helper.cpp
Line
Count
Source
1
// SPDX-License-Identifier: Apache-2.0
2
// SPDX-FileCopyrightText: Copyright The WasmEdge Authors
3
4
#include "executor/executor.h"
5
6
#include "common/spdlog.h"
7
#include "runtime/storemgr.h"
8
#include "system/fault.h"
9
#include "system/stacktrace.h"
10
11
#include <cstdint>
12
#include <shared_mutex>
13
#include <unordered_set>
14
#include <utility>
15
#include <vector>
16
17
namespace WasmEdge {
18
namespace Executor {
19
20
Executor::SavedThreadLocal::SavedThreadLocal(
21
    Executor &Ex, Runtime::StackManager &StackMgr,
22
0
    [[maybe_unused]] const Runtime::Instance::FunctionInstance &Func) noexcept {
23
  // Prepare the execution context.
24
0
  SavedThis = This;
25
0
  This = &Ex;
26
27
0
  SavedExecutionContext = ExecutionContext;
28
0
  ExecutionContext.StopToken = &Ex.StopToken;
29
0
  ExecutionContext.PendingExnTagAddr =
30
0
      reinterpret_cast<void *const *>(&PendingExn.TagInst);
31
0
  if (Ex.Stat) {
32
0
    ExecutionContext.InstrCount = &Ex.Stat->getInstrCountRef();
33
0
    ExecutionContext.CostTable = Ex.Stat->getCostTable().data();
34
0
    ExecutionContext.Gas = &Ex.Stat->getTotalCostRef();
35
0
    ExecutionContext.GasLimit = Ex.Stat->getCostLimit();
36
0
  }
37
38
0
  SavedCurrentStack = CurrentStack;
39
0
  CurrentStack = &StackMgr;
40
0
}
41
42
0
Executor::SavedThreadLocal::~SavedThreadLocal() noexcept {
43
0
  CurrentStack = SavedCurrentStack;
44
0
  ExecutionContext = SavedExecutionContext;
45
0
  This = SavedThis;
46
0
}
47
48
Expect<AST::InstrView::iterator> Executor::enterFunction(
49
    Runtime::StackManager &StackMgr,
50
    const Runtime::Instance::FunctionInstance &Func,
51
    const AST::InstrView::iterator RetIt, bool IsTailCall, bool IsNativeEntry,
52
0
    const Runtime::Instance::ModuleInstance *CallerModInst) {
53
  // RetIt: the return position when the entered function returns.
54
55
  // Check whether interruption occurred.
56
0
  if (unlikely(StopToken.exchange(0, std::memory_order_relaxed))) {
57
0
    spdlog::error(ErrCode::Value::Interrupted);
58
0
    return Unexpect(ErrCode::Value::Interrupted);
59
0
  }
60
61
  // Get the function type for the parameter and return counts.
62
0
  const auto &FuncType = Func.getFuncType();
63
0
  const uint32_t ArgsN = static_cast<uint32_t>(FuncType.getParamTypes().size());
64
0
  const uint32_t RetsN =
65
0
      static_cast<uint32_t>(FuncType.getReturnTypes().size());
66
67
  // For the exception handler, remove the inactive handlers caused by the
68
  // branches.
69
0
  const auto Instrs = Func.getInstrs();
70
0
  if (likely(RetIt) && RetIt != Instrs.begin()) {
71
0
    StackMgr.removeInactiveHandler(RetIt - 1);
72
0
  }
73
74
0
  if (Func.isHostFunction()) {
75
    // Host function case: Push args and call function.
76
0
    auto &HostFunc = Func.getHostFunc();
77
78
    // Finalize the host module on its first host-function invocation, after
79
    // which adding host instances to it is rejected.
80
0
    if (const auto *HostModInst = Func.getModule()) {
81
0
      HostModInst->finalizeInstantiation();
82
0
    }
83
84
    // Generate CallingFrame from current frame.
85
    // The module instance will be nullptr if current frame is a dummy frame.
86
    // For this case, use the module instance of this host function.
87
0
    const auto *ModInst = CallerModInst;
88
0
    if (ModInst == nullptr) {
89
0
      ModInst = StackMgr.getModule();
90
0
    }
91
0
    if (ModInst == nullptr) {
92
0
      ModInst = Func.getModule();
93
0
    }
94
0
    Runtime::CallingFrame CallFrame(this, ModInst);
95
96
    // Push frame.
97
0
    StackMgr.pushFrame(Func.getModule(), // Module instance
98
0
                       RetIt,            // Return PC
99
0
                       ArgsN,            // Only args, no locals in stack
100
0
                       RetsN,            // Returns num
101
0
                       IsTailCall,       // For tail-call
102
0
                       IsNativeEntry     // For native entry
103
0
    );
104
105
    // Do the statistics if the statistics turned on.
106
0
    if (Stat) {
107
      // Check host function cost.
108
0
      if (unlikely(!Stat->addCost(HostFunc.getCost()))) {
109
0
        spdlog::error(ErrCode::Value::CostLimitExceeded);
110
0
        return Unexpect(ErrCode::Value::CostLimitExceeded);
111
0
      }
112
      // Start recording time of running host function.
113
0
      Stat->stopRecordWasm();
114
0
      Stat->startRecordHost();
115
0
    }
116
117
    // Call pre-host-function
118
0
    HostFuncHelper.invokePreHostFunc();
119
120
    // Run host function.
121
0
    Span<ValVariant> Args = StackMgr.getTopSpan(ArgsN);
122
0
    for (uint32_t I = 0; I < ArgsN; I++) {
123
      // For the number type cases of the arguments, the unused bits should be
124
      // erased due to the security issue.
125
0
      cleanNumericVal(Args[I], FuncType.getParamTypes()[I]);
126
0
    }
127
0
    std::vector<ValVariant> Rets(RetsN);
128
0
    auto Ret = HostFunc.run(CallFrame, std::move(Args), Rets);
129
130
    // Call post-host-function
131
0
    HostFuncHelper.invokePostHostFunc();
132
133
    // Do the statistics if the statistics turned on.
134
0
    if (Stat) {
135
      // Stop recording time of running host function.
136
0
      Stat->stopRecordHost();
137
0
      Stat->startRecordWasm();
138
0
    }
139
140
    // Check the host function execution status.
141
0
    if (!Ret) {
142
0
      if (Ret.error() == ErrCode::Value::HostFuncError ||
143
0
          Ret.error().getCategory() != ErrCategory::WASM) {
144
0
        spdlog::error(Ret.error());
145
0
      }
146
0
      return Unexpect(Ret);
147
0
    }
148
149
    // Push returns back to the stack.
150
0
    for (auto &R : Rets) {
151
0
      StackMgr.push(std::move(R));
152
0
    }
153
154
    // A tail call pops the replaced caller's frame, whose `From` is one before
155
    // its resume point, so step it forward one instruction for `runCallOp`.
156
0
    const AST::InstrView::iterator Continuation = StackMgr.popFrame();
157
0
    return IsTailCall ? Continuation + 1 : Continuation;
158
0
  } else if (Func.isCompiledFunction()) {
159
    // Compiled function case: Execute the function and jump to the
160
    // continuation.
161
162
    // Push frame.
163
0
    StackMgr.pushFrame(Func.getModule(), // Module instance
164
0
                       RetIt,            // Return PC
165
0
                       ArgsN,            // Only args, no locals in stack
166
0
                       RetsN,            // Returns num
167
0
                       IsTailCall,       // For tail-call
168
0
                       IsNativeEntry     // For native entry
169
0
    );
170
171
    // Prepare arguments.
172
0
    Span<ValVariant> Args = StackMgr.getTopSpan(ArgsN);
173
0
    std::vector<ValVariant> Rets(RetsN);
174
0
    SavedThreadLocal Saved(*this, StackMgr, Func);
175
176
0
    ErrCode Err;
177
0
    try {
178
      // Get symbol and execute the function.
179
0
      Fault FaultHandler;
180
0
      uint32_t Code = PREPARE_FAULT(FaultHandler);
181
0
      if (Code != 0) {
182
0
        auto InnerStackTrace = FaultHandler.stacktrace();
183
0
        {
184
0
          std::array<void *, 256> Buffer;
185
0
          auto OuterStackTrace = stackTrace(Buffer);
186
0
          while (!OuterStackTrace.empty() && !InnerStackTrace.empty() &&
187
0
                 InnerStackTrace[InnerStackTrace.size() - 1] ==
188
0
                     OuterStackTrace[OuterStackTrace.size() - 1]) {
189
0
            InnerStackTrace = InnerStackTrace.first(InnerStackTrace.size() - 1);
190
0
            OuterStackTrace = OuterStackTrace.first(OuterStackTrace.size() - 1);
191
0
          }
192
0
        }
193
0
        auto LiveModules = collectLiveModules(StackMgr);
194
0
        StackTraceSize =
195
0
            compiledStackTrace(LiveModules, InnerStackTrace, StackTrace).size();
196
0
        Err = ErrCode(static_cast<ErrCategory>(Code >> 24), Code);
197
0
      } else {
198
0
        auto &Wrapper = FuncType.getSymbol();
199
0
        Wrapper(
200
0
            &const_cast<Runtime::Instance::ModuleInstance *>(Func.getModule())
201
0
                 ->ModCtx,
202
0
            &ExecutionContext, Func.getSymbol().get(), Args.data(),
203
0
            Rets.data());
204
0
      }
205
0
    } catch (const ErrCode &E) {
206
0
      Err = E;
207
0
    }
208
0
    if (unlikely(Err)) {
209
0
      if (Err != ErrCode::Value::Terminated) {
210
0
        spdlog::error(Err);
211
0
      }
212
0
      StackTraceSize +=
213
0
          interpreterStackTrace(
214
0
              StackMgr,
215
0
              Span<StackTraceEntry>{StackTrace}.subspan(StackTraceSize))
216
0
              .size();
217
0
      return Unexpect(Err);
218
0
    }
219
220
0
    if (unlikely(PendingExn.TagInst != nullptr)) {
221
      // The exception escapes this frame: discard it, then hand off to the
222
      // native caller or continue the handler walk in the interpreter caller.
223
0
      const bool FromNative = StackMgr.isTopFrameNativeEntry();
224
      // Push the dummy results for popping the frame, then drop them because
225
      // the escaping exception produces no results.
226
0
      for (uint32_t I = 0; I < RetsN; ++I) {
227
0
        StackMgr.push(Rets[I]);
228
0
      }
229
0
      AST::InstrView::iterator ResumePC = StackMgr.popFrame();
230
0
      StackMgr.eraseValueStack(RetsN, 0);
231
0
      if (FromNative) {
232
0
        return Unexpect(ErrCode::Value::PendingException);
233
0
      }
234
0
      auto &TagInst = *PendingExn.TagInst;
235
0
      const auto *ExnInst = PendingExn.Inst;
236
0
      StackMgr.pushValVec(PendingExn.getPayload());
237
0
      PendingExn = {};
238
0
      EXPECTED_TRY(throwException(StackMgr, TagInst, ResumePC, ExnInst));
239
0
      return ResumePC + 1;
240
0
    }
241
242
    // Push returns back to the stack.
243
0
    for (uint32_t I = 0; I < Rets.size(); ++I) {
244
0
      StackMgr.push(Rets[I]);
245
0
    }
246
247
    // As in the host case, step a tail-call continuation forward one
248
    // instruction for `runCallOp`.
249
0
    const AST::InstrView::iterator Continuation = StackMgr.popFrame();
250
0
    return IsTailCall ? Continuation + 1 : Continuation;
251
0
  } else {
252
    // WASM interpreter case: Jump to the start of the function body.
253
254
    // Push local variables into the stack.
255
0
    for (auto &Def : Func.getLocals()) {
256
0
      if (Def.second.isRefType()) {
257
        // Type the null refs with the bottom heap type of their hierarchy so
258
        // that ref.test/ref.cast match them against the concrete types.
259
0
        const auto BotTypeCode = toBottomType(Func.getModule(), Def.second);
260
0
        RefVariant InitVal(ValType(TypeCode::RefNull, BotTypeCode));
261
0
        for (uint32_t I = 0; I < Def.first; I++) {
262
0
          StackMgr.push(InitVal);
263
0
        }
264
0
      } else {
265
0
        for (uint32_t I = 0; I < Def.first; I++) {
266
0
          StackMgr.push(ValueFromType(Def.second));
267
0
        }
268
0
      }
269
0
    }
270
271
    // Push frame.
272
    // The PC must -1 here because in the interpreter mode execution, the PC
273
    // will increase after the callee returns.
274
0
    StackMgr.pushFrame(Func.getModule(),           // Module instance
275
0
                       RetIt - 1,                  // Return PC
276
0
                       ArgsN + Func.getLocalNum(), // Arguments num + local num
277
0
                       RetsN,                      // Returns num
278
0
                       IsTailCall,                 // For tail-call
279
0
                       IsNativeEntry               // For native entry
280
0
    );
281
282
    // For the WASM interpreter case, the continuation will be the start of the
283
    // function body.
284
0
    return Instrs.begin();
285
0
  }
286
0
}
287
288
std::vector<const Runtime::Instance::ModuleInstance *>
289
Executor::collectLiveModules(
290
0
    const Runtime::StackManager &StackMgr) const noexcept {
291
0
  std::vector<const Runtime::Instance::ModuleInstance *> Modules;
292
0
  std::unordered_set<const Runtime::Instance::ModuleInstance *> Seen;
293
0
  auto AddModule = [&](const Runtime::Instance::ModuleInstance *M) {
294
0
    if (M != nullptr && Seen.insert(M).second) {
295
0
      Modules.push_back(M);
296
0
    }
297
0
  };
298
299
0
  for (const auto &Frame : StackMgr.getFramesSpan()) {
300
0
    AddModule(Frame.Module);
301
0
  }
302
303
0
  std::unordered_set<Runtime::StoreManager *> Stores;
304
0
  auto GatherStores = [&](const Runtime::Instance::ModuleInstance *M) {
305
0
    if (M == nullptr) {
306
0
      return;
307
0
    }
308
0
    std::shared_lock Lock(M->Mutex);
309
0
    for (const auto &Entry : M->LinkedStore) {
310
0
      Stores.insert(Entry.first.first);
311
0
    }
312
0
  };
313
314
0
  const size_t SeedCount = Modules.size();
315
0
  for (size_t I = 0; I < SeedCount; ++I) {
316
0
    GatherStores(Modules[I]);
317
0
    for (const auto *Func : Modules[I]->getFunctionInstances()) {
318
0
      if (Func != nullptr) {
319
0
        GatherStores(Func->getModule());
320
0
      }
321
0
    }
322
0
  }
323
324
0
  for (auto *Store : Stores) {
325
0
    Store->getModuleList([&AddModule](const auto &NamedMod) {
326
0
      for (const auto &Entry : NamedMod) {
327
0
        AddModule(Entry.second);
328
0
      }
329
0
    });
330
0
  }
331
0
  return Modules;
332
0
}
333
334
Expect<void>
335
Executor::branchToLabel(Runtime::StackManager &StackMgr,
336
                        const AST::Instruction::JumpDescriptor &JumpDesc,
337
0
                        AST::InstrView::iterator &PC) noexcept {
338
  // Check the stop token.
339
0
  if (unlikely(StopToken.exchange(0, std::memory_order_relaxed))) {
340
0
    spdlog::error(ErrCode::Value::Interrupted);
341
0
    return Unexpect(ErrCode::Value::Interrupted);
342
0
  }
343
344
0
  StackMgr.eraseValueStack(JumpDesc.StackEraseBegin, JumpDesc.StackEraseEnd);
345
  // PC needs -1 here because the PC will increase in the next iteration.
346
0
  PC += (JumpDesc.PCOffset - 1);
347
  // A branch leaves the innermost blocks without running their `end`, so the
348
  // handlers it strands are the top of the handler stack right now. Drop them
349
  // here: once a later try_table is pushed on top they are indistinguishable
350
  // from active handlers, and their stale VPos would invert the erase range in
351
  // popTopHandler. PC + 1 is the instruction being branched to.
352
0
  StackMgr.removeInactiveHandler(PC + 1);
353
0
  return {};
354
0
}
355
356
Expect<void> Executor::throwException(
357
    Runtime::StackManager &StackMgr, Runtime::Instance::TagInstance &TagInst,
358
    AST::InstrView::iterator &PC,
359
0
    const Runtime::Instance::ExceptionInstance *ExnInst) noexcept {
360
0
  StackMgr.removeInactiveHandler(PC);
361
0
  auto AssocValSize = TagInst.getTagType().getAssocValSize();
362
0
  while (true) {
363
    // Pop the top handler.
364
0
    auto Handler = StackMgr.popTopHandler(AssocValSize);
365
0
    if (!Handler.has_value()) {
366
0
      break;
367
0
    }
368
    // Checking through the catch clause.
369
0
    for (const auto &C : Handler->CatchClause) {
370
0
      if (!C.IsAll &&
371
0
          getTagInstByIdx(StackMgr.getModule(), C.TagIndex) != &TagInst) {
372
        // Specific-tag clauses require tag-address equivalence; skip the
373
        // ones that do not match.
374
0
        continue;
375
0
      }
376
0
      if (C.IsRef) {
377
        // Allocate the exception instance lazily on the first catch_ref;
378
        // reuse the one passed in by throw_ref to preserve exnref identity.
379
0
        const Runtime::Instance::ExceptionInstance *Inst = ExnInst;
380
0
        if (Inst == nullptr) {
381
0
          auto Payload = StackMgr.getTopSpan(AssocValSize);
382
0
          std::vector<ValVariant> Vec(Payload.begin(), Payload.end());
383
0
          auto *ModInst = const_cast<Runtime::Instance::ModuleInstance *>(
384
0
              StackMgr.getModule());
385
0
          Inst = ModInst->newException(&TagInst, std::move(Vec));
386
0
        }
387
0
        if (C.IsAll) {
388
0
          StackMgr.eraseValueStack(AssocValSize, 0);
389
0
        }
390
0
        StackMgr.push(
391
0
            RefVariant(ValType(TypeCode::Ref, TypeCode::ExnRef), Inst));
392
0
      } else if (C.IsAll) {
393
0
        StackMgr.eraseValueStack(AssocValSize, 0);
394
0
      }
395
      // When an exception is caught, move the PC to the try block and branch to
396
      // the label.
397
398
0
      PC = Handler->Try;
399
0
      return branchToLabel(StackMgr, C.Jump, PC);
400
0
    }
401
0
  }
402
0
  if (StackMgr.isTopFrameNativeEntry()) {
403
    // Stopped at a frame entered from the native code: record the exception
404
    // as pending and restore the stack; the native caller continues it.
405
0
    PendingExn.TagInst = &TagInst;
406
0
    PendingExn.Inst = ExnInst;
407
0
    PendingExn.setPayload(StackMgr.getTopSpan(AssocValSize));
408
    // Push the dummy results for popping the frame, then drop them because
409
    // the escaping exception produces no results.
410
0
    const uint32_t Arity = StackMgr.getFramesSpan().back().Arity;
411
0
    for (uint32_t I = 0; I < Arity; ++I) {
412
0
      StackMgr.push(ValVariant());
413
0
    }
414
0
    StackMgr.popFrame();
415
0
    StackMgr.eraseValueStack(Arity, 0);
416
0
    return Unexpect(ErrCode::Value::PendingException);
417
0
  }
418
0
  spdlog::error(ErrCode::Value::UncaughtException);
419
0
  return Unexpect(ErrCode::Value::UncaughtException);
420
0
}
421
422
Expect<void>
423
Executor::checkOffsetOverflow(const Runtime::Instance::MemoryInstance &MemInst,
424
                              const AST::Instruction &Instr, const uint64_t Val,
425
0
                              const uint64_t Size) const noexcept {
426
  // This function simply checks that the calculated offset fits in 64 bits.
427
0
  uint64_t StartOffset;
428
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
429
  if (std::numeric_limits<uint64_t>::max() - Instr.getMemoryOffset() < Val) {
430
    StartOffset = Instr.getMemoryOffset() + Val;
431
#else
432
0
  if (unlikely(
433
0
          __builtin_add_overflow(Instr.getMemoryOffset(), Val, &StartOffset))) {
434
0
#endif
435
0
    spdlog::error(ErrCode::Value::MemoryOutOfBounds);
436
0
    spdlog::error(
437
0
        ErrInfo::InfoBoundary(StartOffset, Size, MemInst.getSize(), true));
438
0
    spdlog::error(
439
0
        ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset()));
440
0
    return Unexpect(ErrCode::Value::MemoryOutOfBounds);
441
0
  }
442
0
  return {};
443
0
}
444
445
const AST::SubType *
446
Executor::getDefTypeByIdx(const Runtime::Instance::ModuleInstance *ModInst,
447
0
                          const uint32_t Idx) const {
448
  // When the top frame is a dummy frame, the instance cannot be found.
449
0
  if (unlikely(ModInst == nullptr)) {
450
0
    return nullptr;
451
0
  }
452
0
  return ModInst->unsafeGetType(Idx);
453
0
}
454
455
const WasmEdge::AST::CompositeType &Executor::getCompositeTypeByIdx(
456
    const Runtime::Instance::ModuleInstance *ModInst,
457
0
    const uint32_t Idx) const noexcept {
458
0
  auto *DefType = getDefTypeByIdx(ModInst, Idx);
459
0
  assuming(DefType);
460
0
  const auto &CompType = DefType->getCompositeType();
461
0
  assuming(!CompType.isFunc());
462
0
  return CompType;
463
0
}
464
465
const ValType &Executor::getStructStorageTypeByIdx(
466
    const Runtime::Instance::ModuleInstance *ModInst, const uint32_t Idx,
467
0
    const uint32_t Off) const noexcept {
468
0
  const auto &CompType = getCompositeTypeByIdx(ModInst, Idx);
469
0
  assuming(static_cast<uint32_t>(CompType.getFieldTypes().size()) > Off);
470
0
  return CompType.getFieldTypes()[Off].getStorageType();
471
0
}
472
473
const ValType &Executor::getArrayStorageTypeByIdx(
474
    const Runtime::Instance::ModuleInstance *ModInst,
475
0
    const uint32_t Idx) const noexcept {
476
0
  const auto &CompType = getCompositeTypeByIdx(ModInst, Idx);
477
0
  assuming(static_cast<uint32_t>(CompType.getFieldTypes().size()) == 1);
478
0
  return CompType.getFieldTypes()[0].getStorageType();
479
0
}
480
481
Runtime::Instance::FunctionInstance *
482
Executor::getFuncInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
483
0
                           const uint32_t Idx) const {
484
  // When the top frame is a dummy frame, the instance cannot be found.
485
0
  if (unlikely(ModInst == nullptr)) {
486
0
    return nullptr;
487
0
  }
488
0
  return ModInst->unsafeGetFunction(Idx);
489
0
}
490
491
Runtime::Instance::TableInstance *
492
Executor::getTabInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
493
0
                          const uint32_t Idx) const {
494
  // When the top frame is a dummy frame, the instance cannot be found.
495
0
  if (unlikely(ModInst == nullptr)) {
496
0
    return nullptr;
497
0
  }
498
0
  return ModInst->unsafeGetTable(Idx);
499
0
}
500
501
Runtime::Instance::MemoryInstance *
502
Executor::getMemInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
503
0
                          const uint32_t Idx) const {
504
  // When the top frame is a dummy frame, the instance cannot be found.
505
0
  if (unlikely(ModInst == nullptr)) {
506
0
    return nullptr;
507
0
  }
508
0
  return ModInst->unsafeGetMemory(Idx);
509
0
}
510
511
Runtime::Instance::TagInstance *
512
Executor::getTagInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
513
0
                          const uint32_t Idx) const {
514
  // When the top frame is a dummy frame, the instance cannot be found.
515
0
  if (unlikely(ModInst == nullptr)) {
516
0
    return nullptr;
517
0
  }
518
0
  return ModInst->unsafeGetTag(Idx);
519
0
}
520
521
Runtime::Instance::GlobalInstance *
522
Executor::getGlobInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
523
0
                           const uint32_t Idx) const {
524
  // When the top frame is a dummy frame, the instance cannot be found.
525
0
  if (unlikely(ModInst == nullptr)) {
526
0
    return nullptr;
527
0
  }
528
0
  return ModInst->unsafeGetGlobal(Idx);
529
0
}
530
531
Runtime::Instance::ElementInstance *
532
Executor::getElemInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
533
0
                           const uint32_t Idx) const {
534
  // When the top frame is a dummy frame, the instance cannot be found.
535
0
  if (unlikely(ModInst == nullptr)) {
536
0
    return nullptr;
537
0
  }
538
0
  return ModInst->unsafeGetElem(Idx);
539
0
}
540
541
Runtime::Instance::DataInstance *
542
Executor::getDataInstByIdx(const Runtime::Instance::ModuleInstance *ModInst,
543
0
                           const uint32_t Idx) const {
544
  // When the top frame is a dummy frame, the instance cannot be found.
545
0
  if (unlikely(ModInst == nullptr)) {
546
0
    return nullptr;
547
0
  }
548
0
  return ModInst->unsafeGetData(Idx);
549
0
}
550
551
TypeCode
552
Executor::toBottomType(const Runtime::Instance::ModuleInstance *ModInst,
553
0
                       const ValType &Type) const {
554
0
  if (Type.isRefType()) {
555
0
    if (Type.isAbsHeapType()) {
556
0
      switch (Type.getHeapTypeCode()) {
557
0
      case TypeCode::NullFuncRef:
558
0
      case TypeCode::FuncRef:
559
0
        return TypeCode::NullFuncRef;
560
0
      case TypeCode::NullExternRef:
561
0
      case TypeCode::ExternRef:
562
0
        return TypeCode::NullExternRef;
563
0
      case TypeCode::NullRef:
564
0
      case TypeCode::AnyRef:
565
0
      case TypeCode::EqRef:
566
0
      case TypeCode::I31Ref:
567
0
      case TypeCode::StructRef:
568
0
      case TypeCode::ArrayRef:
569
0
        return TypeCode::NullRef;
570
0
      case TypeCode::NullExnRef:
571
0
      case TypeCode::ExnRef:
572
0
        return TypeCode::NullExnRef;
573
0
      default:
574
0
        assumingUnreachable();
575
0
      }
576
0
    } else {
577
0
      const auto &CompType =
578
0
          ModInst->unsafeGetType(Type.getTypeIndex())->getCompositeType();
579
0
      if (CompType.isFunc()) {
580
0
        return TypeCode::NullFuncRef;
581
0
      } else {
582
0
        return TypeCode::NullRef;
583
0
      }
584
0
    }
585
0
  } else {
586
0
    return Type.getCode();
587
0
  }
588
0
}
589
590
void Executor::cleanNumericVal(ValVariant &Val,
591
0
                               const ValType &Type) const noexcept {
592
0
  if (Type.isNumType()) {
593
0
    switch (Type.getCode()) {
594
0
    case TypeCode::I32: {
595
0
      uint32_t V = Val.get<uint32_t>();
596
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
597
0
      Val.emplace<uint32_t>(V);
598
0
      break;
599
0
    }
600
0
    case TypeCode::F32: {
601
0
      float V = Val.get<float>();
602
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
603
0
      Val.emplace<float>(V);
604
0
      break;
605
0
    }
606
0
    case TypeCode::I64: {
607
0
      uint64_t V = Val.get<uint64_t>();
608
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
609
0
      Val.emplace<uint64_t>(V);
610
0
      break;
611
0
    }
612
0
    case TypeCode::F64: {
613
0
      double V = Val.get<double>();
614
0
      Val.emplace<uint128_t>(static_cast<uint128_t>(0U));
615
0
      Val.emplace<double>(V);
616
0
      break;
617
0
    }
618
0
    default:
619
0
      break;
620
0
    }
621
0
  }
622
0
}
623
624
ValVariant Executor::packVal(const ValType &Type,
625
0
                             const ValVariant &Val) const noexcept {
626
0
  if (Type.isPackType()) {
627
0
    switch (Type.getCode()) {
628
0
    case TypeCode::I8:
629
0
      if constexpr (Endian::native == Endian::little) {
630
0
        return ValVariant(Val.get<uint32_t>() & 0xFFU);
631
      } else {
632
        return ValVariant(Val.get<uint32_t>() << 24);
633
      }
634
0
    case TypeCode::I16:
635
0
      if constexpr (Endian::native == Endian::little) {
636
0
        return ValVariant(Val.get<uint32_t>() & 0xFFFFU);
637
      } else {
638
        return ValVariant(Val.get<uint32_t>() << 16);
639
      }
640
0
    default:
641
0
      assumingUnreachable();
642
0
    }
643
0
  }
644
0
  return Val;
645
0
}
646
647
std::vector<ValVariant>
648
Executor::packVals(const ValType &Type,
649
0
                   std::vector<ValVariant> &&Vals) const noexcept {
650
0
  for (uint32_t I = 0; I < Vals.size(); I++) {
651
0
    Vals[I] = packVal(Type, Vals[I]);
652
0
  }
653
0
  return std::move(Vals);
654
0
}
655
656
ValVariant Executor::unpackVal(const ValType &Type, const ValVariant &Val,
657
0
                               bool IsSigned) const noexcept {
658
0
  if (Type.isPackType()) {
659
0
    uint32_t Num = Val.get<uint32_t>();
660
0
    switch (Type.getCode()) {
661
0
    case TypeCode::I8:
662
      if constexpr (Endian::native == Endian::big) {
663
        Num >>= 24;
664
      }
665
0
      if (IsSigned) {
666
0
        return static_cast<uint32_t>(static_cast<int8_t>(Num));
667
0
      } else {
668
0
        return static_cast<uint32_t>(static_cast<uint8_t>(Num));
669
0
      }
670
0
    case TypeCode::I16:
671
      if constexpr (Endian::native == Endian::big) {
672
        Num >>= 16;
673
      }
674
0
      if (IsSigned) {
675
0
        return static_cast<uint32_t>(static_cast<int16_t>(Num));
676
0
      } else {
677
0
        return static_cast<uint32_t>(static_cast<uint16_t>(Num));
678
0
      }
679
0
    default:
680
0
      assumingUnreachable();
681
0
    }
682
0
  }
683
0
  return Val;
684
0
}
685
686
} // namespace Executor
687
} // namespace WasmEdge