/src/WasmEdge/lib/executor/engine/controlInstr.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: 2019-2024 Second State INC |
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 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 | | // Have else-statement case. Jump to 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 args will be popped from 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 | auto *TagInst = Ref.getPtr<Runtime::Instance::TagInstance>(); |
55 | 0 | return throwException(StackMgr, *TagInst, PC); |
56 | 0 | } |
57 | | |
58 | | Expect<void> Executor::runBrOp(Runtime::StackManager &StackMgr, |
59 | | const AST::Instruction &Instr, |
60 | 0 | AST::InstrView::iterator &PC) noexcept { |
61 | 0 | return branchToLabel(StackMgr, Instr.getJump(), PC); |
62 | 0 | } |
63 | | |
64 | | Expect<void> Executor::runBrIfOp(Runtime::StackManager &StackMgr, |
65 | | const AST::Instruction &Instr, |
66 | 0 | AST::InstrView::iterator &PC) noexcept { |
67 | 0 | if (StackMgr.pop().get<uint32_t>() != 0) { |
68 | 0 | return runBrOp(StackMgr, Instr, PC); |
69 | 0 | } |
70 | 0 | return {}; |
71 | 0 | } |
72 | | |
73 | | Expect<void> Executor::runBrOnNullOp(Runtime::StackManager &StackMgr, |
74 | | const AST::Instruction &Instr, |
75 | 0 | AST::InstrView::iterator &PC) noexcept { |
76 | 0 | if (StackMgr.getTop().get<RefVariant>().isNull()) { |
77 | 0 | StackMgr.pop(); |
78 | 0 | return runBrOp(StackMgr, Instr, PC); |
79 | 0 | } |
80 | 0 | return {}; |
81 | 0 | } |
82 | | |
83 | | Expect<void> Executor::runBrOnNonNullOp(Runtime::StackManager &StackMgr, |
84 | | const AST::Instruction &Instr, |
85 | 0 | AST::InstrView::iterator &PC) noexcept { |
86 | 0 | if (!StackMgr.getTop().get<RefVariant>().isNull()) { |
87 | 0 | return runBrOp(StackMgr, Instr, PC); |
88 | 0 | } |
89 | 0 | StackMgr.pop(); |
90 | 0 | return {}; |
91 | 0 | } |
92 | | |
93 | | Expect<void> Executor::runBrTableOp(Runtime::StackManager &StackMgr, |
94 | | const AST::Instruction &Instr, |
95 | 0 | AST::InstrView::iterator &PC) noexcept { |
96 | | // Get value on top of stack. |
97 | 0 | uint32_t Value = StackMgr.pop().get<uint32_t>(); |
98 | | |
99 | | // Do branch. |
100 | 0 | auto LabelTable = Instr.getLabelList(); |
101 | 0 | const auto LabelTableSize = static_cast<uint32_t>(LabelTable.size() - 1); |
102 | 0 | if (Value < LabelTableSize) { |
103 | 0 | return branchToLabel(StackMgr, LabelTable[Value], PC); |
104 | 0 | } |
105 | 0 | return branchToLabel(StackMgr, LabelTable[LabelTableSize], PC); |
106 | 0 | } |
107 | | |
108 | | Expect<void> Executor::runBrOnCastOp(Runtime::StackManager &StackMgr, |
109 | | const AST::Instruction &Instr, |
110 | | AST::InstrView::iterator &PC, |
111 | 0 | bool IsReverse) noexcept { |
112 | | // Get value on top of stack. |
113 | 0 | const auto *ModInst = StackMgr.getModule(); |
114 | 0 | const auto &Val = StackMgr.getTop().get<RefVariant>(); |
115 | 0 | const auto &VT = Val.getType(); |
116 | 0 | Span<const AST::SubType *const> GotTypeList = ModInst->getTypeList(); |
117 | 0 | if (!VT.isAbsHeapType()) { |
118 | 0 | auto *Inst = Val.getPtr<Runtime::Instance::CompositeBase>(); |
119 | | // Reference must not be nullptr here because the null references are typed |
120 | | // with the least abstract heap type. |
121 | 0 | if (Inst->getModule()) { |
122 | 0 | GotTypeList = Inst->getModule()->getTypeList(); |
123 | 0 | } |
124 | 0 | } |
125 | |
|
126 | 0 | if (AST::TypeMatcher::matchType(ModInst->getTypeList(), |
127 | 0 | Instr.getBrCast().RType2, GotTypeList, |
128 | 0 | VT) != IsReverse) { |
129 | 0 | return branchToLabel(StackMgr, Instr.getBrCast().Jump, PC); |
130 | 0 | } |
131 | 0 | return {}; |
132 | 0 | } |
133 | | |
134 | | Expect<void> Executor::runReturnOp(Runtime::StackManager &StackMgr, |
135 | 0 | AST::InstrView::iterator &PC) noexcept { |
136 | | // Check stop token |
137 | 0 | if (unlikely(StopToken.exchange(0, std::memory_order_relaxed))) { |
138 | 0 | spdlog::error(ErrCode::Value::Interrupted); |
139 | 0 | return Unexpect(ErrCode::Value::Interrupted); |
140 | 0 | } |
141 | 0 | PC = StackMgr.popFrame(); |
142 | 0 | return {}; |
143 | 0 | } |
144 | | |
145 | | Expect<void> Executor::runCallOp(Runtime::StackManager &StackMgr, |
146 | | const AST::Instruction &Instr, |
147 | | AST::InstrView::iterator &PC, |
148 | 0 | bool IsTailCall) noexcept { |
149 | | // Get Function address. |
150 | 0 | const auto *FuncInst = getFuncInstByIdx(StackMgr, Instr.getTargetIndex()); |
151 | 0 | EXPECTED_TRY(auto NextPC, |
152 | 0 | enterFunction(StackMgr, *FuncInst, PC + 1, IsTailCall)); |
153 | 0 | PC = NextPC - 1; |
154 | 0 | return {}; |
155 | 0 | } |
156 | | |
157 | | Expect<void> Executor::runCallRefOp(Runtime::StackManager &StackMgr, |
158 | | const AST::Instruction &Instr, |
159 | | AST::InstrView::iterator &PC, |
160 | 0 | bool IsTailCall) noexcept { |
161 | 0 | const auto Ref = StackMgr.pop().get<RefVariant>(); |
162 | 0 | if (Ref.isNull()) { |
163 | 0 | spdlog::error(ErrCode::Value::AccessNullFunc); |
164 | 0 | spdlog::error( |
165 | 0 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
166 | 0 | return Unexpect(ErrCode::Value::AccessNullFunc); |
167 | 0 | } |
168 | | |
169 | | // Get Function address. |
170 | 0 | const auto *FuncInst = retrieveFuncRef(Ref); |
171 | 0 | EXPECTED_TRY(auto NextPC, |
172 | 0 | enterFunction(StackMgr, *FuncInst, PC + 1, IsTailCall)); |
173 | 0 | PC = NextPC - 1; |
174 | 0 | return {}; |
175 | 0 | } |
176 | | |
177 | | Expect<void> Executor::runCallIndirectOp(Runtime::StackManager &StackMgr, |
178 | | const AST::Instruction &Instr, |
179 | | AST::InstrView::iterator &PC, |
180 | 0 | bool IsTailCall) noexcept { |
181 | | // Get Table Instance |
182 | 0 | const auto *TabInst = getTabInstByIdx(StackMgr, Instr.getSourceIndex()); |
183 | | |
184 | | // Get function type at index x. |
185 | 0 | const auto *ModInst = StackMgr.getModule(); |
186 | 0 | const auto &ExpDefType = **ModInst->getType(Instr.getTargetIndex()); |
187 | | |
188 | | // Pop the value i32.const i from the Stack. |
189 | 0 | uint32_t Idx = StackMgr.pop().get<uint32_t>(); |
190 | | |
191 | | // If idx not small than tab.elem, trap. |
192 | 0 | if (Idx >= TabInst->getSize()) { |
193 | 0 | spdlog::error(ErrCode::Value::UndefinedElement); |
194 | 0 | spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset(), |
195 | 0 | {Idx}, |
196 | 0 | {ValTypeFromType<uint32_t>()})); |
197 | 0 | return Unexpect(ErrCode::Value::UndefinedElement); |
198 | 0 | } |
199 | | |
200 | | // Get function address. The bound is guaranteed. |
201 | 0 | RefVariant Ref = *TabInst->getRefAddr(Idx); |
202 | 0 | if (Ref.isNull()) { |
203 | 0 | spdlog::error(ErrCode::Value::UninitializedElement); |
204 | 0 | spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset(), |
205 | 0 | {Idx}, |
206 | 0 | {ValTypeFromType<uint32_t>()})); |
207 | 0 | return Unexpect(ErrCode::Value::UninitializedElement); |
208 | 0 | } |
209 | | |
210 | | // Check function type. |
211 | 0 | const auto *FuncInst = retrieveFuncRef(Ref); |
212 | 0 | bool IsMatch = false; |
213 | 0 | if (FuncInst->getModule()) { |
214 | 0 | IsMatch = AST::TypeMatcher::matchType( |
215 | 0 | ModInst->getTypeList(), *ExpDefType.getTypeIndex(), |
216 | 0 | FuncInst->getModule()->getTypeList(), FuncInst->getTypeIndex()); |
217 | 0 | } else { |
218 | | // Independent host module instance case. Matching the composite type |
219 | | // directly. |
220 | 0 | IsMatch = AST::TypeMatcher::matchType( |
221 | 0 | ModInst->getTypeList(), ExpDefType.getCompositeType(), |
222 | 0 | FuncInst->getHostFunc().getDefinedType().getCompositeType()); |
223 | 0 | } |
224 | 0 | if (!IsMatch) { |
225 | 0 | auto &ExpFuncType = ExpDefType.getCompositeType().getFuncType(); |
226 | 0 | auto &GotFuncType = FuncInst->getFuncType(); |
227 | 0 | spdlog::error(ErrCode::Value::IndirectCallTypeMismatch); |
228 | 0 | spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset(), |
229 | 0 | {Idx}, |
230 | 0 | {ValTypeFromType<uint32_t>()})); |
231 | 0 | spdlog::error(ErrInfo::InfoMismatch( |
232 | 0 | ExpFuncType.getParamTypes(), ExpFuncType.getReturnTypes(), |
233 | 0 | GotFuncType.getParamTypes(), GotFuncType.getReturnTypes())); |
234 | 0 | return Unexpect(ErrCode::Value::IndirectCallTypeMismatch); |
235 | 0 | } |
236 | | |
237 | | // Enter the function. |
238 | 0 | EXPECTED_TRY(auto NextPC, |
239 | 0 | enterFunction(StackMgr, *FuncInst, PC + 1, IsTailCall)); |
240 | 0 | PC = NextPC - 1; |
241 | 0 | return {}; |
242 | 0 | } |
243 | | |
244 | | Expect<void> Executor::runTryTableOp(Runtime::StackManager &StackMgr, |
245 | | const AST::Instruction &Instr, |
246 | 0 | AST::InstrView::iterator &PC) noexcept { |
247 | 0 | const auto &TryDesc = Instr.getTryCatch(); |
248 | 0 | StackMgr.pushHandler(PC, TryDesc.BlockParamNum, TryDesc.Catch); |
249 | 0 | return {}; |
250 | 0 | } |
251 | | |
252 | | } // namespace Executor |
253 | | } // namespace WasmEdge |