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 | |
16 | use 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. |
25 | pub 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 | |
72 | impl 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. |
86 | macro_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 | |
238 | impl 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 | |