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