/src/WasmEdge/lib/validator/component_canon.cpp
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: Copyright The WasmEdge Authors |
3 | | |
4 | | //===-- component_canon.cpp - Canonical built-in validation ---------------===// |
5 | | // |
6 | | // Validation of canon lift / lower / resource.* definitions. |
7 | | // |
8 | | //===----------------------------------------------------------------------===// |
9 | | |
10 | | #include "common/errinfo.h" |
11 | | #include "common/spdlog.h" |
12 | | #include "validator/validator.h" |
13 | | |
14 | | #include <vector> |
15 | | |
16 | | namespace WasmEdge { |
17 | | namespace Validator { |
18 | | |
19 | | using namespace std::literals; |
20 | | |
21 | | // canon ::= lift | lower | resource.* | async built-in, one switch per opcode. |
22 | | Expect<void> |
23 | 254k | Validator::validate(const AST::Component::Canonical &Canon) noexcept { |
24 | 254k | auto &S = CompCtx.getTop(); |
25 | 254k | const ValType I32V{TypeCode::I32}; |
26 | 254k | const ValType I64V{TypeCode::I64}; |
27 | | |
28 | | // Defines the core function a canonical definition lowers to. |
29 | 254k | auto PushCoreFunc = [&](std::vector<ValType> Params, |
30 | 254k | std::vector<ValType> Results) -> Expect<void> { |
31 | 244k | S.addCoreFunc(CompCtx.addCoreFuncType(Params, Results)); |
32 | 244k | return {}; |
33 | 244k | }; |
34 | | // A built-in takes its core type from the shared table, keyed on Addr. |
35 | 254k | auto PushBuiltin = [&](const ValType &Addr) -> Expect<void> { |
36 | 233k | auto [Params, Results] = AST::Component::Canonical::getBuiltinCoreFuncType( |
37 | 233k | Canon.getOpCode(), Addr); |
38 | 233k | return PushCoreFunc(std::move(Params), std::move(Results)); |
39 | 233k | }; |
40 | | |
41 | | // The resource built-ins take no options and name a resource type. |
42 | 254k | auto GetResourceEntry = |
43 | 254k | [&](std::string_view What) -> Expect<const Component::TypeEntry *> { |
44 | 1.52k | if (!Canon.getOptions().empty()) { |
45 | 0 | spdlog::error(ErrCode::Value::CanonOptionOnBuiltin); |
46 | 0 | spdlog::error(" resource built-ins take no canonical options."sv); |
47 | 0 | return Unexpect(ErrCode::Value::CanonOptionOnBuiltin); |
48 | 0 | } |
49 | 1.52k | const auto *Entry = S.getType(Canon.getIndex()); |
50 | 1.52k | if (Entry == nullptr) { |
51 | 29 | spdlog::error(ErrCode::Value::ComponentTypeIndexOutOfBounds); |
52 | 29 | spdlog::error(" {} type index {} out of bounds."sv, What, |
53 | 29 | Canon.getIndex()); |
54 | 29 | return Unexpect(ErrCode::Value::ComponentTypeIndexOutOfBounds); |
55 | 29 | } |
56 | 1.49k | if (!Entry->isResource()) { |
57 | 5 | spdlog::error(ErrCode::Value::ComponentNotResourceType); |
58 | 5 | spdlog::error(" {} type index {} is not a resource."sv, What, |
59 | 5 | Canon.getIndex()); |
60 | 5 | return Unexpect(ErrCode::Value::ComponentNotResourceType); |
61 | 5 | } |
62 | 1.48k | return Entry; |
63 | 1.49k | }; |
64 | | |
65 | | // resource.new / resource.rep need a locally-defined resource and its rep. |
66 | 254k | auto LocalResourceRep = [&](std::string_view What) -> Expect<ValType> { |
67 | 1.44k | EXPECTED_TRY(const auto *Entry, GetResourceEntry(What)); |
68 | 1.41k | const auto &Res = CompCtx.getResource(*Entry->ResourceId); |
69 | 1.41k | if (Res.RT == nullptr || Res.Origin != &S) { |
70 | 2 | spdlog::error(ErrCode::Value::ComponentNotLocalResource); |
71 | 2 | spdlog::error(" {} requires a locally-defined resource type."sv, What); |
72 | 2 | return Unexpect(ErrCode::Value::ComponentNotLocalResource); |
73 | 2 | } |
74 | 1.41k | return Res.RT->isAddrI64() ? I64V : I32V; |
75 | 1.41k | }; |
76 | | |
77 | | // stream/future built-ins name their element type in the type immediate. |
78 | 254k | auto CheckTypeImmediate = |
79 | 254k | [&](bool WantStream) -> Expect<const Component::TypeEntry *> { |
80 | 184k | const auto *Entry = S.getType(Canon.getIndex()); |
81 | 184k | const auto *Def = Entry != nullptr ? Entry->getDefValType() : nullptr; |
82 | 184k | const bool Ok = |
83 | 184k | Def != nullptr && (WantStream ? Def->isStreamTy() : Def->isFutureTy()); |
84 | 184k | if (!Ok) { |
85 | 82 | const auto Code = WantStream |
86 | 82 | ? ErrCode::Value::ComponentStreamTypeRequired |
87 | 82 | : ErrCode::Value::ComponentFutureTypeRequired; |
88 | 82 | spdlog::error(Code); |
89 | 82 | spdlog::error(" `{}` type index {} does not refer to a {} type."sv, |
90 | 82 | ComponentCanonOpCodeStr[Canon.getOpCode()], |
91 | 82 | Canon.getIndex(), WantStream ? "stream"sv : "future"sv); |
92 | 82 | return Unexpect(Code); |
93 | 82 | } |
94 | 184k | return Entry; |
95 | 184k | }; |
96 | | |
97 | | // Option-bearing built-ins are lowerings: no post-return or callback. |
98 | 254k | auto BuiltinOptions = [&](bool NeedMemory, bool NeedRealloc) -> Expect<void> { |
99 | 1.32k | EXPECTED_TRY(CompCtx.checkOptions(Canon, false)); |
100 | 1.31k | return CompCtx.checkRequiredOptions(Canon, NeedMemory, NeedRealloc, |
101 | 1.31k | "this canonical built-in"sv); |
102 | 1.32k | }; |
103 | | |
104 | | // Built-ins the spec restricts to synchronous use. |
105 | 254k | auto RequireSync = [&]() -> Expect<void> { |
106 | 3.16k | if (Canon.hasOption(ComponentCanonOptCode::Async)) { |
107 | 3 | spdlog::error(ErrCode::Value::CanonAsyncOnBuiltin); |
108 | 3 | spdlog::error(" this canonical built-in is always synchronous."sv); |
109 | 3 | return Unexpect(ErrCode::Value::CanonAsyncOnBuiltin); |
110 | 3 | } |
111 | 3.15k | return {}; |
112 | 3.16k | }; |
113 | | |
114 | 254k | bool ParamsNeedMemory = false, ResultsNeedMemory = false; |
115 | | |
116 | 254k | switch (Canon.getOpCode()) { |
117 | 10.0k | case ComponentCanonOpCode::Lift: { |
118 | 10.0k | const auto *Entry = S.getType(Canon.getTargetIndex()); |
119 | 10.0k | if (Entry == nullptr) { |
120 | 6 | spdlog::error(ErrCode::Value::ComponentTypeIndexOutOfBounds); |
121 | 6 | spdlog::error(" canon lift type index {} out of bounds."sv, |
122 | 6 | Canon.getTargetIndex()); |
123 | 6 | return Unexpect(ErrCode::Value::ComponentTypeIndexOutOfBounds); |
124 | 6 | } |
125 | 10.0k | if (Entry->getFuncType() == nullptr) { |
126 | 3 | spdlog::error(ErrCode::Value::ComponentNotFunctionType); |
127 | 3 | spdlog::error(" canon lift type index {} is not a function type."sv, |
128 | 3 | Canon.getTargetIndex()); |
129 | 3 | return Unexpect(ErrCode::Value::ComponentNotFunctionType); |
130 | 3 | } |
131 | 10.0k | const Component::FuncInfo FI{Entry->getFuncType(), Entry->Home, |
132 | 10.0k | Entry->Remap}; |
133 | 10.0k | EXPECTED_TRY(CompCtx.checkOptions(Canon, true)); |
134 | 10.0k | const bool AsyncOpt = Canon.hasOption(ComponentCanonOptCode::Async); |
135 | 10.0k | if (AsyncOpt && !FI.FT->isAsync()) { |
136 | 1 | spdlog::error(ErrCode::Value::CanonAsyncRequiresAsyncType); |
137 | 1 | spdlog::error(" canon lift with `async` needs `(func async ...)`."sv); |
138 | 1 | return Unexpect(ErrCode::Value::CanonAsyncRequiresAsyncType); |
139 | 1 | } |
140 | 10.0k | if (Canon.hasOption(ComponentCanonOptCode::Callback) && !AsyncOpt) { |
141 | 0 | spdlog::error(ErrCode::Value::CanonCallbackRequiresAsync); |
142 | 0 | spdlog::error(" the `callback` option requires the `async` option."sv); |
143 | 0 | return Unexpect(ErrCode::Value::CanonCallbackRequiresAsync); |
144 | 0 | } |
145 | 10.0k | if (AsyncOpt && Canon.hasOption(ComponentCanonOptCode::PostReturn)) { |
146 | 1 | spdlog::error(ErrCode::Value::CanonPostReturnWithAsync); |
147 | 1 | spdlog::error(" cannot specify post-return function in combination " |
148 | 1 | "with async."sv); |
149 | 1 | return Unexpect(ErrCode::Value::CanonPostReturnWithAsync); |
150 | 1 | } |
151 | | |
152 | 10.0k | const ValType Ptr = CompCtx.getCanonPtrType(Canon); |
153 | 10.0k | EXPECTED_TRY(auto Sig, CompCtx.flattenFuncType(FI, Ptr, ParamsNeedMemory, |
154 | 10.0k | ResultsNeedMemory)); |
155 | 10.0k | const bool ParamsIndirect = |
156 | 10.0k | Sig.getParamTypes().size() > Component::Context::MaxFlatParams; |
157 | 10.0k | const bool ResultsIndirect = |
158 | 10.0k | Sig.getReturnTypes().size() > Component::Context::MaxFlatResults; |
159 | 10.0k | if (ParamsIndirect) { |
160 | 12 | Sig.getParamTypes().assign(1, Ptr); |
161 | 12 | } |
162 | 10.0k | if (AsyncOpt) { |
163 | | // Async lift returns the packed callback code, or nothing when stackful. |
164 | 512 | Sig.getReturnTypes().clear(); |
165 | 512 | if (Canon.hasOption(ComponentCanonOptCode::Callback)) { |
166 | 0 | Sig.getReturnTypes().push_back(I32V); |
167 | 0 | } |
168 | 9.52k | } else if (ResultsIndirect) { |
169 | 14 | Sig.getReturnTypes().assign(1, Ptr); |
170 | 14 | } |
171 | | // Required options: lifting params lowers them into the callee's memory. |
172 | 10.0k | EXPECTED_TRY(CompCtx.checkRequiredOptions( |
173 | 10.0k | Canon, |
174 | 10.0k | ParamsNeedMemory || ParamsIndirect || |
175 | 10.0k | (!AsyncOpt && (ResultsNeedMemory || ResultsIndirect)), |
176 | 10.0k | ParamsNeedMemory || ParamsIndirect, "canon lift"sv)); |
177 | | |
178 | | // The callee must have exactly the flattened core type. |
179 | 10.0k | const auto *Callee = S.getCoreFunc(Canon.getIndex()); |
180 | 10.0k | if (Callee == nullptr) { |
181 | 8 | spdlog::error(ErrCode::Value::ComponentFunctionIndexOutOfBounds); |
182 | 8 | spdlog::error(" canon lift core function index {} out of bounds."sv, |
183 | 8 | Canon.getIndex()); |
184 | 8 | return Unexpect(ErrCode::Value::ComponentFunctionIndexOutOfBounds); |
185 | 8 | } |
186 | 10.0k | const auto &CalleeType = Callee->getCompositeType(); |
187 | 10.0k | if (!CalleeType.isFunc() || |
188 | 10.0k | CalleeType.getFuncType().getParamTypes() != Sig.getParamTypes()) { |
189 | 1 | spdlog::error(ErrCode::Value::CanonLoweredParamsMismatch); |
190 | 1 | spdlog::error(" canon lift core function does not match the " |
191 | 1 | "flattened parameters."sv); |
192 | 1 | return Unexpect(ErrCode::Value::CanonLoweredParamsMismatch); |
193 | 1 | } |
194 | 10.0k | if (CalleeType.getFuncType().getReturnTypes() != Sig.getReturnTypes()) { |
195 | 2 | spdlog::error(ErrCode::Value::CanonLoweredResultsMismatch); |
196 | 2 | spdlog::error(" canon lift core function does not match the " |
197 | 2 | "flattened results."sv); |
198 | 2 | return Unexpect(ErrCode::Value::CanonLoweredResultsMismatch); |
199 | 2 | } |
200 | | |
201 | | // post-return has type (func (param flat_results)). |
202 | 10.0k | for (const auto &Opt : Canon.getOptions()) { |
203 | 1.88k | if (Opt.getCode() != ComponentCanonOptCode::PostReturn) { |
204 | 812 | continue; |
205 | 812 | } |
206 | 1.07k | const auto *Post = S.getCoreFunc(Opt.getIndex()); |
207 | 1.07k | if (Post == nullptr) { |
208 | 3 | spdlog::error(ErrCode::Value::ComponentFunctionIndexOutOfBounds); |
209 | 3 | spdlog::error(" post-return core function index {} out of bounds."sv, |
210 | 3 | Opt.getIndex()); |
211 | 3 | return Unexpect(ErrCode::Value::ComponentFunctionIndexOutOfBounds); |
212 | 3 | } |
213 | 1.06k | const auto &PT = Post->getCompositeType(); |
214 | 1.06k | if (!PT.isFunc() || |
215 | 1.06k | PT.getFuncType().getParamTypes() != Sig.getReturnTypes() || |
216 | 1.06k | !PT.getFuncType().getReturnTypes().empty()) { |
217 | 1 | spdlog::error(ErrCode::Value::CanonPostReturnSignature); |
218 | 1 | spdlog::error( |
219 | 1 | " post-return must take the lifted core results and return " |
220 | 1 | "nothing."sv); |
221 | 1 | return Unexpect(ErrCode::Value::CanonPostReturnSignature); |
222 | 1 | } |
223 | 1.06k | } |
224 | | |
225 | 10.0k | S.addFunc(FI); |
226 | 10.0k | return {}; |
227 | 10.0k | } |
228 | | |
229 | 2.91k | case ComponentCanonOpCode::Lower: { |
230 | 2.91k | const auto *FI = S.getFunc(Canon.getIndex()); |
231 | 2.91k | if (FI == nullptr) { |
232 | 20 | spdlog::error(ErrCode::Value::ComponentFunctionIndexOutOfBounds); |
233 | 20 | spdlog::error(" canon lower function index {} out of bounds."sv, |
234 | 20 | Canon.getIndex()); |
235 | 20 | return Unexpect(ErrCode::Value::ComponentFunctionIndexOutOfBounds); |
236 | 20 | } |
237 | 2.89k | EXPECTED_TRY(CompCtx.checkOptions(Canon, false)); |
238 | 2.89k | const bool AsyncOpt = Canon.hasOption(ComponentCanonOptCode::Async); |
239 | 2.89k | if (FI->FT == nullptr) { |
240 | 0 | spdlog::error(ErrCode::Value::InvalidTypeReference); |
241 | 0 | return Unexpect(ErrCode::Value::InvalidTypeReference); |
242 | 0 | } |
243 | 2.89k | if (AsyncOpt && !FI->FT->isAsync()) { |
244 | 1 | spdlog::error(ErrCode::Value::CanonAsyncRequiresAsyncType); |
245 | 1 | spdlog::error(" canon lower with `async` needs `(func async ...)`."sv); |
246 | 1 | return Unexpect(ErrCode::Value::CanonAsyncRequiresAsyncType); |
247 | 1 | } |
248 | | |
249 | 2.89k | const ValType Ptr = CompCtx.getCanonPtrType(Canon); |
250 | 2.89k | EXPECTED_TRY(auto Sig, CompCtx.flattenFuncType(*FI, Ptr, ParamsNeedMemory, |
251 | 2.89k | ResultsNeedMemory)); |
252 | 2.89k | const bool ParamsIndirect = |
253 | 2.89k | Sig.getParamTypes().size() > |
254 | 2.89k | (AsyncOpt ? Component::Context::MaxFlatAsyncParams |
255 | 2.89k | : Component::Context::MaxFlatParams); |
256 | 2.89k | const bool ResultsIndirect = |
257 | 2.89k | AsyncOpt |
258 | 2.89k | ? !Sig.getReturnTypes().empty() |
259 | 2.89k | : Sig.getReturnTypes().size() > Component::Context::MaxFlatResults; |
260 | 2.89k | if (ParamsIndirect) { |
261 | 0 | Sig.getParamTypes().assign(1, Ptr); |
262 | 0 | } |
263 | 2.89k | if (ResultsIndirect) { |
264 | | // The caller passes a pointer for the results as the last parameter. |
265 | 3 | Sig.getParamTypes().push_back(Ptr); |
266 | 3 | Sig.getReturnTypes().clear(); |
267 | 3 | } |
268 | 2.89k | if (AsyncOpt) { |
269 | | // Async lower: the core function returns the packed subtask state. |
270 | 44 | Sig.getReturnTypes().assign(1, I32V); |
271 | 44 | } |
272 | 2.89k | EXPECTED_TRY( |
273 | 2.88k | CompCtx.checkRequiredOptions(Canon, |
274 | 2.88k | ParamsNeedMemory || ResultsNeedMemory || |
275 | 2.88k | ParamsIndirect || ResultsIndirect, |
276 | 2.88k | ResultsNeedMemory, "canon lower"sv)); |
277 | 2.88k | return PushCoreFunc(Sig.getParamTypes(), Sig.getReturnTypes()); |
278 | 2.89k | } |
279 | | |
280 | 847 | case ComponentCanonOpCode::Resource__new: { |
281 | 847 | EXPECTED_TRY(const auto Rep, LocalResourceRep("resource.new"sv)); |
282 | 836 | return PushCoreFunc({Rep}, {I32V}); |
283 | 847 | } |
284 | 595 | case ComponentCanonOpCode::Resource__rep: { |
285 | 595 | EXPECTED_TRY(const auto Rep, LocalResourceRep("resource.rep"sv)); |
286 | 578 | return PushCoreFunc({I32V}, {Rep}); |
287 | 595 | } |
288 | 78 | case ComponentCanonOpCode::Resource__drop: |
289 | 78 | EXPECTED_TRY(GetResourceEntry("resource.drop"sv)); |
290 | 70 | return PushCoreFunc({I32V}, {}); |
291 | | |
292 | | // Async built-ins take their core signatures from the shared table. |
293 | 219 | case ComponentCanonOpCode::Backpressure__inc: |
294 | 417 | case ComponentCanonOpCode::Backpressure__dec: |
295 | 417 | return PushBuiltin(I32V); |
296 | 1.02k | case ComponentCanonOpCode::Thread__index: |
297 | 1.02k | return PushBuiltin(I32V); |
298 | 3.16k | case ComponentCanonOpCode::Task__return: { |
299 | 3.16k | if (const auto &R = Canon.getResult(); |
300 | 3.16k | R.has_value() && CompCtx.hasBorrow({*R, &S, nullptr})) { |
301 | 1 | spdlog::error(ErrCode::Value::CanonTaskReturnBorrow); |
302 | 1 | return Unexpect(ErrCode::Value::CanonTaskReturnBorrow); |
303 | 1 | } |
304 | | // The results lower as the core parameters; task.return never reallocates. |
305 | 3.16k | EXPECTED_TRY(CompCtx.checkOptions(Canon, false)); |
306 | 3.16k | if (Canon.hasOption(ComponentCanonOptCode::Realloc)) { |
307 | 0 | spdlog::error(ErrCode::Value::CanonReallocOnBuiltin); |
308 | 0 | spdlog::error(" realloc cannot be specified for task.return."sv); |
309 | 0 | return Unexpect(ErrCode::Value::CanonReallocOnBuiltin); |
310 | 0 | } |
311 | 3.16k | EXPECTED_TRY(RequireSync()); |
312 | 3.15k | const ValType Ptr = CompCtx.getCanonPtrType(Canon); |
313 | 3.15k | std::vector<ValType> Params; |
314 | 3.15k | if (const auto &R = Canon.getResult(); R.has_value()) { |
315 | 3.07k | const Component::QualValType Q{*R, &S, nullptr}; |
316 | 3.07k | if (!CompCtx.flattenValType(Q, Params, Ptr)) { |
317 | 13 | spdlog::error(ErrCode::Value::InvalidTypeReference); |
318 | 13 | return Unexpect(ErrCode::Value::InvalidTypeReference); |
319 | 13 | } |
320 | 3.06k | ParamsNeedMemory = ParamsNeedMemory || CompCtx.needsMemory(Q); |
321 | 3.06k | } |
322 | 3.14k | if (Params.size() > Component::Context::MaxFlatParams) { |
323 | 39 | Params.assign(1, Ptr); |
324 | 39 | ParamsNeedMemory = true; |
325 | 39 | } |
326 | 3.14k | EXPECTED_TRY(CompCtx.checkRequiredOptions(Canon, ParamsNeedMemory, false, |
327 | 3.04k | "task.return"sv)); |
328 | 3.04k | return PushCoreFunc(std::move(Params), {}); |
329 | 3.14k | } |
330 | 171 | case ComponentCanonOpCode::Task__cancel: |
331 | 171 | return PushBuiltin(I32V); |
332 | 3.73k | case ComponentCanonOpCode::Context__get: |
333 | 3.87k | case ComponentCanonOpCode::Context__set: { |
334 | 3.87k | const bool IsGet = Canon.getOpCode() == ComponentCanonOpCode::Context__get; |
335 | 3.87k | const std::string_view Name = IsGet ? "context.get"sv : "context.set"sv; |
336 | | // The slot immediate is stored as the constant value, not as an index. |
337 | 3.87k | if (Canon.getConstVal() >= Component::Context::MaxContextSlots) { |
338 | 30 | spdlog::error(ErrCode::Value::ComponentContextSlotOutOfBounds); |
339 | 30 | spdlog::error( |
340 | 30 | " Context slot {} out of bounds, must be less than {}."sv, |
341 | 30 | Canon.getConstVal(), Component::Context::MaxContextSlots); |
342 | 30 | return Unexpect(ErrCode::Value::ComponentContextSlotOutOfBounds); |
343 | 30 | } |
344 | 3.84k | const ValType Ty = Canon.getContextType(); |
345 | 3.84k | if (Ty != I32V && Ty != I64V) { |
346 | 11 | spdlog::error(ErrCode::Value::ComponentContextTypeInvalid); |
347 | 11 | spdlog::error(" `{}` only supports `i32` or `i64`."sv, Name); |
348 | 11 | return Unexpect(ErrCode::Value::ComponentContextTypeInvalid); |
349 | 11 | } |
350 | | // A 64-bit slot is gated the same way a 64-bit resource rep is. |
351 | 3.83k | if (Ty == I64V && !Conf.hasProposal(Proposal::Memory64)) { |
352 | 0 | spdlog::error(ErrCode::Value::ComponentContextTypeInvalid); |
353 | 0 | spdlog::error(ErrInfo::InfoProposal(Proposal::Memory64)); |
354 | 0 | spdlog::error(" a 64-bit `{}` needs the memory64 proposal."sv, Name); |
355 | 0 | return Unexpect(ErrCode::Value::ComponentContextTypeInvalid); |
356 | 0 | } |
357 | | // The whole component shares one thread-local slot type. |
358 | 3.83k | auto &Shared = CompCtx.getTop().ContextType; |
359 | 3.83k | if (Shared.has_value() && *Shared != Ty) { |
360 | 1 | spdlog::error(ErrCode::Value::ComponentContextTypeMismatch); |
361 | 1 | spdlog::error(" `{}` type must match previous context type."sv, Name); |
362 | 1 | return Unexpect(ErrCode::Value::ComponentContextTypeMismatch); |
363 | 1 | } |
364 | 3.83k | Shared = Ty; |
365 | 3.83k | if (IsGet) { |
366 | 3.69k | return PushCoreFunc({}, {Ty}); |
367 | 3.69k | } |
368 | 137 | return PushCoreFunc({Ty}, {}); |
369 | 3.83k | } |
370 | 14 | case ComponentCanonOpCode::Thread__yield: |
371 | 14 | return PushBuiltin(I32V); |
372 | 229 | case ComponentCanonOpCode::Subtask__cancel: |
373 | 229 | return PushBuiltin(I32V); |
374 | 7.44k | case ComponentCanonOpCode::Subtask__drop: |
375 | 7.44k | return PushBuiltin(I32V); |
376 | 1.22k | case ComponentCanonOpCode::Stream__new: |
377 | 1.22k | EXPECTED_TRY(CheckTypeImmediate(true)); |
378 | 1.21k | return PushBuiltin(I32V); |
379 | 90 | case ComponentCanonOpCode::Stream__read: |
380 | 1.05k | case ComponentCanonOpCode::Stream__write: { |
381 | 1.05k | EXPECTED_TRY(const auto *Entry, CheckTypeImmediate(true)); |
382 | 1.04k | const auto &Elem = Entry->getDefValType()->getStream().ValTy; |
383 | | // Reading a payload that allocates needs realloc; writing never does. |
384 | 1.04k | const bool NeedRealloc = |
385 | 1.04k | Canon.getOpCode() == ComponentCanonOpCode::Stream__read && |
386 | 85 | Elem.has_value() && |
387 | 0 | CompCtx.needsMemory({*Elem, Entry->Home, Entry->Remap}); |
388 | 1.04k | EXPECTED_TRY(BuiltinOptions(Elem.has_value(), NeedRealloc)); |
389 | 1.04k | return PushBuiltin(CompCtx.getCanonPtrType(Canon)); |
390 | 1.04k | } |
391 | 691 | case ComponentCanonOpCode::Stream__cancel_read: |
392 | 737 | case ComponentCanonOpCode::Stream__cancel_write: |
393 | 737 | EXPECTED_TRY(CheckTypeImmediate(true)); |
394 | 730 | return PushBuiltin(I32V); |
395 | 76 | case ComponentCanonOpCode::Stream__drop_readable: |
396 | 133 | case ComponentCanonOpCode::Stream__drop_writable: |
397 | 210 | case ComponentCanonOpCode::Stream__forward: |
398 | 210 | EXPECTED_TRY(CheckTypeImmediate(true)); |
399 | 197 | return PushBuiltin(I32V); |
400 | 56.1k | case ComponentCanonOpCode::Future__new: |
401 | 56.1k | EXPECTED_TRY(CheckTypeImmediate(false)); |
402 | 56.1k | return PushBuiltin(I32V); |
403 | 199 | case ComponentCanonOpCode::Future__read: |
404 | 271 | case ComponentCanonOpCode::Future__write: { |
405 | 271 | EXPECTED_TRY(const auto *Entry, CheckTypeImmediate(false)); |
406 | 267 | const auto &Elem = Entry->getDefValType()->getFuture().ValTy; |
407 | | // Reading a payload that allocates needs realloc; writing never does. |
408 | 267 | const bool NeedRealloc = |
409 | 267 | Canon.getOpCode() == ComponentCanonOpCode::Future__read && |
410 | 198 | Elem.has_value() && |
411 | 1 | CompCtx.needsMemory({*Elem, Entry->Home, Entry->Remap}); |
412 | 267 | EXPECTED_TRY(BuiltinOptions(Elem.has_value(), NeedRealloc)); |
413 | 265 | return PushBuiltin(CompCtx.getCanonPtrType(Canon)); |
414 | 267 | } |
415 | 332 | case ComponentCanonOpCode::Future__cancel_read: |
416 | 2.68k | case ComponentCanonOpCode::Future__cancel_write: |
417 | 2.68k | EXPECTED_TRY(CheckTypeImmediate(false)); |
418 | 2.67k | return PushBuiltin(I32V); |
419 | 91 | case ComponentCanonOpCode::Future__drop_readable: |
420 | 120k | case ComponentCanonOpCode::Future__drop_writable: |
421 | 121k | case ComponentCanonOpCode::Future__forward: |
422 | 121k | EXPECTED_TRY(CheckTypeImmediate(false)); |
423 | 121k | return PushBuiltin(I32V); |
424 | 3 | case ComponentCanonOpCode::Error_context__new: |
425 | 3 | EXPECTED_TRY(BuiltinOptions(true, false)); |
426 | 0 | EXPECTED_TRY(RequireSync()); |
427 | 0 | return PushBuiltin(CompCtx.getCanonPtrType(Canon)); |
428 | 6 | case ComponentCanonOpCode::Error_context__debug_message: |
429 | 6 | EXPECTED_TRY(BuiltinOptions(true, true)); |
430 | 0 | EXPECTED_TRY(RequireSync()); |
431 | 0 | return PushBuiltin(CompCtx.getCanonPtrType(Canon)); |
432 | 1.00k | case ComponentCanonOpCode::Error_context__drop: |
433 | 1.00k | return PushBuiltin(I32V); |
434 | 4.73k | case ComponentCanonOpCode::Waitable_set__new: |
435 | 4.73k | return PushBuiltin(I32V); |
436 | 6 | case ComponentCanonOpCode::Waitable_set__wait: |
437 | 22 | case ComponentCanonOpCode::Waitable_set__poll: { |
438 | 22 | if (Canon.getIndex() >= S.CoreMemories.size()) { |
439 | 22 | spdlog::error(ErrCode::Value::ComponentMemoryIndexOutOfBounds); |
440 | 22 | spdlog::error(" Canonical built-in memory index {} out of bounds."sv, |
441 | 22 | Canon.getIndex()); |
442 | 22 | return Unexpect(ErrCode::Value::ComponentMemoryIndexOutOfBounds); |
443 | 22 | } |
444 | | // The payload address carries the address type of the given memory. |
445 | 0 | const auto *Mem = S.CoreMemories[Canon.getIndex()]; |
446 | 0 | const ValType Addr = |
447 | 0 | ValType(Mem != nullptr && Mem->getLimit().is64() ? TypeCode::I64 |
448 | 0 | : TypeCode::I32); |
449 | 0 | return PushBuiltin(Addr); |
450 | 22 | } |
451 | 65 | case ComponentCanonOpCode::Waitable_set__drop: |
452 | 65 | return PushBuiltin(I32V); |
453 | 1.00k | case ComponentCanonOpCode::Waitable__join: |
454 | 1.00k | return PushBuiltin(I32V); |
455 | 3 | case ComponentCanonOpCode::Thread__new_indirect: { |
456 | | // The start type must be (func (param i32)) and the target a core table. |
457 | 3 | const auto *CT = S.getCoreType(Canon.getIndex()); |
458 | 3 | const bool TypeOk = |
459 | 3 | CT != nullptr && CT->Func != nullptr && |
460 | 1 | CT->Func->getCompositeType().isFunc() && |
461 | 0 | CT->Func->getCompositeType().getFuncType().getParamTypes() == |
462 | 0 | std::vector<ValType>{I32V} && |
463 | 0 | CT->Func->getCompositeType().getFuncType().getReturnTypes().empty(); |
464 | 3 | if (!TypeOk) { |
465 | 3 | spdlog::error(ErrCode::Value::InvalidTypeReference); |
466 | 3 | spdlog::error( |
467 | 3 | " thread.new-indirect start type must be (func (param i32))."sv); |
468 | 3 | return Unexpect(ErrCode::Value::InvalidTypeReference); |
469 | 3 | } |
470 | 0 | if (Canon.getTargetIndex() >= S.CoreTables.size()) { |
471 | 0 | spdlog::error(ErrCode::Value::DefTypeIndexOutOfBounds); |
472 | 0 | spdlog::error(" thread.new-indirect table index {} out of bounds."sv, |
473 | 0 | Canon.getTargetIndex()); |
474 | 0 | return Unexpect(ErrCode::Value::DefTypeIndexOutOfBounds); |
475 | 0 | } |
476 | | // The table element type must be exactly `funcref`, not a subtype. |
477 | 0 | const auto *Tab = S.CoreTables[Canon.getTargetIndex()]; |
478 | 0 | if (Tab != nullptr && Tab->getRefType() != ValType(TypeCode::FuncRef)) { |
479 | 0 | spdlog::error(ErrCode::Value::CanonThreadTableType); |
480 | 0 | return Unexpect(ErrCode::Value::CanonThreadTableType); |
481 | 0 | } |
482 | | // The thread index carries the address type of the table. |
483 | 0 | return PushBuiltin(ValType(Tab != nullptr && Tab->getLimit().is64() |
484 | 0 | ? TypeCode::I64 |
485 | 0 | : TypeCode::I32)); |
486 | 0 | } |
487 | 88 | case ComponentCanonOpCode::Thread__resume_later: |
488 | 88 | return PushBuiltin(I32V); |
489 | 302 | case ComponentCanonOpCode::Thread__suspend: |
490 | 302 | return PushBuiltin(I32V); |
491 | 750 | case ComponentCanonOpCode::Thread__suspend_then_resume: |
492 | 1.05k | case ComponentCanonOpCode::Thread__yield_then_resume: |
493 | 32.5k | case ComponentCanonOpCode::Thread__suspend_then_promote: |
494 | 32.6k | case ComponentCanonOpCode::Thread__yield_then_promote: |
495 | 32.6k | return PushBuiltin(I32V); |
496 | 10 | default: |
497 | 10 | spdlog::error(ErrCode::Value::ComponentNotImplValidator); |
498 | 10 | spdlog::error(" canonical built-in {} is not supported yet."sv, |
499 | 10 | static_cast<uint32_t>(Canon.getOpCode())); |
500 | 10 | return Unexpect(ErrCode::Value::ComponentNotImplValidator); |
501 | 254k | } |
502 | 254k | } |
503 | | |
504 | | } // namespace Validator |
505 | | } // namespace WasmEdge |