/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 |