|
| 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("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 | + for_each_operator!(define_arity) |
| 257 | + } |
| 258 | +} |
0 commit comments