diff options
| author | Henry <mail@henrygressmann.de> | 2026-07-15 15:20:00 +0200 |
|---|---|---|
| committer | Henry <mail@henrygressmann.de> | 2026-07-15 15:20:00 +0200 |
| commit | f99b3a90827406b48657d63d7c1f0188ddd68c8f (patch) | |
| tree | 4bd7b11160698a63255258230fd9cc5bf43e5655 /crates/parser/src/macros.rs | |
| parent | dc366165b8cd0f8c43cfe7017cb37f0a16887d6d (diff) | |
feat: decouple validation from parsing
Signed-off-by: Henry <mail@henrygressmann.de>
Diffstat (limited to 'crates/parser/src/macros.rs')
| -rw-r--r-- | crates/parser/src/macros.rs | 297 |
1 files changed, 156 insertions, 141 deletions
diff --git a/crates/parser/src/macros.rs b/crates/parser/src/macros.rs index 8a5330a..ff216a5 100644 --- a/crates/parser/src/macros.rs +++ b/crates/parser/src/macros.rs @@ -2,12 +2,13 @@ pub(crate) mod visit { macro_rules! validate_then_visit { ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {$( fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { - self.0.$visit($($($arg.clone()),*)?); - let validation = self.0.validator.visitor(self.0.position).$visit($($($arg),*)?); - if let Err(e) = validation { - cold_path(); - self.0.record_error(crate::ParseError::ParseError { message: e.to_string(), offset: self.0.position }); + if let Some(validator) = self.validator.as_mut() { + if let Err(e) = validator.visitor(self.position).$visit($($($arg.clone()),*)?) { + core::hint::cold_path(); + return Err(crate::ParseError::ParseError { message: e.to_string(), offset: self.position }); + } } + self.builder.$visit($($($arg),*)?) } )*}; } @@ -15,64 +16,103 @@ pub(crate) mod visit { macro_rules! validate_then_visit_simd { ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*))*) => {$( fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output { - self.0.$visit($($($arg),*)?); - let validation = self.0.validator.simd_visitor(self.0.position).$visit($($($arg),*)?); - if let Err(e) = validation { - cold_path(); - self.0.record_error(crate::ParseError::ParseError { message: e.to_string(), offset: self.0.position }); + if let Some(validator) = self.validator.as_mut() { + if let Err(e) = validator.simd_visitor(self.position).$visit($($($arg.clone()),*)?) { + core::hint::cold_path(); + return Err(crate::ParseError::ParseError { message: e.to_string(), offset: self.position }); + } } + self.builder.$visit($($($arg),*)?) } )*}; } - macro_rules! define_operand { - ($name:ident($instr:expr, $ty:ty)) => { - fn $name(&mut self, arg: $ty) -> Self::Output { - self.instructions.push($instr(arg).into()); - } + macro_rules! lowering_ops { + () => {}; + ($kind:ident $inputs:tt => $outputs:tt { + $($visit:ident $(($($arg:ident: $ty:ty),+))? => $instr:ident),* $(,)? + } $($rest:tt)*) => { + $(lowering_ops!(@$kind $inputs => $outputs $visit $(($($arg: $ty),+))? => $instr);)* + lowering_ops!($($rest)*); + }; + (effect $inputs:tt => $outputs:tt { $($visit:ident),* $(,)? } $($rest:tt)*) => { + $(lowering_ops!(@effect $inputs => $outputs $visit);)* + lowering_ops!($($rest)*); }; - ($name:ident($instr:expr, $ty:ty, $ty2:ty)) => { - fn $name(&mut self, arg: $ty, arg2: $ty2) -> Self::Output { - self.instructions.push($instr(arg, arg2).into()); + (@fixed [$($input:ident),*] => [$($output:ident),*] + $visit:ident $(($($arg:ident: $ty:ty),+))? => $instr:ident + ) => { + fn $visit(&mut self $(, $($arg: $ty),+)?) -> Self::Output { + lowering_ops!(@emit self fixed [$($input),*] => [$($output),*] + Instruction::$instr $(($($arg),+))?.into()) } }; - - ($name:ident($instr:expr)) => { - fn $name(&mut self) -> Self::Output { - self.instructions.push($instr.into()); + (@memory [$($input:ident),*] => [$($output:ident),*] + $visit:ident $(($lane:ident: $ty:ty))? => $instr:ident + ) => { + fn $visit(&mut self, memarg: wasmparser::MemArg $(, $lane: $ty)?) -> Self::Output { + let address = self.metadata.memory_size(memarg.memory)?; + lowering_ops!(@emit self address(address) [$($input),*] => [$($output),*] + Instruction::$instr(MemoryArg::new(memarg.offset, memarg.memory) $(, $lane)?).into()) } }; - } - - macro_rules! define_operands { - ($($name:ident($instr:ident $(,$ty:ty)*)),*) => {$( - define_operand!($name(Instruction::$instr $(,$ty)*)); - )*}; - } - - macro_rules! define_mem_operands { - ($($name:ident($instr:ident)),*) => {$( - fn $name(&mut self, memarg: wasmparser::MemArg) -> Self::Output { - self.instructions.push(Instruction::$instr(MemoryArg::new(memarg.offset, memarg.memory))); + (@global $inputs:tt => $outputs:tt $($operator:tt)*) => { + lowering_ops!(@resolved global_size $inputs => $outputs $($operator)*); + }; + (@memory_index $inputs:tt => $outputs:tt $($operator:tt)*) => { + lowering_ops!(@resolved memory_size $inputs => $outputs $($operator)*); + }; + (@table $inputs:tt => $outputs:tt $($operator:tt)*) => { + lowering_ops!(@resolved table_size $inputs => $outputs $($operator)*); + }; + (@resolved $resolver:ident [$($input:ident),*] => [$($output:ident),*] + $visit:ident($index:ident: $ty:ty) => $instr:ident + ) => { + fn $visit(&mut self, $index: $ty) -> Self::Output { + let address = self.metadata.$resolver($index)?; + lowering_ops!(@emit self address(address) [$($input),*] => [$($output),*] + Instruction::$instr($index).into()) } - )*}; - } - - macro_rules! define_mem_operands_simd { - ($($name:ident($instr:ident)),*) => {$( - fn $name(&mut self, memarg: wasmparser::MemArg) -> Self::Output { - self.instructions.push(Instruction::$instr(MemoryArg::new(memarg.offset, memarg.memory)).into()); + }; + (@resolved $resolver:ident [$($input:ident),*] => [$($output:ident),*] + $visit:ident($arg:ident: $arg_ty:ty, $index:ident: $index_ty:ty) => $instr:ident + ) => { + fn $visit(&mut self, $arg: $arg_ty, $index: $index_ty) -> Self::Output { + let address = self.metadata.$resolver($index)?; + lowering_ops!(@emit self address(address) [$($input),*] => [$($output),*] + Instruction::$instr($arg, $index).into()) } - )*}; - } - - macro_rules! define_mem_operands_simd_lane { - ($($name:ident($instr:ident)),*) => {$( - fn $name(&mut self, memarg: wasmparser::MemArg, lane: u8) -> Self::Output { - self.instructions.push(Instruction::$instr(MemoryArg::new(memarg.offset, memarg.memory), lane).into()); + }; + (@effect [$($input:ident),*] => [$($output:ident),*] $visit:ident) => { + fn $visit(&mut self) -> Self::Output { + self.apply_effect(&[$(lowering_ops!(@size $input)),*], &[$(lowering_ops!(@size $output)),*]) } - )*}; + }; + (@terminating [$($input:ident),*] => [$($output:ident),*] $visit:ident => $instr:ident) => { + fn $visit(&mut self) -> Self::Output { + self.mark_unreachable(); + lowering_ops!(@emit self fixed [$($input),*] => [$($output),*] Instruction::$instr) + } + }; + + (@emit $self:ident fixed [$($input:ident),*] => [$($output:ident),*] $instruction:expr) => { + $self.emit( + &[$(lowering_ops!(@size $input)),*], + &[$(lowering_ops!(@size $output)),*], + $instruction, + ) + }; + (@emit $self:ident address($address:ident) [$($input:ident),*] => [$($output:ident),*] $instruction:expr) => { + $self.emit( + &[$(lowering_ops!(@size $input, $address)),*], + &[$(lowering_ops!(@size $output, $address)),*], + $instruction, + ) + }; + + (@size Addr, $address:ident) => { $address }; + (@size $size:ident $(, $address:ident)?) => { OperandSize::$size }; } macro_rules! impl_visit_operator { @@ -92,100 +132,58 @@ pub(crate) mod visit { (@@$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident ($($ann:tt)*)) => { fn $visit(&mut self $($(,_: $argty)*)?) -> Self::Output { - self.unsupported(stringify!($visit)) + Err(crate::ParseError::UnsupportedOperator(stringify!($visit).to_string())) } }; } - pub(crate) use { - define_mem_operands, define_mem_operands_simd, define_mem_operands_simd_lane, define_operand, define_operands, - impl_visit_operator, validate_then_visit, validate_then_visit_simd, - }; + pub(crate) use {impl_visit_operator, lowering_ops, validate_then_visit, validate_then_visit_simd}; } pub(crate) mod optimize { macro_rules! replace { - ($instructions:ident, $read:ident, $consumed:literal => [$($out:expr),+ $(,)?]) => {{ + ($instructions:ident, $read:ident, $consumed:expr => [$($out:expr),+ $(,)?]) => {{ const { assert!($consumed >= 1 && $consumed <= 3); assert!([$(stringify!($out)),+].len() <= $consumed + 1); } let replacements = [$($out),+]; - let replacement_start = $read + 1 - replacements.len(); - $instructions[$read - $consumed..replacement_start].fill(Instruction::Nop); - $instructions[replacement_start..=$read].copy_from_slice(&replacements); + let start = $read - $consumed; + $instructions[start..start + replacements.len()].copy_from_slice(&replacements); + $instructions.truncate(start + replacements.len()); + #[allow(unused_assignments)] + { $read = $instructions.len() - 1; } }}; - ($instructions:ident, $read:ident, $consumed:literal => $out:expr) => { + ($instructions:ident, $read:ident, $consumed:expr => $out:expr) => { replace!($instructions, $read, $consumed => [$out]); }; } macro_rules! rewrite { - ($instructions:ident, $read:ident, [$a:pat] if ($($guard:tt)+) => [$($out:expr),+ $(,)?]) => { - rewrite!($instructions, $read, [$a] if ($($guard)+) => { replace!($instructions, $read, 1 => [$($out),+]); }) - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat] if ($($guard:tt)+) => [$($out:expr),+ $(,)?]) => { - rewrite!($instructions, $read, [$a, $b] if ($($guard)+) => { replace!($instructions, $read, 2 => [$($out),+]); }) - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] if ($($guard:tt)+) => [$($out:expr),+ $(,)?]) => { - rewrite!($instructions, $read, [$a, $b, $c] if ($($guard)+) => { replace!($instructions, $read, 3 => [$($out),+]); }) - }; - ($instructions:ident, $read:ident, [$a:pat] => [$($out:expr),+ $(,)?]) => { - rewrite!($instructions, $read, [$a] => { replace!($instructions, $read, 1 => [$($out),+]); }) - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat] => [$($out:expr),+ $(,)?]) => { - rewrite!($instructions, $read, [$a, $b] => { replace!($instructions, $read, 2 => [$($out),+]); }) - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] => [$($out:expr),+ $(,)?]) => { - rewrite!($instructions, $read, [$a, $b, $c] => { replace!($instructions, $read, 3 => [$($out),+]); }) + ($instructions:ident, $read:ident, [$($pattern:pat),+] $(if ($($guard:tt)+))? => [$($out:expr),+ $(,)?]) => { + rewrite!($instructions, $read, [$($pattern),+] $(if ($($guard)+))? => { + replace!($instructions, $read, [$(stringify!($pattern)),+].len() => [$($out),+]); + }) }; - ($instructions:ident, $read:ident, [$a:pat] if ($($guard:tt)+) => $body:block $(,)?) => { - if $read > 0 && let $a = $instructions[$read - 1] && $($guard)+ { - $body - } - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat] if ($($guard:tt)+) => $body:block $(,)?) => { - if $read > 1 && let ($a, $b) = ($instructions[$read - 2], $instructions[$read - 1]) && $($guard)+ { - $body - } - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] if ($($guard:tt)+) => $body:block $(,)?) => { - if $read > 2 && let ($a, $b, $c) = ($instructions[$read - 3], $instructions[$read - 2], $instructions[$read - 1]) && $($guard)+ { - $body - } - }; - ($instructions:ident, $read:ident, [$a:pat] => $body:block $(,)?) => { - if $read > 0 && let $a = $instructions[$read - 1] { - $body - } - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat] => $body:block $(,)?) => { - if $read > 1 && let ($a, $b) = ($instructions[$read - 2], $instructions[$read - 1]) { - $body - } - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] => $body:block $(,)?) => { - if $read > 2 && let ($a, $b, $c) = ($instructions[$read - 3], $instructions[$read - 2], $instructions[$read - 1]) { - $body + ($instructions:ident, $read:ident, [$($pattern:pat),+] $(if ($($guard:tt)+))? => $body:block $(,)?) => {{ + const CONSUMED: usize = [$(stringify!($pattern)),+].len(); + if !$instructions.tail_rewritten + && $read < $instructions.len() + && $read >= $instructions.block_start + CONSUMED + { + let previous: [Instruction; CONSUMED] = $instructions[$read - CONSUMED..$read].try_into().unwrap(); + if let [$($pattern),+] = previous $( + && $($guard)+ + )? { + $instructions.tail_rewritten = true; + $body + } } - }; - ($instructions:ident, $read:ident, [$a:pat] if ($($guard:tt)+) => $out:expr $(,)?) => { - rewrite!($instructions, $read, [$a] if ($($guard)+) => { replace!($instructions, $read, 1 => $out); }) - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat] if ($($guard:tt)+) => $out:expr $(,)?) => { - rewrite!($instructions, $read, [$a, $b] if ($($guard)+) => { replace!($instructions, $read, 2 => $out); }) - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] if ($($guard:tt)+) => $out:expr $(,)?) => { - rewrite!($instructions, $read, [$a, $b, $c] if ($($guard)+) => { replace!($instructions, $read, 3 => $out); }) - }; - ($instructions:ident, $read:ident, [$a:pat] => $out:expr $(,)?) => { - rewrite!($instructions, $read, [$a] => { replace!($instructions, $read, 1 => $out); }) - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat] => $out:expr $(,)?) => { - rewrite!($instructions, $read, [$a, $b] => { replace!($instructions, $read, 2 => $out); }) - }; - ($instructions:ident, $read:ident, [$a:pat, $b:pat, $c:pat] => $out:expr $(,)?) => { - rewrite!($instructions, $read, [$a, $b, $c] => { replace!($instructions, $read, 3 => $out); }) + }}; + ($instructions:ident, $read:ident, [$($pattern:pat),+] $(if ($($guard:tt)+))? => $out:expr $(,)?) => { + rewrite!($instructions, $read, [$($pattern),+] $(if ($($guard)+))? => { + replace!($instructions, $read, [$(stringify!($pattern)),+].len() => $out); + }) }; } @@ -201,13 +199,13 @@ pub(crate) mod optimize { binop_local_const_set = $lcset:ident $(, load_local_tee = $loadtee:ident, load_local_set = $loadset:ident)? ) => { - fn $name(instr: Instruction) -> Option<(Instruction, u16)> { + fn $name(instr: Instruction) -> Option<(Option<Instruction>, u16)> { Some(match instr { - Instruction::$get(local) => (Instruction::Nop, local), - Instruction::$tee(local) => (Instruction::$set(local), local), - Instruction::$lltee(op, a, b, local) => (Instruction::$llset(op, a, b, local), local), - Instruction::$lctee(op, src, c, local) => (Instruction::$lcset(op, src, c, local), local), - $(Instruction::$loadtee(memarg, addr, local) => (Instruction::$loadset(memarg, addr, local), local.into()),)? + Instruction::$get(local) => (None, local), + Instruction::$tee(local) => (Some(Instruction::$set(local)), local), + Instruction::$lltee(op, a, b, local) => (Some(Instruction::$llset(op, a, b, local)), local), + Instruction::$lctee(op, src, c, local) => (Some(Instruction::$lcset(op, src, c, local)), local), + $(Instruction::$loadtee(memarg, addr, local) => (Some(Instruction::$loadset(memarg, addr, local)), local.into()),)? _ => return None, }) } @@ -223,21 +221,28 @@ pub(crate) mod optimize { local_local = $local_local:ident, local_const = $local_const:expr ) => {{ - if let Some([(lhs_idx, lhs_src), (rhs_idx, rhs_src), (op_idx, raw_op)]) = - previous_non_nop::<3>($instrs, $read) + if !$instrs.tail_rewritten + && $read < $instrs.len() + && $read >= $instrs.block_start + 3 + && let [lhs_src, rhs_src, raw_op] = [$instrs[$read - 3], $instrs[$read - 2], $instrs[$read - 1]] && let Some((lhs_instr, lhs)) = $source(lhs_src) && let Some(op) = $op(raw_op) { if let Some((rhs_instr, rhs)) = $source(rhs_src) { - $instrs[lhs_idx] = lhs_instr; - $instrs[rhs_idx] = rhs_instr; - $instrs[op_idx] = Instruction::Nop; - $instrs[$read] = Instruction::$local_local(op, lhs, rhs, $dst); + if rhs_instr.is_none() || rhs != lhs { + $instrs.tail_rewritten = true; + $instrs.truncate($read - 3); + $instrs.extend(lhs_instr); + $instrs.extend(rhs_instr); + $instrs.push(Instruction::$local_local(op, lhs, rhs, $dst)); + $read = $instrs.len() - 1; + } } else if let Some(imm) = $const(rhs_src, raw_op) { - $instrs[lhs_idx] = lhs_instr; - $instrs[rhs_idx] = Instruction::Nop; - $instrs[op_idx] = Instruction::Nop; - $instrs[$read] = $local_const($dst, lhs, op, imm); + $instrs.tail_rewritten = true; + $instrs.truncate($read - 3); + $instrs.extend(lhs_instr); + $instrs.push($local_const($dst, lhs, op, imm)); + $read = $instrs.len() - 1; } } }}; @@ -254,7 +259,17 @@ pub(crate) mod optimize { binop_local_const_set = $lcset:expr $(, const_instr = $const_instr:ident, set_local_const = $set_local_const:ident)? ) => {{ - rewrite!($instrs, $read, [$get(src)] => if src == $dst { Instruction::Nop } else { Instruction::$copy(src, $dst) }); + rewrite!($instrs, $read, [$get(src)] if (src != $dst) => Instruction::$copy(src, $dst)); + if !$instrs.tail_rewritten + && $read < $instrs.len() + && $read > $instrs.block_start + && let Instruction::$get(src) = $instrs[$read - 1] + && src == $dst + { + $instrs.tail_rewritten = true; + $instrs.truncate($read - 1); + $read = $instrs.len(); + } $(rewrite!($instrs, $read, [$const_instr(c)] => Instruction::$set_local_const($dst, c));)? rewrite!($instrs, $read, [$ll(op, a, b)] => Instruction::$llset(op, a, b, $dst)); rewrite!($instrs, $read, [$lc(op, src, c)] => { replace!($instrs, $read, 1 => $lcset($dst, src, op, c)); }); @@ -270,7 +285,7 @@ pub(crate) mod optimize { binop_local_const = $lc:ident, binop_local_const_tee = $lctee:ident ) => {{ - rewrite!($instrs, $read, [$get(src)] if (src == $dst) => [Instruction::$get(src), Instruction::Nop]); + rewrite!($instrs, $read, [$get(src)] if (src == $dst) => Instruction::$get(src)); rewrite!($instrs, $read, [$ll(op, a, b)] => Instruction::$lltee(op, a, b, $dst)); rewrite!($instrs, $read, [$lc(op, src, c)] => Instruction::$lctee(op, src, c, $dst)); }}; @@ -286,7 +301,7 @@ pub(crate) mod optimize { binop_local_const_tee = $lctee:ident, binop_local_const_set = $lcset:ident ) => {{ - rewrite!($instrs, $read, [$tee(local)] => [Instruction::$set(local), Instruction::Nop]); + rewrite!($instrs, $read, [$tee(local)] => Instruction::$set(local)); rewrite!($instrs, $read, [$lltee(op, a, b, dst)] => Instruction::$llset(op, a, b, dst)); rewrite!($instrs, $read, [$lctee(op, src, c, dst)] => Instruction::$lcset(op, src, c, dst)); }}; |
