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