/src/WasmEdge/include/executor/component/canonical_abi.h
Line | Count | Source |
1 | | // SPDX-License-Identifier: Apache-2.0 |
2 | | // SPDX-FileCopyrightText: 2019-2024 Second State INC |
3 | | |
4 | | //===-- wasmedge/executor/component/canonical_abi.h -----------------------===// |
5 | | // |
6 | | // Part of the WasmEdge Project. |
7 | | // |
8 | | //===----------------------------------------------------------------------===// |
9 | | /// |
10 | | /// \file |
11 | | /// Component Model Canonical ABI helpers (Preview 2, sync only): |
12 | | /// - alignment / elem_size / flatten_type / flatten_functype |
13 | | /// - load / store / lift_flat / lower_flat |
14 | | /// |
15 | | /// Spec citations refer to |
16 | | /// https://github.com/WebAssembly/component-model/blob/main/design/mvp/CanonicalABI.md |
17 | | /// |
18 | | //===----------------------------------------------------------------------===// |
19 | | #pragma once |
20 | | |
21 | | #include "ast/component/type.h" |
22 | | #include "common/errcode.h" |
23 | | #include "common/expected.h" |
24 | | #include "common/types.h" |
25 | | #include "runtime/instance/component/component.h" |
26 | | #include "runtime/instance/function.h" |
27 | | #include "runtime/instance/memory.h" |
28 | | |
29 | | #include <cstdint> |
30 | | #include <optional> |
31 | | |
32 | | namespace WasmEdge { |
33 | | namespace Executor { |
34 | | |
35 | | // Forward declaration: store / lowerFlat invoke the guest `realloc` through |
36 | | // Executor::invoke when allocating list / string return areas. Pulling in |
37 | | // executor/executor.h here would form a header cycle, so the call is routed |
38 | | // through this forward-declared pointer instead. |
39 | | class Executor; |
40 | | |
41 | | namespace CanonicalABI { |
42 | | |
43 | | /// Sync ABI limits (CanonicalABI.md L2815-2817). |
44 | | constexpr uint32_t MaxFlatParams = 16; |
45 | | constexpr uint32_t MaxFlatResults = 1; |
46 | | |
47 | | /// Context bundle for canonical-ABI operations. Not every helper requires every |
48 | | /// field — alignment / elem_size / flatten_* only need CompInst to resolve type |
49 | | /// indices; load / store / lift_flat / lower_flat additionally require Mem (and |
50 | | /// Realloc + Exec when allocating list / string return areas, since the |
51 | | /// allocation goes through Executor::invoke on the guest's `realloc` core |
52 | | /// function). |
53 | | struct CanonCtx { |
54 | | Executor *Exec = nullptr; |
55 | | Runtime::Instance::MemoryInstance *Mem = nullptr; |
56 | | Runtime::Instance::FunctionInstance *Realloc = nullptr; |
57 | | const Runtime::Instance::ComponentInstance *CompInst = nullptr; |
58 | | /// Guest string encoding for the canon function this context serves |
59 | | /// (CanonicalABI.md `string-encoding` option). Selects the byte layout used |
60 | | /// by load / store / lift_flat / lower_flat for `string` values. Defaults to |
61 | | /// UTF-8; type-only callers (alignment / flatten) never read it. |
62 | | StringEncoding Enc = StringEncoding::UTF8; |
63 | | }; |
64 | | |
65 | | /// Resolve a component type index against the component instance. |
66 | | inline const AST::Component::DefType *resolveDefType(const CanonCtx &Cx, |
67 | 0 | uint32_t Idx) noexcept { |
68 | 0 | if (Cx.CompInst != nullptr) { |
69 | 0 | return Cx.CompInst->getType(Idx); |
70 | 0 | } |
71 | 0 | return nullptr; |
72 | 0 | } |
73 | | |
74 | | /// Discriminant byte width for a variant / enum with NumCases cases. |
75 | | /// CanonicalABI.md L1951-1956 (`def discriminant_type`). |
76 | | uint32_t discriminantSize(uint32_t NumCases) noexcept; |
77 | | |
78 | | /// Alignment of a Component Model value type T (ptr_type = i32, deferred). |
79 | | /// CanonicalABI.md L1904-1985. |
80 | | Expect<uint32_t> alignment(const CanonCtx &Cx, |
81 | | const ComponentValType &T) noexcept; |
82 | | |
83 | | /// Alignment of a defined value type — internal recursion helper. Public so |
84 | | /// callers that already hold a DefValType (e.g., synthesized result tuple) |
85 | | /// can avoid the typeindex round-trip. |
86 | | /// CanonicalABI.md L1904-1985. |
87 | | Expect<uint32_t> alignmentDef(const CanonCtx &Cx, |
88 | | const AST::Component::DefValType &T) noexcept; |
89 | | |
90 | | /// Byte size of a Component Model value type T in linear memory. |
91 | | /// CanonicalABI.md L1990-2040. |
92 | | Expect<uint32_t> elemSize(const CanonCtx &Cx, |
93 | | const ComponentValType &T) noexcept; |
94 | | |
95 | | /// Byte size of a defined value type — internal recursion helper. |
96 | | /// CanonicalABI.md L1990-2040. |
97 | | Expect<uint32_t> elemSizeDef(const CanonCtx &Cx, |
98 | | const AST::Component::DefValType &T) noexcept; |
99 | | |
100 | | /// Result of `flatten_functype`: sequences of core wasm types representing |
101 | | /// the ABI signature seen by core wasm. CanonicalABI.md L2813-2848. |
102 | | struct FlatFuncType { |
103 | | std::vector<ValType> Params; |
104 | | std::vector<ValType> Results; |
105 | | }; |
106 | | |
107 | | /// Flatten a Component Model value type to its core wasm representation. |
108 | | /// CanonicalABI.md L2860-2877. |
109 | | Expect<std::vector<ValType>> flattenType(const CanonCtx &Cx, |
110 | | const ComponentValType &T) noexcept; |
111 | | |
112 | | /// Flatten a defined value type — internal recursion helper. |
113 | | /// CanonicalABI.md L2860-2877. |
114 | | Expect<std::vector<ValType>> |
115 | | flattenTypeDef(const CanonCtx &Cx, |
116 | | const AST::Component::DefValType &T) noexcept; |
117 | | |
118 | | /// Flatten a component function type into its core ABI signature. Sync only |
119 | | /// — async is rejected. CanonicalABI.md L2819-2832. |
120 | | /// |
121 | | /// `IsLift = true` covers the `canon lift` direction (component-typed |
122 | | /// function exposed as core wasm callee); when results exceed |
123 | | /// MaxFlatResults the core function returns a single i32 return-area pointer. |
124 | | /// `IsLift = false` covers `canon lower` and synthesizes the trailing |
125 | | /// out-pointer parameter when results exceed MaxFlatResults. |
126 | | Expect<FlatFuncType> flattenFuncType(const CanonCtx &Cx, |
127 | | const AST::Component::FuncType &FT, |
128 | | bool IsLift) noexcept; |
129 | | |
130 | | /// True iff `T` transitively contains a `list` or `string`. Used by the |
131 | | /// canon-options validation to enforce the spec's `lift(T)` / `lower(T)` |
132 | | /// requirements (CanonicalABI.md L3273-3277). Cycles through type indices |
133 | | /// are bounded by the recursion-guard set the caller passes in. |
134 | | bool containsListOrString(const CanonCtx &Cx, |
135 | | const ComponentValType &T) noexcept; |
136 | | |
137 | | /// Load a Component Model value of type T from linear memory at Ptr. |
138 | | /// CanonicalABI.md L2050-2289 (and L2305-2322 for own/borrow). |
139 | | /// |
140 | | /// Cx must provide a MemoryInstance. Callers are responsible for |
141 | | /// alignment and bounds pre-checks at the top level (per spec |
142 | | /// `lift_flat_values` L3197-3199); load() itself relies on |
143 | | /// MemoryInstance::loadValue for per-primitive bounds checking and does |
144 | | /// not re-validate alignment. |
145 | | Expect<ComponentValVariant> load(const CanonCtx &Cx, uint32_t Ptr, |
146 | | const ComponentValType &T) noexcept; |
147 | | |
148 | | /// Load a defined value type — internal recursion helper. |
149 | | Expect<ComponentValVariant> |
150 | | loadDef(const CanonCtx &Cx, uint32_t Ptr, |
151 | | const AST::Component::DefValType &T) noexcept; |
152 | | |
153 | | /// Store a Component Model value of type T into linear memory at Ptr. |
154 | | /// CanonicalABI.md L2360-2735. |
155 | | /// |
156 | | /// String / variable-length list storage requires invoking realloc, which is |
157 | | /// not yet wired through this helper — those cases return |
158 | | /// ComponentNotImplInstantiate. |
159 | | Expect<void> store(const CanonCtx &Cx, const ComponentValVariant &V, |
160 | | const ComponentValType &T, uint32_t Ptr) noexcept; |
161 | | |
162 | | /// Store a value into a defined value type — internal recursion helper. |
163 | | Expect<void> storeDef(const CanonCtx &Cx, const ComponentValVariant &V, |
164 | | const AST::Component::DefValType &T, |
165 | | uint32_t Ptr) noexcept; |
166 | | |
167 | | /// Iterator over a sequence of core wasm values backing a flat lift. |
168 | | /// Mirrors the spec's CoreValueIter (CanonicalABI.md L2928-2948). |
169 | | class FlatIter { |
170 | | public: |
171 | | FlatIter(Span<const std::pair<ValVariant, ValType>> Vs) noexcept |
172 | 0 | : Pairs(Vs), Singles{} {} |
173 | 0 | FlatIter(Span<const ValVariant> Vs) noexcept : Pairs{}, Singles(Vs) {} |
174 | | |
175 | | /// Read the next core value as a ValVariant. Returns std::nullopt if the |
176 | | /// iterator is exhausted. |
177 | 0 | std::optional<ValVariant> next() noexcept { |
178 | 0 | if (!Pairs.empty()) { |
179 | 0 | auto V = Pairs[Idx].first; |
180 | 0 | ++Idx; |
181 | 0 | return V; |
182 | 0 | } |
183 | 0 | if (Idx < Singles.size()) { |
184 | 0 | return Singles[Idx++]; |
185 | 0 | } |
186 | 0 | return std::nullopt; |
187 | 0 | } |
188 | 0 | bool done() const noexcept { |
189 | 0 | return Idx >= (Pairs.empty() ? Singles.size() : Pairs.size()); |
190 | 0 | } |
191 | 0 | size_t pos() const noexcept { return Idx; } |
192 | | |
193 | | private: |
194 | | Span<const std::pair<ValVariant, ValType>> Pairs; |
195 | | Span<const ValVariant> Singles; |
196 | | size_t Idx = 0; |
197 | | }; |
198 | | |
199 | | /// Lift a flat representation of a Component Model value into the rich |
200 | | /// ComponentValVariant. CanonicalABI.md L2957-3084 (lift_flat). The |
201 | | /// CoerceValueIter (spec L3042-3072) handling for variant/option/result |
202 | | /// payloads is implemented; mismatched join slots reinterpret per L3047-3055. |
203 | | Expect<ComponentValVariant> liftFlat(const CanonCtx &Cx, FlatIter &VI, |
204 | | const ComponentValType &T) noexcept; |
205 | | |
206 | | /// Lift a defined value type from flat values — internal recursion helper. |
207 | | Expect<ComponentValVariant> |
208 | | liftFlatDef(const CanonCtx &Cx, FlatIter &VI, |
209 | | const AST::Component::DefValType &T) noexcept; |
210 | | |
211 | | /// Lower a ComponentValVariant to its flat representation, the symmetric |
212 | | /// inverse of `liftFlat`. CanonicalABI.md L3086-3192 (lower_flat). |
213 | | /// |
214 | | /// String / variable-length list lowering allocates a payload buffer via |
215 | | /// `Cx.Realloc`; callers must populate Exec / Realloc on the CanonCtx. |
216 | | /// Variant/option/result payload coerce (spec L3158-3180) is implemented: |
217 | | /// the selected case's native flat slots are reinterpreted into the joined |
218 | | /// shape and the suffix is zero-padded. |
219 | | Expect<std::vector<ValVariant>> lowerFlat(const CanonCtx &Cx, |
220 | | const ComponentValVariant &V, |
221 | | const ComponentValType &T) noexcept; |
222 | | |
223 | | /// Lower a defined value type to flat values — internal recursion helper. |
224 | | Expect<std::vector<ValVariant>> |
225 | | lowerFlatDef(const CanonCtx &Cx, const ComponentValVariant &V, |
226 | | const AST::Component::DefValType &T) noexcept; |
227 | | |
228 | | /// Spec L3193-3202 (`def lift_flat_values`). Reads MaxFlat-bounded flat values |
229 | | /// from VI; when the type list flattens to more than MaxFlat, reads a single |
230 | | /// pointer and loads the synthesized tuple from memory at that pointer. |
231 | | Expect<std::vector<ComponentValVariant>> |
232 | | liftFlatValues(const CanonCtx &Cx, FlatIter &VI, |
233 | | Span<const ComponentValType> Types, uint32_t MaxFlat) noexcept; |
234 | | |
235 | | /// Spec L3212-3232 (`def lower_flat_values`). Lowers Values into flat core |
236 | | /// wasm values. When OutParam is provided, the indirect-store buffer is the |
237 | | /// caller-supplied pointer (lower-direction returning to an out-pointer) and |
238 | | /// the returned vector is empty. When OutParam is std::nullopt, the indirect |
239 | | /// case allocates a buffer via realloc and returns a single i32 pointer. |
240 | | Expect<std::vector<ValVariant>> |
241 | | lowerFlatValues(const CanonCtx &Cx, Span<const ComponentValVariant> Values, |
242 | | Span<const ComponentValType> Types, uint32_t MaxFlat, |
243 | | std::optional<uint32_t> OutParam = std::nullopt) noexcept; |
244 | | |
245 | | } // namespace CanonicalABI |
246 | | } // namespace Executor |
247 | | } // namespace WasmEdge |