/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.getModule(), 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 &Val = StackMgr.getTop().get<RefVariant>(); |
117 | 0 | if (matchRef(StackMgr.getModule(), Instr.getBrCast().RType2, Val) != |
118 | 0 | IsReverse) { |
119 | 0 | return branchToLabel(StackMgr, Instr.getBrCast().Jump, PC); |
120 | 0 | } |
121 | 0 | return {}; |
122 | 0 | } |
123 | | |
124 | | Expect<void> Executor::runReturnOp(Runtime::StackManager &StackMgr, |
125 | 0 | AST::InstrView::iterator &PC) noexcept { |
126 | | // Check stop token |
127 | 0 | if (unlikely(StopToken.exchange(0, std::memory_order_relaxed))) { |
128 | 0 | spdlog::error(ErrCode::Value::Interrupted); |
129 | 0 | return Unexpect(ErrCode::Value::Interrupted); |
130 | 0 | } |
131 | 0 | PC = StackMgr.popFrame(); |
132 | 0 | return {}; |
133 | 0 | } |
134 | | |
135 | | Expect<void> Executor::runCallOp(Runtime::StackManager &StackMgr, |
136 | | const AST::Instruction &Instr, |
137 | | AST::InstrView::iterator &PC, |
138 | 0 | bool IsTailCall) noexcept { |
139 | | // Get Function address. |
140 | 0 | const auto *FuncInst = |
141 | 0 | getFuncInstByIdx(StackMgr.getModule(), Instr.getTargetIndex()); |
142 | 0 | EXPECTED_TRY(auto NextPC, |
143 | 0 | enterFunction(StackMgr, *FuncInst, PC + 1, IsTailCall)); |
144 | 0 | PC = NextPC - 1; |
145 | 0 | return {}; |
146 | 0 | } |
147 | | |
148 | | Expect<void> Executor::runCallRefOp(Runtime::StackManager &StackMgr, |
149 | | const AST::Instruction &Instr, |
150 | | AST::InstrView::iterator &PC, |
151 | 0 | bool IsTailCall) noexcept { |
152 | 0 | const auto Ref = StackMgr.pop().get<RefVariant>(); |
153 | 0 | const auto *FuncInst = retrieveFuncRef(Ref); |
154 | 0 | if (FuncInst == nullptr) { |
155 | 0 | spdlog::error(ErrCode::Value::AccessNullFunc); |
156 | 0 | spdlog::error( |
157 | 0 | ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset())); |
158 | 0 | return Unexpect(ErrCode::Value::AccessNullFunc); |
159 | 0 | } |
160 | | |
161 | | // Get Function address. |
162 | 0 | EXPECTED_TRY(auto NextPC, |
163 | 0 | enterFunction(StackMgr, *FuncInst, PC + 1, IsTailCall)); |
164 | 0 | PC = NextPC - 1; |
165 | 0 | return {}; |
166 | 0 | } |
167 | | |
168 | | Expect<void> Executor::runCallIndirectOp(Runtime::StackManager &StackMgr, |
169 | | const AST::Instruction &Instr, |
170 | | AST::InstrView::iterator &PC, |
171 | 0 | bool IsTailCall) noexcept { |
172 | | // Get Table Instance. |
173 | 0 | const auto *TabInst = |
174 | 0 | getTabInstByIdx(StackMgr.getModule(), Instr.getSourceIndex()); |
175 | | |
176 | | // Get function type at index x. |
177 | 0 | const auto *ModInst = StackMgr.getModule(); |
178 | 0 | const auto &ExpDefType = *ModInst->unsafeGetType(Instr.getTargetIndex()); |
179 | | |
180 | | // Pop the value of index from the Stack. |
181 | 0 | const auto AddrType = TabInst->getTableType().getLimit().getAddrType(); |
182 | 0 | uint64_t Idx = extractAddr(StackMgr.pop(), AddrType); |
183 | | |
184 | | // If idx not small than tab.elem, trap. |
185 | 0 | if (Idx >= TabInst->getSize()) { |
186 | 0 | spdlog::error(ErrCode::Value::UndefinedElement); |
187 | 0 | spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset(), |
188 | 0 | {Idx}, |
189 | 0 | {ValTypeFromAddressType(AddrType)})); |
190 | 0 | return Unexpect(ErrCode::Value::UndefinedElement); |
191 | 0 | } |
192 | | |
193 | | // Get function address. The bound is guaranteed. |
194 | 0 | RefVariant Ref = *TabInst->getRefAddr(Idx); |
195 | 0 | const auto *FuncInst = retrieveFuncRef(Ref); |
196 | 0 | if (FuncInst == nullptr) { |
197 | 0 | spdlog::error(ErrCode::Value::UninitializedElement); |
198 | 0 | spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset(), |
199 | 0 | {Idx}, |
200 | 0 | {ValTypeFromAddressType(AddrType)})); |
201 | 0 | return Unexpect(ErrCode::Value::UninitializedElement); |
202 | 0 | } |
203 | | |
204 | | // Check function type. |
205 | 0 | if (!matchRef(ModInst, ValType(TypeCode::Ref, Instr.getTargetIndex()), Ref)) { |
206 | 0 | auto &ExpFuncType = ExpDefType.getCompositeType().getFuncType(); |
207 | 0 | auto &GotFuncType = FuncInst->getFuncType(); |
208 | 0 | spdlog::error(ErrCode::Value::IndirectCallTypeMismatch); |
209 | 0 | spdlog::error(ErrInfo::InfoInstruction(Instr.getOpCode(), Instr.getOffset(), |
210 | 0 | {Idx}, |
211 | 0 | {ValTypeFromAddressType(AddrType)})); |
212 | 0 | spdlog::error(ErrInfo::InfoMismatch( |
213 | 0 | ExpFuncType.getParamTypes(), ExpFuncType.getReturnTypes(), |
214 | 0 | GotFuncType.getParamTypes(), GotFuncType.getReturnTypes())); |
215 | 0 | return Unexpect(ErrCode::Value::IndirectCallTypeMismatch); |
216 | 0 | } |
217 | | |
218 | | // Enter the function. |
219 | 0 | EXPECTED_TRY(auto NextPC, |
220 | 0 | enterFunction(StackMgr, *FuncInst, PC + 1, IsTailCall)); |
221 | 0 | PC = NextPC - 1; |
222 | 0 | return {}; |
223 | 0 | } |
224 | | |
225 | | Expect<void> Executor::runTryTableOp(Runtime::StackManager &StackMgr, |
226 | | const AST::Instruction &Instr, |
227 | 0 | AST::InstrView::iterator &PC) noexcept { |
228 | 0 | const auto &TryDesc = Instr.getTryCatch(); |
229 | 0 | StackMgr.pushHandler(PC, TryDesc.BlockParamNum, TryDesc.Catch); |
230 | 0 | return {}; |
231 | 0 | } |
232 | | |
233 | | } // namespace Executor |
234 | | } // namespace WasmEdge |