1/* Copyright 2024 Mozilla Foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16use crate::{
17 BinaryReader, BinaryReaderError, BlockType, CompositeInnerType, ContType, FrameKind, FuncType,
18 Operator, RefType, Result, SubType,
19};
20
21/// To compute the arity (param and result counts) of "variable-arity"
22/// operators, the operator_arity macro needs information about the
23/// module's types and the current control stack. The ModuleArity
24/// trait exposes this information.
25pub trait ModuleArity {
26 /// Type with given index
27 fn sub_type_at(&self, type_idx: u32) -> Option<&SubType>;
28
29 /// Arity (param and result counts) of tag with given index
30 fn tag_type_arity(&self, at: u32) -> Option<(u32, u32)>;
31
32 /// Type index of function with given index
33 fn type_index_of_function(&self, function_idx: u32) -> Option<u32>;
34
35 /// Function type for a given continuation type
36 fn func_type_of_cont_type(&self, c: &ContType) -> Option<&FuncType>;
37
38 /// Sub type for a given reference type
39 fn sub_type_of_ref_type(&self, rt: &RefType) -> Option<&SubType>;
40
41 /// Current height of control stack
42 fn control_stack_height(&self) -> u32;
43
44 /// BlockType and FrameKind of label with given index
45 fn label_block(&self, depth: u32) -> Option<(BlockType, FrameKind)>;
46
47 /// Computes arity of given SubType
48 fn sub_type_arity(&self, t: &SubType) -> Option<(u32, u32)> {
49 match &t.composite_type.inner {
50 CompositeInnerType::Func(f) => {
51 Some((f.params().len() as u32, f.results().len() as u32))
52 }
53 CompositeInnerType::Struct(s) => Some((s.fields.len() as u32, s.fields.len() as u32)),
54 CompositeInnerType::Array(_) => None,
55 CompositeInnerType::Cont(c) => {
56 let f = self.func_type_of_cont_type(c)?;
57 Some((f.params().len() as u32, f.results().len() as u32))
58 }
59 }
60 }
61
62 /// Computes arity of given BlockType
63 fn block_type_arity(&self, ty: BlockType) -> Option<(u32, u32)> {
64 match ty {
65 BlockType::Empty => Some((0, 0)),
66 BlockType::Type(_) => Some((0, 1)),
67 BlockType::FuncType(t) => self.sub_type_arity(self.sub_type_at(t)?),
68 }
69 }
70}
71
72impl BinaryReader<'_> {
73 /// Read the next operator and compute its arity (param and result counts)
74 pub fn operator_arity(&self, module: &impl ModuleArity) -> Result<(u32, u32)> {
75 self.clone()
76 .read_operator()?
77 .operator_arity(module)
78 .ok_or_else(|| {
79 BinaryReaderError::new(message:"operator arity is unknown", self.original_position())
80 })
81 }
82}
83
84/// The operator_arity macro interprets the annotations in the for_each_operator macro
85/// to compute the arity of each operator. It needs access to a ModuleArity implementation.
86macro_rules! operator_arity {
87 (arity $self:ident $({ $($arg:ident: $argty:ty),* })? arity $($ann:tt)*) => {
88 {
89 let params = (|| -> Option<(i32, i32)> { operator_arity!(params $self { $($($arg: $argty),*)? } $($ann)*) })();
90 let results = (|| -> Option<(i32, i32)> { operator_arity!(results $self { $($($arg: $argty),*)? } $($ann)*) })();
91 match (params, results) {
92 (Some((a,_)), Some((_,d))) if a >= 0 && d >= 0 => (Some((a as u32, d as u32))),
93 _ => None,
94 }
95 }
96 };
97
98 (arity $self:ident $({ $($arg:ident: $argty:ty),* })? $cat:ident $($ann:tt)*) => {
99 Some(operator_arity!(fixed $cat $($ann)*))
100 };
101
102 (params $self:ident { $($arg:ident: $argty:ty),* } ~ $cat:ident $($tokens:tt)*) => { { let (a, b) = operator_arity!(count $self { $($arg: $argty),* } $cat)?;
103 let (c, d) = operator_arity!(params $self { $($arg: $argty),* } $($tokens)*)?;
104 Some((b as i32 + c as i32, a as i32 + d as i32)) } };
105 (params $self:ident { $($arg:ident: $argty:ty),* } $val:literal $($tokens:tt)*) => { { let rest = operator_arity!(params $self { $($arg: $argty),* } $($tokens)*)?;
106 Some(($val + rest.0, $val + rest.1)) } };
107 (params $self:ident { $($arg:ident: $argty:ty),* } $cat:ident $($tokens:tt)*) => { { let (a, b) = operator_arity!(count $self { $($arg: $argty),* } $cat)?;
108 let (c, d) = operator_arity!(params $self { $($arg: $argty),* } $($tokens)*)?;
109 Some((a as i32 + c as i32, b as i32 + d as i32)) } };
110 (params $self:ident { $($arg:ident: $argty:ty),* } -> $($tokens:tt)*) => { Some((0, 0)) };
111 (params $self:ident { $($arg:ident: $argty:ty),* }) => { Some((0, 0)) };
112
113 (results $self:ident { $($arg:ident: $argty:ty),* } ~ $($tokens:tt)*) => { operator_arity!(results $self { $($arg: $argty),* } $($tokens)*) };
114 (results $self:ident { $($arg:ident: $argty:ty),* } $val:literal $($tokens:tt)*) => { operator_arity!(results $self { $($arg: $argty),* } $($tokens)*) };
115 (results $self:ident { $($arg:ident: $argty:ty),* } $cat:ident $($tokens:tt)*) => { operator_arity!(results $self { $($arg: $argty),* } $($tokens)*) };
116 (results $self:ident { $($arg:ident: $argty:ty),* } -> $($tokens:tt)*) => { operator_arity!(params $self { $($arg: $argty),* } $($tokens)*) };
117
118 (count $self:ident { $tag_index:ident: $_:ty } tag) => {{
119 operator_arity!(tag_index $tag_index);
120 $self.tag_type_arity($tag_index)
121 }};
122
123 (count $self:ident { $_1:ident: $_2:ty, $tag_index:ident: $($_3:tt)* } tag) => { operator_arity!(count $self { $tag_index: _ } tag) };
124
125 (count $self:ident { $func_index:ident: $_:ty } func) => {{
126 operator_arity!(func_index $func_index);
127 $self.sub_type_arity($self.sub_type_at($self.type_index_of_function($func_index)?)?)
128 }};
129
130 (count $self:ident { $type_index:ident: $($_:tt)* } type) => {{
131 operator_arity!(type_index $type_index);
132 $self.sub_type_arity($self.sub_type_at($type_index)?)
133 }};
134
135 (count $self:ident { $type_index:ident: $($_:tt)* } switch) => {{
136 operator_arity!(type_index $type_index);
137 let st = &$self.sub_type_at($type_index)?.composite_type.inner;
138 if let CompositeInnerType::Cont(ct) = &st {
139 let last_param = $self.func_type_of_cont_type(ct)?.params().last()?;
140 $self.sub_type_arity($self.sub_type_of_ref_type(&last_param.as_reference_type()?)?)
141 } else {
142 None
143 }
144 }};
145
146 (count $self:ident { $type1_index:ident: $t1:ty, $type2_index:ident: $t2:ty } type_diff) => {{
147 operator_arity!(type_index $type1_index);
148 operator_arity!(type_index $type2_index);
149 let a = $self.sub_type_arity($self.sub_type_at($type1_index)?)?;
150 let b = $self.sub_type_arity($self.sub_type_at($type2_index)?)?;
151 Some((a.0.checked_sub(b.0)?, a.1.checked_sub(b.1)?))
152 }};
153
154 (count $self:ident { $arg1:ident: $argty:ty, $size:ident: $sizety:ty } size) => {{
155 operator_arity!(size_value $size);
156 Some(($size, $size))
157 }};
158
159 (count $self:ident { $depth:ident: $($_:tt)* } br) => {{
160 operator_arity!(depth $depth);
161 let (ty, kind) = $self.label_block($depth)?;
162 let (params, results) = $self.block_type_arity(ty)?;
163 let n = match kind {
164 FrameKind::Loop => params,
165 _ => results,
166 };
167 Some((n, n))
168 }};
169
170 (count $self:ident { $($_:ident: $__:ty),* } ret) => {{
171 let (ty, _) = $self.control_stack_height().checked_sub(1)
172 .and_then(|x| $self.label_block(x))?;
173 $self.block_type_arity(ty)
174 }};
175
176 (count $self:ident { $blockty:ident: $($_:tt)* } block) => {{
177 operator_arity!(blockty $blockty);
178 $self.block_type_arity($blockty)
179 }};
180
181 (count $self:ident {} implicit_else) => {{
182 let (ty, kind) = $self.label_block(0)?;
183 let (params, results) = $self.block_type_arity(ty)?;
184 Some(match kind {
185 FrameKind::If => (results, params),
186 _ => (0, 0),
187 })
188 }};
189
190 (count $self:ident { $($_: ident: $__:ty),* } end) => {{
191 let (ty, _) = $self.label_block(0)?;
192 $self.block_type_arity(ty)
193 }};
194
195 (count $self:ident { $try_table:ident: $($_:tt)* } try_table) => {{
196 operator_arity!(try_table $try_table);
197 $self.block_type_arity($try_table.ty)
198 }};
199
200 (count $self:ident { $br_table:ident: $($_:tt)* } br_table) => {{
201 operator_arity!(br_table $br_table);
202 let relative_depth: u32 = $br_table.default();
203 operator_arity!(count $self { relative_depth: u32 } br)
204 }};
205
206 (tag_index tag_index $($_:tt)*) => {};
207 (func_index function_index $($_:tt)*) => {};
208 (type_index type_index $($_:tt)*) => {};
209 (type_index struct_type_index $($_:tt)*) => {};
210 (type_index argument_index $($_:tt)*) => {};
211 (type_index result_index $($_:tt)*) => {};
212 (type_index cont_type_index $($_:tt)*) => {};
213 (size_value array_size $($_:tt)*) => {};
214 (depth relative_depth $($_:tt)*) => {};
215 (blockty blockty $($_:tt)*) => {};
216 (try_table try_table $($_:tt)*) => {};
217 (br_table targets $($_:tt)*) => {};
218
219 (fixed load lane $($_:tt)*) => {(2, 1)};
220 (fixed load $($_:tt)*) => {(1, 1)};
221 (fixed store $($_:tt)*) => {(2, 0)};
222 (fixed test $($_:tt)*) => {(1, 1)};
223 (fixed unary $($_:tt)*) => {(1, 1)};
224 (fixed binary $($_:tt)*) => {(2, 1)};
225 (fixed cmp $($_:tt)*) => {(2, 1)};
226 (fixed shift $($_:tt)*) => {(2, 1)};
227 (fixed splat $($_:tt)*) => {(1, 1)};
228 (fixed ternary $($_:tt)*) => {(3, 1)};
229 (fixed conversion $($_:tt)*) => {(1, 1)};
230 (fixed push $($_:tt)*) => {(0, 1)};
231 (fixed extract $($_:tt)*) => {(1, 1)};
232 (fixed replace $($_:tt)*) => {(2, 1)};
233 (fixed atomic rmw array $($_:tt)*) => {(3, 1)};
234 (fixed atomic rmw $($_:tt)*) => {(2, 1)};
235 (fixed atomic cmpxchg $($_:tt)*) => {(3, 1)};
236}
237
238impl Operator<'_> {
239 /// Compute the arity (param and result counts) of the operator, given
240 /// an impl ModuleArity, which stores the necessary module state.
241 pub fn operator_arity(&self, module: &impl ModuleArity) -> Option<(u32, u32)> {
242 macro_rules! define_arity {
243 ($(@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*) )*) => (
244 match self.clone() {
245 $(
246 Operator::$op $({ $($arg),* })? => {
247 $(
248 $(let _ = $arg;)*
249 )?
250 operator_arity!(arity module $({ $($arg: $argty),* })? $($ann)*)
251 }
252 )*
253 }
254 );
255 }
256 crate::for_each_operator!(define_arity)
257 }
258}
259