Coverage Report

Created: 2026-07-16 07:09

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