Coverage Report

Created: 2026-06-30 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/WasmEdge/lib/executor/engine/controlInstr.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 <cstdint>
7
8
namespace WasmEdge {
9
namespace Executor {
10
11
Expect<void> Executor::runIfElseOp(Runtime::StackManager &StackMgr,
12
                                   const AST::Instruction &Instr,
13
0
                                   AST::InstrView::iterator &PC) noexcept {
14
  // Get condition.
15
0
  uint32_t Cond = StackMgr.pop().get<uint32_t>();
16
17
  // If non-zero, run if-statement; else, run else-statement.
18
0
  if (Cond == 0) {
19
0
    if (Instr.getJumpElse() == Instr.getJumpEnd()) {
20
      // No else-statement case. Jump to right before the End instruction.
21
0
      PC += (Instr.getJumpEnd() - 1);
22
0
    } else {
23
0
      if (Stat) {
24
0
        Stat->incInstrCount();
25
0
        if (unlikely(!Stat->addInstrCost(OpCode::Else))) {
26
0
          return Unexpect(ErrCode::Value::CostLimitExceeded);
27
0
        }
28
0
      }
29
      // Else-statement case. Jump to the Else instruction to continue.
30
0
      PC += Instr.getJumpElse();
31
0
    }
32
0
  }
33
0
  return {};
34
0
}
35
36
Expect<void> Executor::runThrowOp(Runtime::StackManager &StackMgr,
37
                                  const AST::Instruction &Instr,
38
0
                                  AST::InstrView::iterator &PC) noexcept {
39
0
  auto *TagInst = getTagInstByIdx(StackMgr, Instr.getTargetIndex());
40
  // The arguments will be popped from the stack in the throw function.
41
0
  return throwException(StackMgr, *TagInst, PC);
42
0
}
43
44
Expect<void> Executor::runThrowRefOp(Runtime::StackManager &StackMgr,
45
                                     const AST::Instruction &Instr,
46
0
                                     AST::InstrView::iterator &PC) noexcept {
47
0
  const auto Ref = StackMgr.pop().get<RefVariant>();
48
0
  if (Ref.isNull()) {
49
0
    spdlog::error(ErrCode::Value::AccessNullException);
50
0
    spdlog::error(
51
0
        ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset()));
52
0
    return Unexpect(ErrCode::Value::AccessNullException);
53
0
  }
54
0
  const auto *ExnInst = Ref.getPtr<Runtime::Instance::ExceptionInstance>();
55
0
  auto *TagInst = ExnInst->getTag();
56
  // Re-push the captured payload to mirror an initial `throw`, then unwind.
57
0
  StackMgr.pushValVec(ExnInst->getPayload());
58
0
  return throwException(StackMgr, *TagInst, PC, ExnInst);
59
0
}
60
61
Expect<void> Executor::runBrOp(Runtime::StackManager &StackMgr,
62
                               const AST::Instruction &Instr,
63
0
                               AST::InstrView::iterator &PC) noexcept {
64
0
  return branchToLabel(StackMgr, Instr.getJump(), PC);
65
0
}
66
67
Expect<void> Executor::runBrIfOp(Runtime::StackManager &StackMgr,
68
                                 const AST::Instruction &Instr,
69
0
                                 AST::InstrView::iterator &PC) noexcept {
70
0
  if (StackMgr.pop().get<uint32_t>() != 0) {
71
0
    return runBrOp(StackMgr, Instr, PC);
72
0
  }
73
0
  return {};
74
0
}
75
76
Expect<void> Executor::runBrOnNullOp(Runtime::StackManager &StackMgr,
77
                                     const AST::Instruction &Instr,
78
0
                                     AST::InstrView::iterator &PC) noexcept {
79
0
  if (StackMgr.getTop().get<RefVariant>().isNull()) {
80
0
    StackMgr.pop();
81
0
    return runBrOp(StackMgr, Instr, PC);
82
0
  }
83
0
  return {};
84
0
}
85
86
Expect<void> Executor::runBrOnNonNullOp(Runtime::StackManager &StackMgr,
87
                                        const AST::Instruction &Instr,
88
0
                                        AST::InstrView::iterator &PC) noexcept {
89
0
  if (!StackMgr.getTop().get<RefVariant>().isNull()) {
90
0
    return runBrOp(StackMgr, Instr, PC);
91
0
  }
92
0
  StackMgr.pop();
93
0
  return {};
94
0
}
95
96
Expect<void> Executor::runBrTableOp(Runtime::StackManager &StackMgr,
97
                                    const AST::Instruction &Instr,
98
0
                                    AST::InstrView::iterator &PC) noexcept {
99
  // Get the value on top of the stack.
100
0
  uint32_t Value = StackMgr.pop().get<uint32_t>();
101
102
  // Do branch.
103
0
  auto LabelTable = Instr.getLabelList();
104
0
  const auto LabelTableSize = static_cast<uint32_t>(LabelTable.size() - 1);
105
0
  if (Value < LabelTableSize) {
106
0
    return branchToLabel(StackMgr, LabelTable[Value], PC);
107
0
  }
108
0
  return branchToLabel(StackMgr, LabelTable[LabelTableSize], PC);
109
0
}
110
111
Expect<void> Executor::runBrOnCastOp(Runtime::StackManager &StackMgr,
112
                                     const AST::Instruction &Instr,
113
                                     AST::InstrView::iterator &PC,
114
0
                                     bool IsReverse) noexcept {
115
  // Get the value on top of the stack.
116
0
  const auto *ModInst = StackMgr.getModule();
117
0
  const auto &Val = StackMgr.getTop().get<RefVariant>();
118
0
  const auto &VT = Val.getType();
119
0
  Span<const AST::SubType *const> GotTypeList = ModInst->getTypeList();
120
0
  if (!VT.isAbsHeapType()) {
121
0
    auto *Inst = Val.getPtr<Runtime::Instance::CompositeBase>();
122
    // Reference must not be nullptr here because the null references are typed
123
    // with the least abstract heap type.
124
0
    if (Inst->getModule()) {
125
0
      GotTypeList = Inst->getModule()->getTypeList();
126
0
    }
127
0
  }
128
129
0
  ValType NormalizedVT = VT;
130
0
  if (NormalizedVT.isExternalized()) {
131
    // An externalized reference must appear as an 'externref' to the matcher.
132
    // We preserve the nullability (Ref vs RefNull).
133
0
    NormalizedVT =
134
0
        ValType(VT.isNullableRefType() ? TypeCode::RefNull : TypeCode::Ref,
135
0
                TypeCode::ExternRef);
136
0
  }
137
138
0
  bool MatchResult = AST::TypeMatcher::matchType(ModInst->getTypeList(),
139
0
                                                 Instr.getBrCast().RType2,
140
0
                                                 GotTypeList, NormalizedVT);
141
0
  if (MatchResult != IsReverse) {
142
0
    return branchToLabel(StackMgr, Instr.getBrCast().Jump, PC);
143
0
  }
144
0
  return {};
145
0
}
146
147
Expect<void> Executor::runReturnOp(Runtime::StackManager &StackMgr,
148
0
                                   AST::InstrView::iterator &PC) noexcept {
149
  // Check stop token
150
0
  if (unlikely(StopToken.exchange(0, std::memory_order_relaxed))) {
151
0
    spdlog::error(ErrCode::Value::Interrupted);
152
0
    return Unexpect(ErrCode::Value::Interrupted);
153
0
  }
154
0
  PC = StackMgr.popFrame();
155
0
  return {};
156
0
}
157
158
Expect<void> Executor::runCallOp(Runtime::StackManager &StackMgr,
159
                                 const AST::Instruction &Instr,
160
                                 AST::InstrView::iterator &PC,
161
0
                                 bool IsTailCall) noexcept {
162
  // Get Function address.
163
0
  const auto *FuncInst = getFuncInstByIdx(StackMgr, Instr.getTargetIndex());
164
0
  EXPECTED_TRY(auto NextPC,
165
0
               enterFunction(StackMgr, *FuncInst, PC + 1, IsTailCall));
166
0
  PC = NextPC - 1;
167
0
  return {};
168
0
}
169
170
Expect<void> Executor::runCallRefOp(Runtime::StackManager &StackMgr,
171
                                    const AST::Instruction &Instr,
172
                                    AST::InstrView::iterator &PC,
173
0
                                    bool IsTailCall) noexcept {
174
0
  const auto Ref = StackMgr.pop().get<RefVariant>();
175
0
  const auto *FuncInst = retrieveFuncRef(Ref);
176
0
  if (FuncInst == nullptr) {
177
0
    spdlog::error(ErrCode::Value::AccessNullFunc);
178
0
    spdlog::error(
179
0
        ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset()));
180
0
    return Unexpect(ErrCode::Value::AccessNullFunc);
181
0
  }
182
183
  // Get Function address.
184
0
  EXPECTED_TRY(auto NextPC,
185
0
               enterFunction(StackMgr, *FuncInst, PC + 1, IsTailCall));
186
0
  PC = NextPC - 1;
187
0
  return {};
188
0
}
189
190
Expect<void> Executor::runCallIndirectOp(Runtime::StackManager &StackMgr,
191
                                         const AST::Instruction &Instr,
192
                                         AST::InstrView::iterator &PC,
193
0
                                         bool IsTailCall) noexcept {
194
  // Get Table Instance.
195
0
  const auto *TabInst = getTabInstByIdx(StackMgr, Instr.getSourceIndex());
196
197
  // Get function type at index x.
198
0
  const auto *ModInst = StackMgr.getModule();
199
0
  const auto &ExpDefType = *ModInst->unsafeGetType(Instr.getTargetIndex());
200
201
  // Pop the value of index from the Stack.
202
0
  const auto AddrType = TabInst->getTableType().getLimit().getAddrType();
203
0
  uint64_t Idx = extractAddr(StackMgr.pop(), AddrType);
204
205
  // If idx not small than tab.elem, trap.
206
0
  if (Idx >= TabInst->getSize()) {
207
0
    spdlog::error(ErrCode::Value::UndefinedElement);
208
0
    spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset(),
209
0
                                           {Idx},
210
0
                                           {ValTypeFromType<uint32_t>()}));
211
0
    return Unexpect(ErrCode::Value::UndefinedElement);
212
0
  }
213
214
  // Get function address. The bound is guaranteed.
215
0
  RefVariant Ref = *TabInst->getRefAddr(Idx);
216
0
  const auto *FuncInst = retrieveFuncRef(Ref);
217
0
  if (FuncInst == nullptr) {
218
0
    spdlog::error(ErrCode::Value::UninitializedElement);
219
0
    spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset(),
220
0
                                           {Idx},
221
0
                                           {ValTypeFromType<uint32_t>()}));
222
0
    return Unexpect(ErrCode::Value::UninitializedElement);
223
0
  }
224
225
  // Check function type.
226
0
  bool IsMatch = false;
227
0
  if (FuncInst->getModule()) {
228
0
    IsMatch = AST::TypeMatcher::matchType(
229
0
        ModInst->getTypeList(), *ExpDefType.getTypeIndex(),
230
0
        FuncInst->getModule()->getTypeList(), FuncInst->getTypeIndex());
231
0
  } else {
232
    // Independent host module instance case. Matching the composite type
233
    // directly.
234
0
    IsMatch = AST::TypeMatcher::matchType(
235
0
        ModInst->getTypeList(), ExpDefType.getCompositeType(),
236
0
        FuncInst->getHostFunc().getDefinedType().getCompositeType());
237
0
  }
238
0
  if (!IsMatch) {
239
0
    auto &ExpFuncType = ExpDefType.getCompositeType().getFuncType();
240
0
    auto &GotFuncType = FuncInst->getFuncType();
241
0
    spdlog::error(ErrCode::Value::IndirectCallTypeMismatch);
242
0
    spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset(),
243
0
                                           {Idx},
244
0
                                           {ValTypeFromType<uint32_t>()}));
245
0
    spdlog::error(ErrInfo::InfoMismatch(
246
0
        ExpFuncType.getParamTypes(), ExpFuncType.getReturnTypes(),
247
0
        GotFuncType.getParamTypes(), GotFuncType.getReturnTypes()));
248
0
    return Unexpect(ErrCode::Value::IndirectCallTypeMismatch);
249
0
  }
250
251
  // Enter the function.
252
0
  EXPECTED_TRY(auto NextPC,
253
0
               enterFunction(StackMgr, *FuncInst, PC + 1, IsTailCall));
254
0
  PC = NextPC - 1;
255
0
  return {};
256
0
}
257
258
Expect<void> Executor::runTryTableOp(Runtime::StackManager &StackMgr,
259
                                     const AST::Instruction &Instr,
260
0
                                     AST::InstrView::iterator &PC) noexcept {
261
0
  const auto &TryDesc = Instr.getTryCatch();
262
0
  StackMgr.pushHandler(PC, TryDesc.BlockParamNum, TryDesc.Catch);
263
0
  return {};
264
0
}
265
266
} // namespace Executor
267
} // namespace WasmEdge